Commit 465a5b0b authored by Christian Beier's avatar Christian Beier

Update noVNC HTML5 client to latest version from https://github.com/kanaka/noVNC.

parent ad7a054e
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org> noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
The noVNC core library is licensed under the LGPLv3 (GNU Lesser The noVNC core library files are licensed under the MPL 2.0 (Mozilla
General Public License). The noVNC core library is composed of the Public License 2.0). The noVNC core library is composed of the
Javascript code necessary for full noVNC operation. This includes (but Javascript code necessary for full noVNC operation. This includes (but
is not limited to): is not limited to):
...@@ -10,6 +10,7 @@ is not limited to): ...@@ -10,6 +10,7 @@ is not limited to):
include/display.js include/display.js
include/input.js include/input.js
include/jsunzip.js include/jsunzip.js
include/keysym.js
include/logo.js include/logo.js
include/rfb.js include/rfb.js
include/ui.js include/ui.js
...@@ -36,21 +37,15 @@ The HTML, CSS, font and image files are licensed as follows: ...@@ -36,21 +37,15 @@ The HTML, CSS, font and image files are licensed as follows:
images/ : Creative Commons Attribution-ShareAlike images/ : Creative Commons Attribution-ShareAlike
http://creativecommons.org/licenses/by-sa/3.0/ http://creativecommons.org/licenses/by-sa/3.0/
In addition the following file, which is part of the noVNC core
library, may be licensed under either the LGPL-2, LGPL-3 or MPL 2.0
when it used separately from the noVNC core library.
include/input.js : LGPL-2 or any later version
Some portions of noVNC are copyright to their individual authors. Some portions of noVNC are copyright to their individual authors.
Please refer to the individual source files and/or to the noVNC commit Please refer to the individual source files and/or to the noVNC commit
history: https://github.com/kanaka/noVNC/commits/master history: https://github.com/kanaka/noVNC/commits/master
The are several files and projects that have been incorporated into The are several files and projects that have been incorporated into
the noVNC core library. Here is a list of those files and the original the noVNC core library. Here is a list of those files and the original
licenses (all LGPL-3 compatible): licenses (all MPL 2.0 compatible):
include/base64.js : MPL 1.1, GPL-2 or LGPL-2.1 include/base64.js : MPL 2.0
include/des.js : Various BSD style licenses include/des.js : Various BSD style licenses
...@@ -59,20 +54,29 @@ licenses (all LGPL-3 compatible): ...@@ -59,20 +54,29 @@ licenses (all LGPL-3 compatible):
include/web-socket-js/ : New BSD license (3-clause). Source code at include/web-socket-js/ : New BSD license (3-clause). Source code at
http://github.com/gimite/web-socket-js http://github.com/gimite/web-socket-js
include/chrome-app/tcp-stream.js
: Apache 2.0 license
utils/websockify
utils/websocket.py : LGPL 3
The following license texts are included: The following license texts are included:
docs/LICENSE.MPL-2.0
docs/LICENSE.LGPL-3 and docs/LICENSE.LGPL-3 and
docs/LICENSE.GPL-3 docs/LICENSE.GPL-3
docs/LICENSE.OFL-1.1 docs/LICENSE.OFL-1.1
docs/LICENSE.BSD-3-Clause (New BSD) docs/LICENSE.BSD-3-Clause (New BSD)
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD) docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
docs/LICENSE.zlib docs/LICENSE.zlib
docs/LICENSE.MPL-2.0 docs/LICENSE.Apache-2.0
Or alternatively the license texts may be found here: Or alternatively the license texts may be found here:
http://www.mozilla.org/MPL/2.0/
http://www.gnu.org/licenses/lgpl.html and http://www.gnu.org/licenses/lgpl.html and
http://www.gnu.org/licenses/gpl.html http://www.gnu.org/licenses/gpl.html
http://scripts.sil.org/OFL http://scripts.sil.org/OFL
http://www.mozilla.org/MPL/1.1/ http://en.wikipedia.org/wiki/BSD_licenses
http://www.mozilla.org/MPL/2.0/ http://www.gzip.org/zlib/zlib_license.html
http://www.apache.org/licenses/LICENSE-2.0.html
## noVNC: HTML5 VNC Client ## noVNC: HTML5 VNC Client
[![Build Status](https://travis-ci.org/kanaka/noVNC.svg?branch=master)](https://travis-ci.org/kanaka/noVNC)
### Description ### Description
noVNC is a HTML5 VNC client that runs well in any modern browser noVNC is a HTML5 VNC client that runs well in any modern browser
including mobile browsers (iPhone/iPad and Android). including mobile browsers (iPhone/iPad and Android).
Many companies/projects have integrated noVNC including [Ganeti Web
Manager](http://code.osuosl.org/projects/ganeti-webmgr),
[OpenStack](http://www.openstack.org),
[OpenNebula](http://opennebula.org/), and
[LibVNCServer](http://libvncserver.sourceforge.net). See [the Projects
and Companies wiki
page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC)
for a more complete list with additional info and links.
### News/help/contact
Notable commits, announcements and news are posted to Notable commits, announcements and news are posted to
@<a href="http://www.twitter.com/noVNC">noVNC</a> <a href="http://www.twitter.com/noVNC">@noVNC</a>
If you are a noVNC developer/integrator/user (or want to be) please
join the <a
href="https://groups.google.com/forum/?fromgroups#!forum/novnc">noVNC
discussion group</a>
Bugs and feature requests can be submitted via [github
issues](https://github.com/kanaka/noVNC/issues). If you are looking
for a place to start contributing to noVNC, a good place to start
would be the issues that are marked as
["patchwelcome"](https://github.com/kanaka/noVNC/issues?labels=patchwelcome).
There are many companies/projects that have integrated noVNC into If you want to show appreciation for noVNC you could donate to a great
their products including: [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), [SlapOS](http://www.slapos.org), [Intel MeshCentral](https://meshcentral.com), [Amahi](http://amahi.org), [Brightbox](http://brightbox.com/), [Foreman](http://theforeman.org), [LibVNCServer](http://libvnc.github.io/) and [PocketVNC](http://www.pocketvnc.com/blog/?page_id=866). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links. non-profits such as: [Compassion
International](http://www.compassion.com/), [SIL](http://www.sil.org),
[Habitat for Humanity](http://www.habitat.org), [Electronic Frontier
Foundation](https://www.eff.org/), [Against Malaria
Foundation](http://www.againstmalaria.com/), [Nothing But
Nets](http://www.nothingbutnets.net/), etc. Please tweet <a
href="http://www.twitter.com/noVNC">@noVNC</a> if you do.
### Features ### Features
...@@ -24,7 +53,7 @@ their products including: [Ganeti Web Manager](http://code.osuosl.org/projects/g ...@@ -24,7 +53,7 @@ their products including: [Ganeti Web Manager](http://code.osuosl.org/projects/g
* Clipboard copy/paste * Clipboard copy/paste
* Clipping or scolling modes for large remote screens * Clipping or scolling modes for large remote screens
* Easy site integration and theming (3 example themes included) * Easy site integration and theming (3 example themes included)
* Licensed under the [LGPLv3](http://www.gnu.org/licenses/lgpl.html) * Licensed under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/)
### Screenshots ### Screenshots
...@@ -49,17 +78,18 @@ See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">h ...@@ -49,17 +78,18 @@ See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">h
* Fast Javascript Engine: this is not strictly a requirement, but * Fast Javascript Engine: this is not strictly a requirement, but
without a fast Javascript engine, noVNC might be painfully slow. without a fast Javascript engine, noVNC might be painfully slow.
* I maintain a more detailed browser compatibility list <a * See the more detailed [browser compatibility wiki page](https://github.com/kanaka/noVNC/wiki/Browser-support).
href="https://github.com/kanaka/noVNC/wiki/Browser-support">here</a>.
### Server Requirements ### Server Requirements
Unless you are using a VNC server with support for WebSockets Unless you are using a VNC server with support for WebSockets
connections (such as [x11vnc/libvncserver](http://libvnc.github.io/) or connections (such as
[PocketVNC](http://www.pocketvnc.com/blog/?page_id=866)), [x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
you need to use a WebSockets to TCP socket proxy. There is [QEMU](http://www.qemu.org/), or
a python proxy included ('websockify'). [PocketVNC](http://www.pocketvnc.com/blog/?page_id=866)), you need to
use a WebSockets to TCP socket proxy. There is a python proxy included
('websockify').
### Quick Start ### Quick Start
...@@ -88,8 +118,14 @@ a python proxy included ('websockify'). ...@@ -88,8 +118,14 @@ a python proxy included ('websockify').
### Authors/Contributors ### Authors/Contributors
* noVNC : Joel Martin (github.com/kanaka) * Core team:
* New UI and Icons : Chris Gordon * [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)
* Notable contributions:
* UI and Icons : Chris Gordon
* Original Logo : Michael Sersen * Original Logo : Michael Sersen
* tight encoding : Michael Tinglof (Mercuri.ca) * tight encoding : Michael Tinglof (Mercuri.ca)
...@@ -100,5 +136,3 @@ a python proxy included ('websockify'). ...@@ -100,5 +136,3 @@ a python proxy included ('websockify').
* jsunzip : Erik Moller (github.com/operasoftware/jsunzip), * jsunzip : Erik Moller (github.com/operasoftware/jsunzip),
* tinflate : Joergen Ibsen (ibsensoftware.com) * tinflate : Joergen Ibsen (ibsensoftware.com)
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs) * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
/* /*
* noVNC base CSS * noVNC base CSS
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* noVNC is licensed under the LGPL-3 (see LICENSE.txt) * Copyright (C) 2013 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). * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/ */
...@@ -40,9 +41,6 @@ html { ...@@ -40,9 +41,6 @@ html {
} }
#noVNC_encrypt { #noVNC_encrypt {
} }
#noVNC_connectTimeout {
width: 30px;
}
#noVNC_path { #noVNC_path {
width: 100px; width: 100px;
} }
...@@ -51,6 +49,9 @@ html { ...@@ -51,6 +49,9 @@ html {
float:right; float:right;
} }
#noVNC_buttons {
white-space: nowrap;
}
#noVNC_view_drag_button { #noVNC_view_drag_button {
display: none; display: none;
...@@ -58,38 +59,43 @@ html { ...@@ -58,38 +59,43 @@ html {
#sendCtrlAltDelButton { #sendCtrlAltDelButton {
display: none; display: none;
} }
#noVNC_xvp_buttons {
display: none;
}
#noVNC_mobile_buttons { #noVNC_mobile_buttons {
display: none; display: none;
} }
#noVNC_extra_keys {
display: inline;
list-style-type: none;
padding: 0px;
margin: 0px;
position: relative;
}
.noVNC-buttons-left { .noVNC-buttons-left {
float: left; float: left;
padding-left:10px; z-index: 1;
padding-top:4px; position: relative;
} }
.noVNC-buttons-right { .noVNC-buttons-right {
float:right; float:right;
right: 0px; right: 0px;
padding-right:10px; z-index: 2;
padding-top:4px; position: absolute;
} }
#noVNC_status_bar { #noVNC_status {
margin-top: 0px;
padding: 0px;
}
#noVNC_status_bar div {
font-size: 12px; font-size: 12px;
padding-top: 4px; padding-top: 4px;
width:100%; height:32px;
}
#noVNC_status {
height:20px;
text-align: center; text-align: center;
font-weight: bold;
color: #fff;
} }
#noVNC_settings_menu { #noVNC_settings_menu {
margin: 3px; margin: 3px;
text-align: left; text-align: left;
...@@ -104,22 +110,12 @@ html { ...@@ -104,22 +110,12 @@ html {
float:right; float:right;
} }
.noVNC_status_normal {
background: #eee;
}
.noVNC_status_error {
background: #f44;
}
.noVNC_status_warn {
background: #ff4;
}
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect /* Do not set width/height for VNC_screen or VNC_canvas or incorrect
* scaling will occur. Canvas resizes to remote VNC settings */ * scaling will occur. Canvas resizes to remote VNC settings */
#noVNC_screen_pad { #noVNC_screen_pad {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
height: 44px; height: 36px;
} }
#noVNC_screen { #noVNC_screen {
text-align: center; text-align: center;
...@@ -154,14 +150,14 @@ html { ...@@ -154,14 +150,14 @@ html {
/*Bubble contents divs*/ /*Bubble contents divs*/
#noVNC_settings { #noVNC_settings {
display:none; display:none;
margin-top:77px; margin-top:73px;
right:20px; right:20px;
position:fixed; position:fixed;
} }
#noVNC_controls { #noVNC_controls {
display:none; display:none;
margin-top:77px; margin-top:73px;
right:12px; right:12px;
position:fixed; position:fixed;
} }
...@@ -173,7 +169,7 @@ html { ...@@ -173,7 +169,7 @@ html {
display:none; display:none;
position:fixed; position:fixed;
margin-top:77px; margin-top:73px;
right:20px; right:20px;
left:20px; left:20px;
padding:15px; padding:15px;
...@@ -186,9 +182,40 @@ html { ...@@ -186,9 +182,40 @@ html {
border-radius:10px; border-radius:10px;
} }
#noVNC_popup_status_panel {
display:none;
position: fixed;
z-index: 1;
margin:15px;
margin-top:60px;
padding:15px;
width:auto;
text-align:center;
font-weight:bold;
word-wrap:break-word;
color:#fff;
background:rgba(0,0,0,0.65);
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
#noVNC_xvp {
display:none;
margin-top:73px;
right:30px;
position:fixed;
}
#noVNC_xvp.top:after {
right:125px;
}
#noVNC_clipboard { #noVNC_clipboard {
display:none; display:none;
margin-top:77px; margin-top:73px;
right:30px; right:30px;
position:fixed; position:fixed;
} }
...@@ -207,17 +234,11 @@ html { ...@@ -207,17 +234,11 @@ html {
z-index: -1; z-index: -1;
} }
.noVNC_status_warn {
background-color:yellow;
}
/* /*
* Advanced Styling * Advanced Styling
*/ */
/* Control bar */ .noVNC_status_normal {
#noVNC-control-bar {
position:fixed;
background: #b2bdcd; /* Old browsers */ background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
...@@ -225,9 +246,32 @@ html { ...@@ -225,9 +246,32 @@ html {
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
/* Control bar */
#noVNC-control-bar {
position:fixed;
display:block; display:block;
height:44px; height:36px;
left:0; left:0;
top:0; top:0;
width:100%; width:100%;
...@@ -368,22 +412,85 @@ html { ...@@ -368,22 +412,85 @@ html {
font-size: 180px; font-size: 180px;
} }
@media screen and (min-width: 481px) and (max-width: 640px) { .noVNC-buttons-left {
.noVNC_status_button { padding-left: 10px;
font-size: 10px; }
.noVNC-buttons-right {
padding-right: 10px;
}
#noVNC_status {
z-index: 0;
position: absolute;
width: 100%;
margin-left: 0px;
}
#showExtraKeysButton { display: none; }
#toggleCtrlButton { display: inline; }
#toggleAltButton { display: inline; }
#sendTabButton { display: inline; }
#sendEscButton { display: inline; }
/* left-align the status text on lower resolutions */
@media screen and (max-width: 800px){
#noVNC_status {
z-index: 1;
position: relative;
width: auto;
float: left;
margin-left: 4px;
} }
}
@media screen and (max-width: 640px){
#noVNC_clipboard_text { #noVNC_clipboard_text {
width: 410px; width: 410px;
} }
#noVNC_logo { #noVNC_logo {
font-size: 150px; font-size: 150px;
} }
}
@media screen and (min-width: 321px) and (max-width: 480px) {
.noVNC_status_button { .noVNC_status_button {
font-size: 10px; font-size: 10px;
} }
.noVNC-buttons-left {
padding-left: 0px;
}
.noVNC-buttons-right {
padding-right: 0px;
}
/* collapse the extra keys on lower resolutions */
#showExtraKeysButton {
display: inline;
}
#toggleCtrlButton {
display: none;
position: absolute;
top: 30px;
left: 0px;
}
#toggleAltButton {
display: none;
position: absolute;
top: 65px;
left: 0px;
}
#sendTabButton {
display: none;
position: absolute;
top: 100px;
left: 0px;
}
#sendEscButton {
display: none;
position: absolute;
top: 135px;
left: 0px;
}
}
@media screen and (min-width: 321px) and (max-width: 480px) {
#noVNC_clipboard_text { #noVNC_clipboard_text {
width: 250px; width: 250px;
} }
......
/* /* This Source Code Form is subject to the terms of the Mozilla Public
* Modified from: * License, v. 2.0. If a copy of the MPL was not distributed with this
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
*/
/* ***** BEGIN LICENSE BLOCK ***** // From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XML-RPC Client component.
*
* The Initial Developer of the Original Code is
* Digital Creations 2, Inc.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Martijn Pieters <mj@digicool.com> (original author)
* Samuel Sieb <samuel@sieb.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*jslint white: false, bitwise: false, plusplus: false */ /*jslint white: false */
/*global console */ /*global console */
var Base64 = { var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
/* Convert data (an array of integers) to a Base64 string. */ toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', base64Pad : '=',
base64Pad : '=',
encode: function (data) {
encode: function (data) { "use strict";
"use strict"; var result = '';
var result = '', var toBase64Table = Base64.toBase64Table;
chrTable = Base64.toBase64Table.split(''), var length = data.length;
pad = Base64.base64Pad, var lengthpad = (length % 3);
length = data.length, // Convert every three bytes to 4 ascii characters.
i;
// Convert every three bytes to 4 ascii characters. for (var i = 0; i < (length - 2); i += 3) {
for (i = 0; i < (length - 2); i += 3) { result += toBase64Table[data[i] >> 2];
result += chrTable[data[i] >> 2]; result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; result += toBase64Table[data[i + 2] & 0x3f];
result += chrTable[data[i+2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
result += chrTable[data[i] >> 2];
if ((length%3) === 2) {
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[(data[i+1] & 0x0f) << 2];
result += pad;
} else {
result += chrTable[(data[i] & 0x03) << 4];
result += pad + pad;
} }
}
return result;
},
/* Convert Base64 data to a string */
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,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-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
],
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var binTable = Base64.toBinaryTable,
pad = Base64.base64Pad,
result, result_length, idx, i, c, padding,
leftbits = 0, // number of bits decoded, but yet to be appended
leftdata = 0, // bits decoded, but yet to be appended
data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; } // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
var j = 0;
/* Every four characters is 3 resulting numbers */ if (lengthpad === 2) {
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); j = length - lengthpad;
result = new Array(result_length); result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
// Convert one by one. result += toBase64Table[(data[j + 1] & 0x0f) << 2];
for (idx = 0, i = offset; i < data.length; i++) { result += toBase64Table[64];
c = binTable[data.charCodeAt(i) & 0x7f]; } else if (lengthpad === 1) {
padding = (data.charAt(i) === pad); j = length - lengthpad;
// Skip illegal characters and whitespace result += toBase64Table[data[j] >> 2];
if (c === -1) { result += toBase64Table[(data[j] & 0x03) << 4];
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i); result += toBase64Table[64];
continue; result += toBase64Table[64];
} }
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result return result;
if (leftbits >= 8) { },
leftbits -= 8;
// Append if not padding. /* Convert Base64 data to a string */
if (!padding) { /* jshint -W013 */
result[idx++] = (leftdata >> leftbits) & 0xff; 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,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-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
],
/* jshint +W013 */
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = Base64.toBinaryTable;
var base64Pad = Base64.base64Pad;
var result, result_length;
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
var data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length);
// 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);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
continue;
}
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding) {
result[idx++] = (leftdata >> leftbits) & 0xff;
}
leftdata &= (1 << leftbits) - 1;
} }
leftdata &= (1 << leftbits) - 1;
} }
}
// If there are any bits left, the base64 string was corrupted // If there are any bits left, the base64 string was corrupted
if (leftbits) { if (leftbits) {
throw {name: 'Base64-Error', err = new Error('Corrupted base64 string');
message: 'Corrupted base64 string'}; err.name = 'Base64-Error';
} throw err;
}
return result;
}
return result;
}
}; /* End of Base64 namespace */ }; /* End of Base64 namespace */
/* /*
* noVNC base CSS * noVNC black CSS
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* noVNC is licensed under the LGPL-3 (see LICENSE.txt) * Copyright (C) 2013 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). * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/ */
...@@ -9,7 +10,7 @@ ...@@ -9,7 +10,7 @@
background-color:#000; background-color:#000;
} }
#noVNC-control-bar { .noVNC_status_normal {
background: #4c4c4c; /* Old browsers */ background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
...@@ -18,6 +19,24 @@ ...@@ -18,6 +19,24 @@
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
} }
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.triangle-right { .triangle-right {
border:2px solid #fff; border:2px solid #fff;
......
/* /*
* noVNC base CSS * noVNC blue CSS
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* noVNC is licensed under the LGPL-3 (see LICENSE.txt) * Copyright (C) 2013 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). * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/ */
#noVNC-control-bar { .noVNC_status_normal {
background-color:#04073d; background-color:#04073d;
background-image: -webkit-gradient( background-image: -webkit-gradient(
linear, linear,
...@@ -20,6 +21,36 @@ ...@@ -20,6 +21,36 @@
rgb(4,7,61) 50% rgb(4,7,61) 50%
); );
} }
.noVNC_status_error {
background-color:#f04040;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(240,64,64)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(4,7,61) 54%,
rgb(249,64,64) 50%
);
}
.noVNC_status_warn {
background-color:#f0f040;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(240,240,64)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(4,7,61) 54%,
rgb(240,240,64) 50%
);
}
.triangle-right { .triangle-right {
border:2px solid #fff; border:2px solid #fff;
......
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: Boris Smus (smus@chromium.org)
*/
(function(exports) {
// Define some local variables here.
var socket = chrome.socket || chrome.experimental.socket;
var dns = chrome.experimental.dns;
/**
* Creates an instance of the client
*
* @param {String} host The remote host to connect to
* @param {Number} port The port to connect to at the remote host
*/
function TcpClient(host, port, pollInterval) {
this.host = host;
this.port = port;
this.pollInterval = pollInterval || 15;
// Callback functions.
this.callbacks = {
connect: null, // Called when socket is connected.
disconnect: null, // Called when socket is disconnected.
recvBuffer: null, // Called (as ArrayBuffer) when client receives data from server.
recvString: null, // Called (as string) when client receives data from server.
sent: null // Called when client sends data to server.
};
// Socket.
this.socketId = null;
this.isConnected = false;
log('initialized tcp client');
}
/**
* Connects to the TCP socket, and creates an open socket.
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-create
* @param {Function} callback The function to call on connection
*/
TcpClient.prototype.connect = function(callback) {
// First resolve the hostname to an IP.
dns.resolve(this.host, function(result) {
this.addr = result.address;
socket.create('tcp', {}, this._onCreate.bind(this));
// Register connect callback.
this.callbacks.connect = callback;
}.bind(this));
};
/**
* Sends an arraybuffer/view down the wire to the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
* @param {String} msg The arraybuffer/view to send
* @param {Function} callback The function to call when the message has sent
*/
TcpClient.prototype.sendBuffer = function(buf, callback) {
if (buf.buffer) {
buf = buf.buffer;
}
/*
// Debug
var bytes = [], u8 = new Uint8Array(buf);
for (var i = 0; i < u8.length; i++) {
bytes.push(u8[i]);
}
log("sending bytes: " + (bytes.join(',')));
*/
socket.write(this.socketId, buf, this._onWriteComplete.bind(this));
// Register sent callback.
this.callbacks.sent = callback;
};
/**
* Sends a string down the wire to the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
* @param {String} msg The string to send
* @param {Function} callback The function to call when the message has sent
*/
TcpClient.prototype.sendString = function(msg, callback) {
/*
// Debug
log("sending string: " + msg);
*/
this._stringToArrayBuffer(msg, function(arrayBuffer) {
socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
}.bind(this));
// Register sent callback.
this.callbacks.sent = callback;
};
/**
* Sets the callback for when a message is received
*
* @param {Function} callback The function to call when a message has arrived
* @param {String} type The callback argument type: "arraybuffer" or "string"
*/
TcpClient.prototype.addResponseListener = function(callback, type) {
if (typeof type === "undefined") {
type = "arraybuffer";
}
// Register received callback.
if (type === "string") {
this.callbacks.recvString = callback;
} else {
this.callbacks.recvBuffer = callback;
}
};
/**
* Sets the callback for when the socket disconnects
*
* @param {Function} callback The function to call when the socket disconnects
* @param {String} type The callback argument type: "arraybuffer" or "string"
*/
TcpClient.prototype.addDisconnectListener = function(callback) {
// Register disconnect callback.
this.callbacks.disconnect = callback;
};
/**
* Disconnects from the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect
*/
TcpClient.prototype.disconnect = function() {
if (this.isConnected) {
this.isConnected = false;
socket.disconnect(this.socketId);
if (this.callbacks.disconnect) {
this.callbacks.disconnect();
}
log('socket disconnected');
}
};
/**
* The callback function used for when we attempt to have Chrome
* create a socket. If the socket is successfully created
* we go ahead and connect to the remote side.
*
* @private
* @see http://developer.chrome.com/trunk/apps/socket.html#method-connect
* @param {Object} createInfo The socket details
*/
TcpClient.prototype._onCreate = function(createInfo) {
this.socketId = createInfo.socketId;
if (this.socketId > 0) {
socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this));
} else {
error('Unable to create socket');
}
};
/**
* The callback function used for when we attempt to have Chrome
* connect to the remote side. If a successful connection is
* made then polling starts to check for data to read
*
* @private
* @param {Number} resultCode Indicates whether the connection was successful
*/
TcpClient.prototype._onConnectComplete = function(resultCode) {
// Start polling for reads.
this.isConnected = true;
setTimeout(this._periodicallyRead.bind(this), this.pollInterval);
if (this.callbacks.connect) {
log('connect complete');
this.callbacks.connect();
}
log('onConnectComplete');
};
/**
* Checks for new data to read from the socket
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-read
*/
TcpClient.prototype._periodicallyRead = function() {
var that = this;
socket.getInfo(this.socketId, function (info) {
if (info.connected) {
setTimeout(that._periodicallyRead.bind(that), that.pollInterval);
socket.read(that.socketId, null, that._onDataRead.bind(that));
} else if (that.isConnected) {
log('socket disconnect detected');
that.disconnect();
}
});
};
/**
* Callback function for when data has been read from the socket.
* Converts the array buffer that is read in to a string
* and sends it on for further processing by passing it to
* the previously assigned callback function.
*
* @private
* @see TcpClient.prototype.addResponseListener
* @param {Object} readInfo The incoming message
*/
TcpClient.prototype._onDataRead = function(readInfo) {
// Call received callback if there's data in the response.
if (readInfo.resultCode > 0) {
log('onDataRead');
/*
// Debug
var bytes = [], u8 = new Uint8Array(readInfo.data);
for (var i = 0; i < u8.length; i++) {
bytes.push(u8[i]);
}
log("received bytes: " + (bytes.join(',')));
*/
if (this.callbacks.recvBuffer) {
// Return raw ArrayBuffer directly.
this.callbacks.recvBuffer(readInfo.data);
}
if (this.callbacks.recvString) {
// Convert ArrayBuffer to string.
this._arrayBufferToString(readInfo.data, function(str) {
this.callbacks.recvString(str);
}.bind(this));
}
// Trigger another read right away
setTimeout(this._periodicallyRead.bind(this), 0);
}
};
/**
* Callback for when data has been successfully
* written to the socket.
*
* @private
* @param {Object} writeInfo The outgoing message
*/
TcpClient.prototype._onWriteComplete = function(writeInfo) {
log('onWriteComplete');
// Call sent callback.
if (this.callbacks.sent) {
this.callbacks.sent(writeInfo);
}
};
/**
* Converts an array buffer to a string
*
* @private
* @param {ArrayBuffer} buf The buffer to convert
* @param {Function} callback The function to call when conversion is complete
*/
TcpClient.prototype._arrayBufferToString = function(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
};
/**
* Converts a string to an array buffer
*
* @private
* @param {String} str The string to convert
* @param {Function} callback The function to call when conversion is complete
*/
TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
var bb = new Blob([str]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsArrayBuffer(bb);
};
/**
* Wrapper function for logging
*/
function log(msg) {
console.log(msg);
}
/**
* Wrapper function for error logging
*/
function error(msg) {
console.error(msg);
}
exports.TcpClient = TcpClient;
})(window);
...@@ -75,199 +75,202 @@ ...@@ -75,199 +75,202 @@
* fine Java utilities: http://www.acme.com/java/ * fine Java utilities: http://www.acme.com/java/
*/ */
"use strict"; /* jslint white: false */
/*jslint white: false, bitwise: false, plusplus: false */
function DES(passwd) { function DES(passwd) {
"use strict";
// Tables, permutations, S-boxes, etc. // Tables, permutations, S-boxes, etc.
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, // jshint -W013
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
keys = []; z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
keys = [];
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; // jshint -W015
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, a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
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, 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,
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, 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,
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|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,
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; 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];
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=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
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, 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,
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, 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|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]; 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,
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; 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];
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, a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
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, 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,
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|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,
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]; 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,
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; 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];
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, a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
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, 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,
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, 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,
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]; 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,
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|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];
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=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
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, 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,
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, 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,
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]; 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,
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|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];
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, a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
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, 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,
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, 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,
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]; 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=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; 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];
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, a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
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, 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|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, 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,
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]; 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,
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|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];
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, a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
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, 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,
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, 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,
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]; a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
// jshint +W013,+W015
// Set the key. // Set the key.
function setKeys(keyBlock) { function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi; raw0, raw1, rawi, KnLi;
for (j = 0, l = 56; j < 56; ++j, l-=8) { for (j = 0, l = 56; j < 56; ++j, l -= 8) {
l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1 l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
m = l & 0x7; m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0; pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
} }
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
m = i << 1; m = i << 1;
n = m + 1; n = m + 1;
kn[m] = kn[n] = 0; kn[m] = kn[n] = 0;
for (o=28; o<59; o+=28) { for (o = 28; o < 59; o += 28) {
for (j = o-28; j < o; ++j) { for (j = o - 28; j < o; ++j) {
l = j + totrot[i]; l = j + totrot[i];
if (l < o) { if (l < o) {
pcr[j] = pc1m[l]; pcr[j] = pc1m[l];
} else { } else {
pcr[j] = pc1m[l - 28]; pcr[j] = pc1m[l - 28];
}
} }
} }
} for (j = 0; j < 24; ++j) {
for (j = 0; j < 24; ++j) { if (pcr[PC2[j]] !== 0) {
if (pcr[PC2[j]] !== 0) { kn[m] |= 1 << (23 - j);
kn[m] |= 1<<(23-j); }
} if (pcr[PC2[j + 24]] !== 0) {
if (pcr[PC2[j + 24]] !== 0) { kn[n] |= 1 << (23 - j);
kn[n] |= 1<<(23-j); }
} }
} }
}
// cookey // cookey
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) { for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
raw0 = kn[rawi++]; raw0 = kn[rawi++];
raw1 = kn[rawi++]; raw1 = kn[rawi++];
keys[KnLi] = (raw0 & 0x00fc0000) << 6; keys[KnLi] = (raw0 & 0x00fc0000) << 6;
keys[KnLi] |= (raw0 & 0x00000fc0) << 10; keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10; keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
++KnLi; ++KnLi;
keys[KnLi] = (raw0 & 0x0003f000) << 12; keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16; keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f); keys[KnLi] |= (raw1 & 0x0000003f);
++KnLi; ++KnLi;
}
} }
}
// Encrypt 8 bytes of text // Encrypt 8 bytes of text
function enc8(text) { function enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0, var i = 0, b = text.slice(), fval, keysi = 0,
l, r, x; // left, right, accumulator l, r, x; // left, right, accumulator
// Squash 8 bytes to 2 ints // Squash 8 bytes to 2 ints
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
x = ((l >>> 4) ^ r) & 0x0f0f0f0f; x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
r ^= x; r ^= x;
l ^= (x << 4); l ^= (x << 4);
x = ((l >>> 16) ^ r) & 0x0000ffff; x = ((l >>> 16) ^ r) & 0x0000ffff;
r ^= x; r ^= x;
l ^= (x << 16); l ^= (x << 16);
x = ((r >>> 2) ^ l) & 0x33333333; x = ((r >>> 2) ^ l) & 0x33333333;
l ^= x; l ^= x;
r ^= (x << 2); r ^= (x << 2);
x = ((r >>> 8) ^ l) & 0x00ff00ff; x = ((r >>> 8) ^ l) & 0x00ff00ff;
l ^= x; l ^= x;
r ^= (x << 8); r ^= (x << 8);
r = (r << 1) | ((r >>> 31) & 1); r = (r << 1) | ((r >>> 31) & 1);
x = (l ^ r) & 0xaaaaaaaa; x = (l ^ r) & 0xaaaaaaaa;
l ^= x; l ^= x;
r ^= x; r ^= x;
l = (l << 1) | ((l >>> 31) & 1); l = (l << 1) | ((l >>> 31) & 1);
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
x = (r << 28) | (r >>> 4); x = (r << 28) | (r >>> 4);
x ^= keys[keysi++]; x ^= keys[keysi++];
fval = SP7[x & 0x3f]; fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f];
x = r ^ keys[keysi++]; x = r ^ keys[keysi++];
fval |= SP8[x & 0x3f]; fval |= SP8[x & 0x3f];
fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f]; fval |= SP2[(x >>> 24) & 0x3f];
l ^= fval; l ^= fval;
x = (l << 28) | (l >>> 4); x = (l << 28) | (l >>> 4);
x ^= keys[keysi++]; x ^= keys[keysi++];
fval = SP7[x & 0x3f]; fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f];
x = l ^ keys[keysi++]; x = l ^ keys[keysi++];
fval |= SP8[x & 0x0000003f]; fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f]; fval |= SP2[(x >>> 24) & 0x3f];
r ^= fval; r ^= fval;
} }
r = (r << 31) | (r >>> 1); r = (r << 31) | (r >>> 1);
x = (l ^ r) & 0xaaaaaaaa; x = (l ^ r) & 0xaaaaaaaa;
l ^= x; l ^= x;
r ^= x; r ^= x;
l = (l << 31) | (l >>> 1); l = (l << 31) | (l >>> 1);
x = ((l >>> 8) ^ r) & 0x00ff00ff; x = ((l >>> 8) ^ r) & 0x00ff00ff;
r ^= x; r ^= x;
l ^= (x << 8); l ^= (x << 8);
x = ((l >>> 2) ^ r) & 0x33333333; x = ((l >>> 2) ^ r) & 0x33333333;
r ^= x; r ^= x;
l ^= (x << 2); l ^= (x << 2);
x = ((r >>> 16) ^ l) & 0x0000ffff; x = ((r >>> 16) ^ l) & 0x0000ffff;
l ^= x; l ^= x;
r ^= (x << 16); r ^= (x << 16);
x = ((r >>> 4) ^ l) & 0x0f0f0f0f; x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
l ^= x; l ^= x;
r ^= (x << 4); r ^= (x << 4);
// Spread ints to bytes // Spread ints to bytes
x = [r, l]; x = [r, l];
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256; b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
if (b[i] < 0) { b[i] += 256; } // unsigned if (b[i] < 0) { b[i] += 256; } // unsigned
}
return b;
} }
return b;
}
// Encrypt 16 bytes of text using passwd as key // Encrypt 16 bytes of text using passwd as key
function encrypt(t) { function encrypt(t) {
return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16))); return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
} }
setKeys(passwd); // Setup keys setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface return {'encrypt': encrypt}; // Public interface
} // function DES } // function DES
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
/*jslint browser: true, white: false, bitwise: false */ /*jslint browser: true, white: false */
/*global Util, Base64, changeCursor */ /*global Util, Base64, changeCursor */
function Display(defaults) { var Display;
"use strict";
var that = {}, // Public API methods
conf = {}, // Configuration attributes
// Private Display namespace variables
c_ctx = null,
c_forceCanvas = false,
// Queued drawing actions for in-order rendering
renderQ = [],
// Predefine function variables (jslint)
imageDataGet, rgbImageData, bgrxImageData, cmapImageData,
setFillColor, rescale, scan_renderQ,
// The full frame buffer (logical canvas) size
fb_width = 0,
fb_height = 0,
// The visible "physical canvas" viewport
viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 },
cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1},
c_prevStyle = "",
tile = null,
tile16x16 = null,
tile_x = 0,
tile_y = 0;
// Configuration attributes
Util.conf_defaults(conf, that, defaults, [
['target', 'wo', 'dom', null, 'Canvas element for rendering'],
['context', 'ro', 'raw', null, 'Canvas 2D context for rendering (read-only)'],
['logo', 'rw', 'raw', null, 'Logo to display when cleared: {"width": width, "height": height, "data": data}'],
['true_color', 'rw', 'bool', true, 'Use true-color pixel data'],
['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'],
['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'],
['viewport', 'rw', 'bool', false, 'Use a viewport set with viewportChange()'],
['width', 'rw', 'int', null, 'Display area width'],
['height', 'rw', 'int', null, 'Display area height'],
['render_mode', 'ro', 'str', '', 'Canvas rendering mode (read-only)'],
['prefer_js', 'rw', 'str', null, 'Prefer Javascript over canvas methods'],
['cursor_uri', 'rw', 'raw', null, 'Can we render cursor using data URI']
]);
// Override some specific getters/setters (function () {
that.get_context = function () { return c_ctx; }; "use strict";
that.set_scale = function(scale) { rescale(scale); }; Display = function (defaults) {
this._drawCtx = null;
this._c_forceCanvas = false;
that.set_width = function (val) { that.resize(val, fb_height); }; this._renderQ = []; // queue drawing actions for in-oder rendering
that.get_width = function() { return fb_width; };
that.set_height = function (val) { that.resize(fb_width, val); }; // the full frame buffer (logical canvas) size
that.get_height = function() { return fb_height; }; this._fb_width = 0;
this._fb_height = 0;
// the visible "physical canvas" viewport
this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 };
this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 };
this._prevDrawStyle = "";
this._tile = null;
this._tile16x16 = null;
this._tile_x = 0;
this._tile_y = 0;
// Util.set_defaults(this, defaults, {
// Private functions 'true_color': true,
// 'colourMap': [],
'scale': 1.0,
'viewport': false,
'render_mode': ''
});
// Create the public API interface Util.Debug(">> Display.constructor");
function constructor() {
Util.Debug(">> Display.constructor");
var c, func, i, curDat, curSave, if (!this._target) {
has_imageData = false, UE = Util.Engine; throw new Error("Target must be set");
}
if (! conf.target) { throw("target must be set"); } if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (typeof conf.target === 'string') { if (!this._target.getContext) {
throw("target must be a DOM element"); throw new Error("no getContext method");
} }
c = conf.target; if (!this._drawCtx) {
this._drawCtx = this._target.getContext('2d');
}
if (! c.getContext) { throw("no getContext method"); } Util.Debug("User Agent: " + navigator.userAgent);
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
if (! c_ctx) { c_ctx = c.getContext('2d'); } this.clear();
Util.Debug("User Agent: " + navigator.userAgent); // Check canvas features
if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); } if ('createImageData' in this._drawCtx) {
if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); } this._render_mode = 'canvas rendering';
if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); } } else {
if (UE.presto) { Util.Debug("Browser: presto " + UE.presto); } throw new Error("Canvas does not support createImageData");
}
that.clear(); if (this._prefer_js === null) {
Util.Info("Prefering javascript operations");
this._prefer_js = true;
}
// Check canvas features // Determine browser support for setting the cursor via data URI scheme
if ('createImageData' in c_ctx) { var curDat = [];
conf.render_mode = "canvas rendering"; for (var i = 0; i < 8 * 8 * 4; i++) {
} else { curDat.push(255);
throw("Canvas does not support createImageData"); }
} try {
if (conf.prefer_js === null) { var curSave = this._target.style.cursor;
Util.Info("Prefering javascript operations"); Display.changeCursor(this._target, curDat, curDat, 2, 2, 8, 8);
conf.prefer_js = true; if (this._target.style.cursor) {
} if (this._cursor_uri === null || this._cursor_uri === undefined) {
this._cursor_uri = true;
}
Util.Info("Data URI scheme cursor supported");
} else {
if (this._cursor_uri === null || this._cursor_uri === undefined) {
this._cursor_uri = false;
}
Util.Warn("Data URI scheme cursor not supported");
}
this._target.style.cursor = curSave;
} catch (exc) {
Util.Error("Data URI scheme cursor test exception: " + exc);
this._cursor_uri = false;
}
// Initialize cached tile imageData Util.Debug("<< Display.constructor");
tile16x16 = c_ctx.createImageData(16, 16); };
/* Display.prototype = {
* Determine browser support for setting the cursor via data URI // Public methods
* scheme viewportChange: function (deltaX, deltaY, width, height) {
*/ var vp = this._viewportLoc;
curDat = []; var cr = this._cleanRect;
for (i=0; i < 8 * 8 * 4; i += 1) { var canvas = this._target;
curDat.push(255);
} if (!this._viewport) {
try { Util.Debug("Setting viewport to full display region");
curSave = c.style.cursor; deltaX = -vp.w; // clamped later of out of bounds
changeCursor(conf.target, curDat, curDat, 2, 2, 8, 8); deltaY = -vp.h;
if (c.style.cursor) { width = this._fb_width;
if (conf.cursor_uri === null) { height = this._fb_height;
conf.cursor_uri = true;
} }
Util.Info("Data URI scheme cursor supported");
} else { if (typeof(deltaX) === "undefined") { deltaX = 0; }
if (conf.cursor_uri === null) { if (typeof(deltaY) === "undefined") { deltaY = 0; }
conf.cursor_uri = false; if (typeof(width) === "undefined") { width = vp.w; }
if (typeof(height) === "undefined") { height = vp.h; }
// Size change
if (width > this._fb_width) { width = this._fb_width; }
if (height > this._fb_height) { height = this._fb_height; }
if (vp.w !== width || vp.h !== height) {
// Change width
if (width < vp.w && cr.x2 > vp.x + width - 1) {
cr.x2 = vp.x + width - 1;
}
vp.w = width;
// Change height
if (height < vp.h && cr.y2 > vp.y + height - 1) {
cr.y2 = vp.y + height - 1;
}
vp.h = height;
var saveImg = null;
if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) {
var img_width = canvas.width < vp.w ? canvas.width : vp.w;
var img_height = canvas.height < vp.h ? canvas.height : vp.h;
saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height);
}
canvas.width = vp.w;
canvas.height = vp.h;
if (saveImg) {
this._drawCtx.putImageData(saveImg, 0, 0);
}
} }
Util.Warn("Data URI scheme cursor not supported");
}
c.style.cursor = curSave;
} catch (exc2) {
Util.Error("Data URI scheme cursor test exception: " + exc2);
conf.cursor_uri = false;
}
Util.Debug("<< Display.constructor");
return that ;
}
rescale = function(factor) {
var c, tp, x, y,
properties = ['transform', 'WebkitTransform', 'MozTransform', null];
c = conf.target;
tp = properties.shift();
while (tp) {
if (typeof c.style[tp] !== 'undefined') {
break;
}
tp = properties.shift();
}
if (tp === null) {
Util.Debug("No scaling support");
return;
}
if (typeof(factor) === "undefined") {
factor = conf.scale;
} else if (factor > 1.0) {
factor = 1.0;
} else if (factor < 0.1) {
factor = 0.1;
}
if (conf.scale === factor) {
//Util.Debug("Display already scaled to '" + factor + "'");
return;
}
conf.scale = factor;
x = c.width - c.width * factor;
y = c.height - c.height * factor;
c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
};
setFillColor = function(color) {
var bgr, newStyle;
if (conf.true_color) {
bgr = color;
} else {
bgr = conf.colourMap[color[0]];
}
newStyle = "rgb(" + bgr[2] + "," + bgr[1] + "," + bgr[0] + ")";
if (newStyle !== c_prevStyle) {
c_ctx.fillStyle = newStyle;
c_prevStyle = newStyle;
}
};
//
// Public API interface functions
//
// Shift and/or resize the visible viewport
that.viewportChange = function(deltaX, deltaY, width, height) {
var c = conf.target, v = viewport, cr = cleanRect,
saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h;
if (!conf.viewport) {
Util.Debug("Setting viewport to full display region");
deltaX = -v.w; // Clamped later if out of bounds
deltaY = -v.h; // Clamped later if out of bounds
width = fb_width;
height = fb_height;
}
if (typeof(deltaX) === "undefined") { deltaX = 0; }
if (typeof(deltaY) === "undefined") { deltaY = 0; }
if (typeof(width) === "undefined") { width = v.w; }
if (typeof(height) === "undefined") { height = v.h; }
// Size change
if (width > fb_width) { width = fb_width; }
if (height > fb_height) { height = fb_height; }
if ((v.w !== width) || (v.h !== height)) {
// Change width
if ((width < v.w) && (cr.x2 > v.x + width -1)) {
cr.x2 = v.x + width - 1;
}
v.w = width;
// Change height var vx2 = vp.x + vp.w - 1;
if ((height < v.h) && (cr.y2 > v.y + height -1)) { var vy2 = vp.y + vp.h - 1;
cr.y2 = v.y + height - 1;
}
v.h = height;
// Position change
if (v.w > 0 && v.h > 0 && c.width > 0 && c.height > 0) { if (deltaX < 0 && vp.x + deltaX < 0) {
saveImg = c_ctx.getImageData(0, 0, deltaX = -vp.x;
(c.width < v.w) ? c.width : v.w, }
(c.height < v.h) ? c.height : v.h); if (vx2 + deltaX >= this._fb_width) {
} deltaX -= vx2 + deltaX - this._fb_width + 1;
}
c.width = v.w; if (vp.y + deltaY < 0) {
c.height = v.h; deltaY = -vp.y;
}
if (vy2 + deltaY >= this._fb_height) {
deltaY -= (vy2 + deltaY - this._fb_height + 1);
}
if (saveImg) { if (deltaX === 0 && deltaY === 0) {
c_ctx.putImageData(saveImg, 0, 0); return;
} }
} Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
vx2 = v.x + v.w - 1; vp.x += deltaX;
vy2 = v.y + v.h - 1; vx2 += deltaX;
vp.y += deltaY;
vy2 += deltaY;
// Position change
// Update the clean rectangle
if ((deltaX < 0) && ((v.x + deltaX) < 0)) { if (vp.x > cr.x1) {
deltaX = - v.x; cr.x1 = vp.x;
} }
if ((vx2 + deltaX) >= fb_width) { if (vx2 < cr.x2) {
deltaX -= ((vx2 + deltaX) - fb_width + 1); cr.x2 = vx2;
} }
if (vp.y > cr.y1) {
if ((v.y + deltaY) < 0) { cr.y1 = vp.y;
deltaY = - v.y; }
} if (vy2 < cr.y2) {
if ((vy2 + deltaY) >= fb_height) { cr.y2 = vy2;
deltaY -= ((vy2 + deltaY) - fb_height + 1); }
}
var x1, w;
if ((deltaX === 0) && (deltaY === 0)) { if (deltaX < 0) {
//Util.Debug("skipping viewport change"); // Shift viewport left, redraw left section
return; x1 = 0;
} w = -deltaX;
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); } else {
// Shift viewport right, redraw right section
v.x += deltaX; x1 = vp.w - deltaX;
vx2 += deltaX; w = deltaX;
v.y += deltaY; }
vy2 += deltaY;
var y1, h;
// Update the clean rectangle if (deltaY < 0) {
if (v.x > cr.x1) { // Shift viewport up, redraw top section
cr.x1 = v.x; y1 = 0;
} h = -deltaY;
if (vx2 < cr.x2) { } else {
cr.x2 = vx2; // Shift viewport down, redraw bottom section
} y1 = vp.h - deltaY;
if (v.y > cr.y1) { h = deltaY;
cr.y1 = v.y; }
}
if (vy2 < cr.y2) { // Copy the valid part of the viewport to the shifted location
cr.y2 = vy2; var saveStyle = this._drawCtx.fillStyle;
} this._drawCtx.fillStyle = "rgb(255,255,255)";
if (deltaX !== 0) {
if (deltaX < 0) { this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, 0, vp.w, vp.h);
// Shift viewport left, redraw left section this._drawCtx.fillRect(x1, 0, w, vp.h);
x1 = 0; }
w = - deltaX; if (deltaY !== 0) {
} else { this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, 0, -deltaY, vp.w, vp.h);
// Shift viewport right, redraw right section this._drawCtx.fillRect(0, y1, vp.w, h);
x1 = v.w - deltaX; }
w = deltaX; this._drawCtx.fillStyle = saveStyle;
} },
if (deltaY < 0) {
// Shift viewport up, redraw top section // Return a map of clean and dirty areas of the viewport and reset the
y1 = 0; // tracking of clean and dirty areas
h = - deltaY; //
} else { // Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h},
// Shift viewport down, redraw bottom section // 'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] }
y1 = v.h - deltaY; getCleanDirtyReset: function () {
h = deltaY; var vp = this._viewportLoc;
} var cr = this._cleanRect;
// Copy the valid part of the viewport to the shifted location var cleanBox = { 'x': cr.x1, 'y': cr.y1,
saveStyle = c_ctx.fillStyle; 'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 };
c_ctx.fillStyle = "rgb(255,255,255)";
if (deltaX !== 0) { var dirtyBoxes = [];
//that.copyImage(0, 0, -deltaX, 0, v.w, v.h); if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) {
//that.fillRect(x1, 0, w, v.h, [255,255,255]); // Whole viewport is dirty
c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h); dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h });
c_ctx.fillRect(x1, 0, w, v.h); } else {
} // Redraw dirty regions
if (deltaY !== 0) { var vx2 = vp.x + vp.w - 1;
//that.copyImage(0, 0, 0, -deltaY, v.w, v.h); var vy2 = vp.y + vp.h - 1;
//that.fillRect(0, y1, v.w, h, [255,255,255]);
c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h); if (vp.x < cr.x1) {
c_ctx.fillRect(0, y1, v.w, h); // left side dirty region
} dirtyBoxes.push({'x': vp.x, 'y': vp.y,
c_ctx.fillStyle = saveStyle; 'w': cr.x1 - vp.x + 1, 'h': vp.h});
}; }
if (vx2 > cr.x2) {
// right side dirty region
// Return a map of clean and dirty areas of the viewport and reset the dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y,
// tracking of clean and dirty areas. 'w': vx2 - cr.x2, 'h': vp.h});
// }
// Returns: {'cleanBox': {'x': x, 'y': y, 'w': w, 'h': h}, if(vp.y < cr.y1) {
// 'dirtyBoxes': [{'x': x, 'y': y, 'w': w, 'h': h}, ...]} // top/middle dirty region
that.getCleanDirtyReset = function() { dirtyBoxes.push({'x': cr.x1, 'y': vp.y,
var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [], 'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y});
vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1; }
if (vy2 > cr.y2) {
// bottom/middle dirty region
// Copy the cleanRect dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1,
cleanBox = {'x': c.x1, 'y': c.y1, 'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2});
'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1}; }
}
if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) {
// Whole viewport is dirty this._cleanRect = {'x1': vp.x, 'y1': vp.y,
dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h}); 'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1};
} else {
// Redraw dirty regions return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes};
if (v.x < c.x1) { },
// left side dirty region
dirtyBoxes.push({'x': v.x, 'y': v.y, absX: function (x) {
'w': c.x1 - v.x + 1, 'h': v.h}); return x + this._viewportLoc.x;
} },
if (vx2 > c.x2) {
// right side dirty region absY: function (y) {
dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y, return y + this._viewportLoc.y;
'w': vx2 - c.x2, 'h': v.h}); },
}
if (v.y < c.y1) { resize: function (width, height) {
// top/middle dirty region this._prevDrawStyle = "";
dirtyBoxes.push({'x': c.x1, 'y': v.y,
'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y}); this._fb_width = width;
} this._fb_height = height;
if (vy2 > c.y2) {
// bottom/middle dirty region this._rescale(this._scale);
dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1,
'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2}); this.viewportChange();
} },
}
clear: function () {
// Reset the cleanRect to the whole viewport if (this._logo) {
cleanRect = {'x1': v.x, 'y1': v.y, this.resize(this._logo.width, this._logo.height);
'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1}; this.blitStringImage(this._logo.data, 0, 0);
} else {
return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; if (Util.Engine.trident === 6) {
}; // NB(directxman12): there's a bug in IE10 where we can fail to actually
// clear the canvas here because of the resize.
// Translate viewport coordinates to absolute coordinates // Clearing the current viewport first fixes the issue
that.absX = function(x) { this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h);
return x + viewport.x; }
}; this.resize(640, 20);
that.absY = function(y) { this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h);
return y + viewport.y; }
};
this._renderQ = [];
},
that.resize = function(width, height) {
c_prevStyle = ""; fillRect: function (x, y, width, height, color) {
this._setFillColor(color);
fb_width = width; this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height);
fb_height = height; },
rescale(conf.scale); copyImage: function (old_x, old_y, new_x, new_y, w, h) {
that.viewportChange(); var x1 = old_x - this._viewportLoc.x;
}; var y1 = old_y - this._viewportLoc.y;
var x2 = new_x - this._viewportLoc.x;
that.clear = function() { var y2 = new_y - this._viewportLoc.y;
if (conf.logo) { this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h);
that.resize(conf.logo.width, conf.logo.height); },
that.blitStringImage(conf.logo.data, 0, 0);
} else { // start updating a tile
that.resize(640, 20); startTile: function (x, y, width, height, color) {
c_ctx.clearRect(0, 0, viewport.w, viewport.h); this._tile_x = x;
} this._tile_y = y;
if (width === 16 && height === 16) {
renderQ = []; this._tile = this._tile16x16;
} else {
// No benefit over default ("source-over") in Chrome and firefox this._tile = this._drawCtx.createImageData(width, height);
//c_ctx.globalCompositeOperation = "copy"; }
};
if (this._prefer_js) {
that.fillRect = function(x, y, width, height, color) { var bgr;
setFillColor(color); if (this._true_color) {
c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height); bgr = color;
};
that.copyImage = function(old_x, old_y, new_x, new_y, w, h) {
var x1 = old_x - viewport.x, y1 = old_y - viewport.y,
x2 = new_x - viewport.x, y2 = new_y - viewport.y;
c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h);
};
// Start updating a tile
that.startTile = function(x, y, width, height, color) {
var data, bgr, red, green, blue, i;
tile_x = x;
tile_y = y;
if ((width === 16) && (height === 16)) {
tile = tile16x16;
} else {
tile = c_ctx.createImageData(width, height);
}
data = tile.data;
if (conf.prefer_js) {
if (conf.true_color) {
bgr = color;
} else {
bgr = conf.colourMap[color[0]];
}
red = bgr[2];
green = bgr[1];
blue = bgr[0];
for (i = 0; i < (width * height * 4); i+=4) {
data[i ] = red;
data[i + 1] = green;
data[i + 2] = blue;
data[i + 3] = 255;
}
} else {
that.fillRect(x, y, width, height, color);
}
};
// Update sub-rectangle of the current tile
that.subTile = function(x, y, w, h, color) {
var data, p, bgr, red, green, blue, width, j, i, xend, yend;
if (conf.prefer_js) {
data = tile.data;
width = tile.width;
if (conf.true_color) {
bgr = color;
} else {
bgr = conf.colourMap[color[0]];
}
red = bgr[2];
green = bgr[1];
blue = bgr[0];
xend = x + w;
yend = y + h;
for (j = y; j < yend; j += 1) {
for (i = x; i < xend; i += 1) {
p = (i + (j * width) ) * 4;
data[p ] = red;
data[p + 1] = green;
data[p + 2] = blue;
data[p + 3] = 255;
}
}
} else {
that.fillRect(tile_x + x, tile_y + y, w, h, color);
}
};
// Draw the current tile to the screen
that.finishTile = function() {
if (conf.prefer_js) {
c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y);
}
// else: No-op, if not prefer_js then already done by setSubTile
};
rgbImageData = function(x, y, width, height, arr, offset) {
var img, i, j, data, v = viewport;
/*
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
(x - v.x + width < 0) || (y - v.y + height < 0)) {
// Skipping because outside of viewport
return;
}
*/
img = c_ctx.createImageData(width, height);
data = img.data;
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+3) {
data[i ] = arr[j ];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - v.x, y - v.y);
};
bgrxImageData = function(x, y, width, height, arr, offset) {
var img, i, j, data, v = viewport;
/*
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
(x - v.x + width < 0) || (y - v.y + height < 0)) {
// Skipping because outside of viewport
return;
}
*/
img = c_ctx.createImageData(width, height);
data = img.data;
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
data[i ] = arr[j + 2];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j ];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - v.x, y - v.y);
};
cmapImageData = function(x, y, width, height, arr, offset) {
var img, i, j, data, bgr, cmap;
img = c_ctx.createImageData(width, height);
data = img.data;
cmap = conf.colourMap;
for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
bgr = cmap[arr[j]];
data[i ] = bgr[2];
data[i + 1] = bgr[1];
data[i + 2] = bgr[0];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - viewport.x, y - viewport.y);
};
that.blitImage = function(x, y, width, height, arr, offset) {
if (conf.true_color) {
bgrxImageData(x, y, width, height, arr, offset);
} else {
cmapImageData(x, y, width, height, arr, offset);
}
};
that.blitRgbImage = function(x, y, width, height, arr, offset) {
if (conf.true_color) {
rgbImageData(x, y, width, height, arr, offset);
} else {
// prolly wrong...
cmapImageData(x, y, width, height, arr, offset);
}
};
that.blitStringImage = function(str, x, y) {
var img = new Image();
img.onload = function () {
c_ctx.drawImage(img, x - viewport.x, y - viewport.y);
};
img.src = str;
};
// Wrap ctx.drawImage but relative to viewport
that.drawImage = function(img, x, y) {
c_ctx.drawImage(img, x - viewport.x, y - viewport.y);
};
that.renderQ_push = function(action) {
renderQ.push(action);
if (renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise
// the scanner will start polling the queue (every
// requestAnimationFrame interval)
scan_renderQ();
}
};
scan_renderQ = function() {
var a, ready = true;
while (ready && renderQ.length > 0) {
a = renderQ[0];
switch (a.type) {
case 'copy':
that.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height);
break;
case 'fill':
that.fillRect(a.x, a.y, a.width, a.height, a.color);
break;
case 'blit':
that.blitImage(a.x, a.y, a.width, a.height, a.data, 0);
break;
case 'blitRgb':
that.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0);
break;
case 'img':
if (a.img.complete) {
that.drawImage(a.img, a.x, a.y);
} else { } else {
// We need to wait for this image to 'load' bgr = this._colourMap[color[0]];
// to keep things in-order
ready = false;
} }
break; var red = bgr[2];
} var green = bgr[1];
if (ready) { var blue = bgr[0];
a = renderQ.shift();
} var data = this._tile.data;
} for (var i = 0; i < width * height * 4; i += 4) {
if (renderQ.length > 0) { data[i] = red;
requestAnimFrame(scan_renderQ); data[i + 1] = green;
} data[i + 2] = blue;
}; data[i + 3] = 255;
}
} else {
this.fillRect(x, y, width, height, color);
}
},
// update sub-rectangle of the current tile
subTile: function (x, y, w, h, color) {
if (this._prefer_js) {
var bgr;
if (this._true_color) {
bgr = color;
} else {
bgr = this._colourMap[color[0]];
}
var red = bgr[2];
var green = bgr[1];
var blue = bgr[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;
}
}
} else {
this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color);
}
},
// draw the current tile to the screen
finishTile: function () {
if (this._prefer_js) {
this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x,
this._tile_y - this._viewportLoc.y);
}
// else: No-op -- already done by setSubTile
},
that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { blitImage: function (x, y, width, height, arr, offset) {
if (conf.cursor_uri === false) { if (this._true_color) {
Util.Warn("changeCursor called but no cursor data URI support"); this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
return; } else {
} this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
if (conf.true_color) { blitRgbImage: function (x, y , width, height, arr, offset) {
changeCursor(conf.target, pixels, mask, hotx, hoty, w, h); if (this._true_color) {
} else { this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
changeCursor(conf.target, pixels, mask, hotx, hoty, w, h, conf.colourMap); } else {
} // probably wrong?
}; this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
blitStringImage: function (str, x, y) {
var img = new Image();
img.onload = function () {
this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y);
}.bind(this);
img.src = str;
return img; // for debugging purposes
},
// wrap ctx.drawImage but relative to viewport
drawImage: function (img, x, y) {
this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y);
},
renderQ_push: function (action) {
this._renderQ.push(action);
if (this._renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise
// the scanner will start polling the queue (every
// requestAnimationFrame interval)
this._scan_renderQ();
}
},
that.defaultCursor = function() { changeCursor: function (pixels, mask, hotx, hoty, w, h) {
conf.target.style.cursor = "default"; if (this._cursor_uri === false) {
}; Util.Warn("changeCursor called but no cursor data URI support");
return;
}
return constructor(); // Return the public API interface if (this._true_color) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
} else {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h, this._colourMap);
}
},
defaultCursor: function () {
this._target.style.cursor = "default";
},
// Overridden getters/setters
get_context: function () {
return this._drawCtx;
},
set_scale: function (scale) {
this._rescale(scale);
},
set_width: function (w) {
this.resize(w, this._fb_height);
},
get_width: function () {
return this._fb_width;
},
set_height: function (h) {
this.resize(this._fb_width, h);
},
get_height: function () {
return this._fb_height;
},
// Private Methods
_rescale: function (factor) {
var canvas = this._target;
var properties = ['transform', 'WebkitTransform', 'MozTransform'];
var transform_prop;
while ((transform_prop = properties.shift())) {
if (typeof canvas.style[transform_prop] !== 'undefined') {
break;
}
}
} // End of Display() if (transform_prop === null) {
Util.Debug("No scaling support");
return;
}
if (typeof(factor) === "undefined") {
factor = this._scale;
} else if (factor > 1.0) {
factor = 1.0;
} else if (factor < 0.1) {
factor = 0.1;
}
/* Set CSS cursor property using data URI encoded cursor file */ if (this._scale === factor) {
function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) { return;
"use strict"; }
var cur = [], rgb, IHDRsz, RGBsz, ANDsz, XORsz, url, idx, alpha, x, y;
//Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h);
// Push multi-byte little-endian values
cur.push16le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF );
};
cur.push32le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF );
};
IHDRsz = 40; this._scale = factor;
RGBsz = w * h * 4; var x = canvas.width - (canvas.width * factor);
XORsz = Math.ceil( (w * h) / 8.0 ); var y = canvas.height - (canvas.height * factor);
ANDsz = Math.ceil( (w * h) / 8.0 ); canvas.style[transform_prop] = 'scale(' + this._scale + ') translate(-' + x + 'px, -' + y + 'px)';
},
// Main header
cur.push16le(0); // 0: Reserved _setFillColor: function (color) {
cur.push16le(2); // 2: .CUR type var bgr;
cur.push16le(1); // 4: Number of images, 1 for non-animated ico if (this._true_color) {
bgr = color;
// Cursor #1 header (ICONDIRENTRY)
cur.push(w); // 6: width
cur.push(h); // 7: height
cur.push(0); // 8: colors, 0 -> true-color
cur.push(0); // 9: reserved
cur.push16le(hotx); // 10: hotspot x coordinate
cur.push16le(hoty); // 12: hotspot y coordinate
cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz);
// 14: cursor data byte size
cur.push32le(22); // 18: offset of cursor data in the file
// Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO)
cur.push32le(IHDRsz); // 22: Infoheader size
cur.push32le(w); // 26: Cursor width
cur.push32le(h*2); // 30: XOR+AND height
cur.push16le(1); // 34: number of planes
cur.push16le(32); // 36: bits per pixel
cur.push32le(0); // 38: Type of compression
cur.push32le(XORsz + ANDsz); // 43: Size of Image
// Gimp leaves this as 0
cur.push32le(0); // 46: reserved
cur.push32le(0); // 50: reserved
cur.push32le(0); // 54: reserved
cur.push32le(0); // 58: reserved
// 62: color data (RGBQUAD icColors[])
for (y = h-1; y >= 0; y -= 1) {
for (x = 0; x < w; x += 1) {
idx = y * Math.ceil(w / 8) + Math.floor(x/8);
alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
if (cmap) {
idx = (w * y) + x;
rgb = cmap[pixels[idx]];
cur.push(rgb[2]); // blue
cur.push(rgb[1]); // green
cur.push(rgb[0]); // red
cur.push(alpha); // alpha
} else { } else {
idx = ((w * y) + x) * 4; bgr = this._colourMap[color[0]];
cur.push(pixels[idx + 2]); // blue }
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx ]); // red var newStyle = 'rgb(' + bgr[2] + ',' + bgr[1] + ',' + bgr[0] + ')';
cur.push(alpha); // alpha if (newStyle !== this._prevDrawStyle) {
this._drawCtx.fillStyle = newStyle;
this._prevDrawStyle = newStyle;
}
},
_rgbImageData: function (x, y, vx, vy, 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 - vx, y - vy);
},
_bgrxImageData: function (x, y, vx, vy, 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 - vx, y - vy);
},
_cmapImageData: function (x, y, vx, vy, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
var cmap = this._colourMap;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j++) {
var bgr = cmap[arr[j]];
data[i] = bgr[2];
data[i + 1] = bgr[1];
data[i + 2] = bgr[0];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x - vx, y - vy);
},
_scan_renderQ: function () {
var ready = true;
while (ready && this._renderQ.length > 0) {
var a = this._renderQ[0];
switch (a.type) {
case 'copy':
this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height);
break;
case 'fill':
this.fillRect(a.x, a.y, a.width, a.height, a.color);
break;
case 'blit':
this.blitImage(a.x, a.y, a.width, a.height, a.data, 0);
break;
case 'blitRgb':
this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0);
break;
case 'img':
if (a.img.complete) {
this.drawImage(a.img, a.x, a.y);
} else {
// We need to wait for this image to 'load'
// to keep things in-order
ready = false;
}
break;
}
if (ready) {
this._renderQ.shift();
}
} }
if (this._renderQ.length > 0) {
requestAnimFrame(this._scan_renderQ.bind(this));
}
},
};
Util.make_properties(Display, [
['target', 'wo', 'dom'], // Canvas element for rendering
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "data": data}
['true_color', 'rw', 'bool'], // Use true-color pixel data
['colourMap', 'rw', 'arr'], // Colour map array (when not true-color)
['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0
['viewport', 'rw', 'bool'], // Use a viewport set with viewportChange()
['width', 'rw', 'int'], // Display area width
['height', 'rw', 'int'], // Display area height
['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only)
['prefer_js', 'rw', 'str'], // Prefer Javascript over canvas methods
['cursor_uri', 'rw', 'raw'] // Can we render cursor using data URI
]);
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
var w = w0;
var h = h0;
if (h < w) {
h = w; // increase h to make it square
} else {
w = h; // increase w to make it square
} }
}
// XOR/bitmask data (BYTE icXOR[]) var cur = [];
// (ignored, just needs to be right size)
for (y = 0; y < h; y += 1) { // Push multi-byte little-endian values
for (x = 0; x < Math.ceil(w / 8); x += 1) { cur.push16le = function (num) {
cur.push(0x00); this.push(num & 0xFF, (num >> 8) & 0xFF);
};
cur.push32le = function (num) {
this.push(num & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF);
};
var IHDRsz = 40;
var RGBsz = w * h * 4;
var XORsz = Math.ceil((w * h) / 8.0);
var ANDsz = Math.ceil((w * h) / 8.0);
cur.push16le(0); // 0: Reserved
cur.push16le(2); // 2: .CUR type
cur.push16le(1); // 4: Number of images, 1 for non-animated ico
// Cursor #1 header (ICONDIRENTRY)
cur.push(w); // 6: width
cur.push(h); // 7: height
cur.push(0); // 8: colors, 0 -> true-color
cur.push(0); // 9: reserved
cur.push16le(hotx); // 10: hotspot x coordinate
cur.push16le(hoty); // 12: hotspot y coordinate
cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz);
// 14: cursor data byte size
cur.push32le(22); // 18: offset of cursor data in the file
// Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO)
cur.push32le(IHDRsz); // 22: InfoHeader size
cur.push32le(w); // 26: Cursor width
cur.push32le(h * 2); // 30: XOR+AND height
cur.push16le(1); // 34: number of planes
cur.push16le(32); // 36: bits per pixel
cur.push32le(0); // 38: Type of compression
cur.push32le(XORsz + ANDsz);
// 42: Size of Image
cur.push32le(0); // 46: reserved
cur.push32le(0); // 50: reserved
cur.push32le(0); // 54: reserved
cur.push32le(0); // 58: reserved
// 62: color data (RGBQUAD icColors[])
var y, x;
for (y = h - 1; y >= 0; y--) {
for (x = 0; x < w; x++) {
if (x >= w0 || y >= h0) {
cur.push(0); // blue
cur.push(0); // green
cur.push(0); // red
cur.push(0); // alpha
} else {
var idx = y * Math.ceil(w0 / 8) + Math.floor(x / 8);
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
if (cmap) {
idx = (w0 * y) + x;
var rgb = cmap[pixels[idx]];
cur.push(rgb[2]); // blue
cur.push(rgb[1]); // green
cur.push(rgb[0]); // red
cur.push(alpha); // alpha
}
}
}
}
// XOR/bitmask data (BYTE icXOR[])
// (ignored, just needs to be the right size)
for (y = 0; y < h; y++) {
for (x = 0; x < Math.ceil(w / 8); x++) {
cur.push(0);
}
} }
}
// AND/bitmask data (BYTE icAND[]) // AND/bitmask data (BYTE icAND[])
// (ignored, just needs to be right size) // (ignored, just needs to be the right size)
for (y = 0; y < h; y += 1) { for (y = 0; y < h; y++) {
for (x = 0; x < Math.ceil(w / 8); x += 1) { for (x = 0; x < Math.ceil(w / 8); x++) {
cur.push(0x00); cur.push(0);
}
} }
}
url = "data:image/x-icon;base64," + Base64.encode(cur); var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default"; target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
//Util.Debug("<< changeCursor, cur.length: " + cur.length); };
} })();
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-2 or any later version (see LICENSE.txt) * Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
/*jslint browser: true, white: false, bitwise: false */ /*jslint browser: true, white: false */
/*global window, Util */ /*global window, Util */
var Keyboard, Mouse;
(function () {
"use strict";
//
// Keyboard event handler
//
Keyboard = function (defaults) {
this._keyDownList = []; // List of depressed keys
// (even if they are happy)
Util.set_defaults(this, defaults, {
'target': document,
'focused': true
});
// create the keyboard handler
this._handler = new KeyEventDecoder(kbdUtil.ModifierSync(),
VerifyCharModifier( /* jshint newcap: false */
TrackKeyState(
EscapeModifiers(this._handleRfbEvent.bind(this))
)
)
); /* jshint newcap: true */
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this)
};
};
Keyboard.prototype = {
// private methods
_handleRfbEvent: function (e) {
if (this._onKeyPress) {
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
this._onKeyPress(e.keysym.keysym, e.type == 'keydown');
}
},
// _handleKeyDown: function (e) {
// Keyboard event handler if (!this._focused) { return true; }
//
function Keyboard(defaults) { if (this._handler.keydown(e)) {
"use strict"; // Suppress bubbling/default actions
Util.stopEvent(e);
return false;
} else {
// Allow the event to bubble and become a keyPress event which
// will have the character code translated
return true;
}
},
var that = {}, // Public API methods _handleKeyPress: function (e) {
conf = {}, // Configuration attributes if (!this._focused) { return true; }
keyDownList = []; // List of depressed keys if (this._handler.keypress(e)) {
// (even if they are happy) // Suppress bubbling/default actions
Util.stopEvent(e);
return false;
} else {
// Allow the event to bubble and become a keyPress event which
// will have the character code translated
return true;
}
},
// Configuration attributes _handleKeyUp: function (e) {
Util.conf_defaults(conf, that, defaults, [ if (!this._focused) { return true; }
['target', 'wo', 'dom', document, 'DOM element that captures keyboard input'],
['focused', 'rw', 'bool', true, 'Capture and send key events'],
['onKeyPress', 'rw', 'func', null, 'Handler for key press/release'] if (this._handler.keyup(e)) {
]); // Suppress bubbling/default actions
Util.stopEvent(e);
return false;
} else {
// Allow the event to bubble and become a keyPress event which
// will have the character code translated
return true;
}
},
_allKeysUp: function () {
Util.Debug(">> Keyboard.allKeysUp");
this._handler.releaseAll();
Util.Debug("<< Keyboard.allKeysUp");
},
// // Public methods
// Private functions
// grab: function () {
//Util.Debug(">> Keyboard.grab");
// From the event keyCode return the keysym value for keys that need var c = this._target;
// to be suppressed otherwise they may trigger unintended browser
// actions Util.addEvent(c, 'keydown', this._eventHandlers.keydown);
function getKeysymSpecial(evt) { Util.addEvent(c, 'keyup', this._eventHandlers.keyup);
var keysym = null; Util.addEvent(c, 'keypress', this._eventHandlers.keypress);
switch ( evt.keyCode ) { // Release (key up) if window loses focus
// These generate a keyDown and keyPress in Firefox and Opera Util.addEvent(window, 'blur', this._eventHandlers.blur);
case 8 : keysym = 0xFF08; break; // BACKSPACE
case 13 : keysym = 0xFF0D; break; // ENTER //Util.Debug("<< Keyboard.grab");
},
// This generates a keyDown and keyPress in Opera
case 9 : keysym = 0xFF09; break; // TAB
default : break;
}
if (evt.type === 'keydown') {
switch ( evt.keyCode ) {
case 27 : keysym = 0xFF1B; break; // ESCAPE
case 46 : keysym = 0xFFFF; break; // DELETE
case 36 : keysym = 0xFF50; break; // HOME
case 35 : keysym = 0xFF57; break; // END
case 33 : keysym = 0xFF55; break; // PAGE_UP
case 34 : keysym = 0xFF56; break; // PAGE_DOWN
case 45 : keysym = 0xFF63; break; // INSERT
// '-' during keyPress
case 37 : keysym = 0xFF51; break; // LEFT
case 38 : keysym = 0xFF52; break; // UP
case 39 : keysym = 0xFF53; break; // RIGHT
case 40 : keysym = 0xFF54; break; // DOWN
case 16 : keysym = 0xFFE1; break; // SHIFT
case 17 : keysym = 0xFFE3; break; // CONTROL
//case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command)
case 112 : keysym = 0xFFBE; break; // F1
case 113 : keysym = 0xFFBF; break; // F2
case 114 : keysym = 0xFFC0; break; // F3
case 115 : keysym = 0xFFC1; break; // F4
case 116 : keysym = 0xFFC2; break; // F5
case 117 : keysym = 0xFFC3; break; // F6
case 118 : keysym = 0xFFC4; break; // F7
case 119 : keysym = 0xFFC5; break; // F8
case 120 : keysym = 0xFFC6; break; // F9
case 121 : keysym = 0xFFC7; break; // F10
case 122 : keysym = 0xFFC8; break; // F11
case 123 : keysym = 0xFFC9; break; // F12
default : break;
}
}
if ((!keysym) && (evt.ctrlKey || evt.altKey)) {
if ((typeof(evt.which) !== "undefined") && (evt.which > 0)) {
keysym = evt.which;
} else {
// IE9 always
// Firefox and Opera when ctrl/alt + special
Util.Warn("which not set, using keyCode");
keysym = evt.keyCode;
}
/* Remap symbols */ ungrab: function () {
switch (keysym) { //Util.Debug(">> Keyboard.ungrab");
case 186 : keysym = 59; break; // ; (IE) var c = this._target;
case 187 : keysym = 61; break; // = (IE)
case 188 : keysym = 44; break; // , (Mozilla, IE) Util.removeEvent(c, 'keydown', this._eventHandlers.keydown);
case 109 : // - (Mozilla, Opera) Util.removeEvent(c, 'keyup', this._eventHandlers.keyup);
if (Util.Engine.gecko || Util.Engine.presto) { Util.removeEvent(c, 'keypress', this._eventHandlers.keypress);
keysym = 45; } Util.removeEvent(window, 'blur', this._eventHandlers.blur);
break;
case 189 : keysym = 45; break; // - (IE) // Release (key up) all keys that are in a down state
case 190 : keysym = 46; break; // . (Mozilla, IE) this._allKeysUp();
case 191 : keysym = 47; break; // / (Mozilla, IE)
case 192 : keysym = 96; break; // ` (Mozilla, IE) //Util.Debug(">> Keyboard.ungrab");
case 219 : keysym = 91; break; // [ (Mozilla, IE) },
case 220 : keysym = 92; break; // \ (Mozilla, IE)
case 221 : keysym = 93; break; // ] (Mozilla, IE) sync: function (e) {
case 222 : keysym = 39; break; // ' (Mozilla, IE) this._handler.syncModifiers(e);
} }
};
/* Remap shifted and unshifted keys */
if (!!evt.shiftKey) { Util.make_properties(Keyboard, [
switch (keysym) { ['target', 'wo', 'dom'], // DOM element that captures keyboard input
case 48 : keysym = 41 ; break; // ) (shifted 0) ['focused', 'rw', 'bool'], // Capture and send key events
case 49 : keysym = 33 ; break; // ! (shifted 1)
case 50 : keysym = 64 ; break; // @ (shifted 2) ['onKeyPress', 'rw', 'func'] // Handler for key press/release
case 51 : keysym = 35 ; break; // # (shifted 3) ]);
case 52 : keysym = 36 ; break; // $ (shifted 4)
case 53 : keysym = 37 ; break; // % (shifted 5) //
case 54 : keysym = 94 ; break; // ^ (shifted 6) // Mouse event handler
case 55 : keysym = 38 ; break; // & (shifted 7) //
case 56 : keysym = 42 ; break; // * (shifted 8)
case 57 : keysym = 40 ; break; // ( (shifted 9) Mouse = function (defaults) {
this._mouseCaptured = false;
case 59 : keysym = 58 ; break; // : (shifted `)
case 61 : keysym = 43 ; break; // + (shifted ;) this._doubleClickTimer = null;
case 44 : keysym = 60 ; break; // < (shifted ,) this._lastTouchPos = null;
case 45 : keysym = 95 ; break; // _ (shifted -)
case 46 : keysym = 62 ; break; // > (shifted .) // Configuration attributes
case 47 : keysym = 63 ; break; // ? (shifted /) Util.set_defaults(this, defaults, {
case 96 : keysym = 126; break; // ~ (shifted `) 'target': document,
case 91 : keysym = 123; break; // { (shifted [) 'focused': true,
case 92 : keysym = 124; break; // | (shifted \) 'scale': 1.0,
case 93 : keysym = 125; break; // } (shifted ]) 'touchButton': 1
case 39 : keysym = 34 ; break; // " (shifted ') });
this._eventHandlers = {
'mousedown': this._handleMouseDown.bind(this),
'mouseup': this._handleMouseUp.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mousewheel': this._handleMouseWheel.bind(this),
'mousedisable': this._handleMouseDisable.bind(this)
};
};
Mouse.prototype = {
// private methods
_captureMouse: function () {
// capturing the mouse ensures we get the mouseup event
if (this._target.setCapture) {
this._target.setCapture();
} }
} else if ((keysym >= 65) && (keysym <=90)) {
/* Remap unshifted A-Z */ // some browsers give us mouseup events regardless,
keysym += 32; // so if we never captured the mouse, we can disregard the event
} else if (evt.keyLocation === 3) { this._mouseCaptured = true;
// numpad keys },
switch (keysym) {
case 96 : keysym = 48; break; // 0 _releaseMouse: function () {
case 97 : keysym = 49; break; // 1 if (this._target.releaseCapture) {
case 98 : keysym = 50; break; // 2 this._target.releaseCapture();
case 99 : keysym = 51; break; // 3
case 100: keysym = 52; break; // 4
case 101: keysym = 53; break; // 5
case 102: keysym = 54; break; // 6
case 103: keysym = 55; break; // 7
case 104: keysym = 56; break; // 8
case 105: keysym = 57; break; // 9
case 109: keysym = 45; break; // -
case 110: keysym = 46; break; // .
case 111: keysym = 47; break; // /
} }
} this._mouseCaptured = false;
} },
return keysym; _resetDoubleClickTimer: function () {
} this._doubleClickTimer = null;
},
/* Translate DOM keyPress event to keysym value */
function getKeysym(evt) { _handleMouseButton: function (e, down) {
var keysym, msg; if (!this._focused) { return true; }
if (typeof(evt.which) !== "undefined") { if (this._notify) {
// WebKit, Firefox, Opera this._notify(e);
keysym = evt.which; }
} else {
// IE9 var evt = (e ? e : window.event);
Util.Warn("which not set, using keyCode"); var pos = Util.getEventPosition(e, this._target, this._scale);
keysym = evt.keyCode;
} var bmask;
if (e.touches || e.changedTouches) {
if ((keysym > 255) && (keysym < 0xFF00)) { // Touch device
msg = "Mapping character code " + keysym;
// Map Unicode outside Latin 1 to X11 keysyms // When two touches occur within 500 ms of each other and are
keysym = unicodeTable[keysym]; // closer than 20 pixels together a double click is triggered.
if (typeof(keysym) === 'undefined') { if (down == 1) {
keysym = 0; if (this._doubleClickTimer === null) {
} this._lastTouchPos = pos;
Util.Debug(msg + " to " + keysym); } else {
} clearTimeout(this._doubleClickTimer);
return keysym; // When the distance between the two touches is small enough
} // force the position of the latter touch to the position of
// the first.
function show_keyDownList(kind) {
var c; var xs = this._lastTouchPos.x - pos.x;
var msg = "keyDownList (" + kind + "):\n"; var ys = this._lastTouchPos.y - pos.y;
for (c = 0; c < keyDownList.length; c++) { var d = Math.sqrt((xs * xs) + (ys * ys));
msg = msg + " " + c + " - keyCode: " + keyDownList[c].keyCode +
" - which: " + keyDownList[c].which + "\n"; // The goal is to trigger on a certain physical width, the
} // devicePixelRatio brings us a bit closer but is not optimal.
Util.Debug(msg); if (d < 20 * window.devicePixelRatio) {
} pos = this._lastTouchPos;
}
function copyKeyEvent(evt) { }
var members = ['type', 'keyCode', 'charCode', 'which', this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
'altKey', 'ctrlKey', 'shiftKey', }
'keyLocation', 'keyIdentifier'], i, obj = {}; bmask = this._touchButton;
for (i = 0; i < members.length; i++) { // If bmask is set
if (typeof(evt[members[i]]) !== "undefined") { } else if (evt.which) {
obj[members[i]] = evt[members[i]]; /* everything except IE */
} bmask = 1 << evt.button;
}
return obj;
}
function pushKeyEvent(fevt) {
keyDownList.push(fevt);
}
function getKeyEvent(keyCode, pop) {
var i, fevt = null;
for (i = keyDownList.length-1; i >= 0; i--) {
if (keyDownList[i].keyCode === keyCode) {
if ((typeof(pop) !== "undefined") && (pop)) {
fevt = keyDownList.splice(i, 1)[0];
} else { } else {
fevt = keyDownList[i]; /* IE including 9 */
bmask = (evt.button & 0x1) + // Left
(evt.button & 0x2) * 2 + // Right
(evt.button & 0x4) / 2; // Middle
} }
break;
}
}
return fevt;
}
function ignoreKeyEvent(evt) {
// Blarg. Some keys have a different keyCode on keyDown vs keyUp
if (evt.keyCode === 229) {
// French AZERTY keyboard dead key.
// Lame thing is that the respective keyUp is 219 so we can't
// properly ignore the keyUp event
return true;
}
return false;
}
//
// Key Event Handling:
//
// There are several challenges when dealing with key events:
// - The meaning and use of keyCode, charCode and which depends on
// both the browser and the event type (keyDown/Up vs keyPress).
// - We cannot automatically determine the keyboard layout
// - The keyDown and keyUp events have a keyCode value that has not
// been translated by modifier keys.
// - The keyPress event has a translated (for layout and modifiers)
// character code but the attribute containing it differs. keyCode
// contains the translated value in WebKit (Chrome/Safari), Opera
// 11 and IE9. charCode contains the value in WebKit and Firefox.
// The which attribute contains the value on WebKit, Firefox and
// Opera 11.
// - The keyDown/Up keyCode value indicates (sort of) the physical
// key was pressed but only for standard US layout. On a US
// keyboard, the '-' and '_' characters are on the same key and
// generate a keyCode value of 189. But on an AZERTY keyboard even
// though they are different physical keys they both still
// generate a keyCode of 189!
// - To prevent a key event from propagating to the browser and
// causing unwanted default actions (such as closing a tab,
// opening a menu, shifting focus, etc) we must suppress this
// event in both keyDown and keyPress because not all key strokes
// generate on a keyPress event. Also, in WebKit and IE9
// suppressing the keyDown prevents a keyPress but other browsers
// still generated a keyPress even if keyDown is suppressed.
//
// For safe key events, we wait until the keyPress event before
// reporting a key down event. For unsafe key events, we report a key
// down event when the keyDown event fires and we suppress any further
// actions (including keyPress).
//
// In order to report a key up event that matches what we reported
// for the key down event, we keep a list of keys that are currently
// down. When the keyDown event happens, we add the key event to the
// list. If it is a safe key event, then we update the which attribute
// in the most recent item on the list when we received a keyPress
// event (keyPress should immediately follow keyDown). When we
// received a keyUp event we search for the event on the list with
// a matching keyCode and we report the character code using the value
// in the 'which' attribute that was stored with that key.
//
function onKeyDown(e) {
if (! conf.focused) {
return true;
}
var fevt = null, evt = (e ? e : window.event),
keysym = null, suppress = false;
//Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
fevt = copyKeyEvent(evt);
keysym = getKeysymSpecial(evt);
// Save keysym decoding for use in keyUp
fevt.keysym = keysym;
if (keysym) {
// If it is a key or key combination that might trigger
// browser behaviors or it has no corresponding keyPress
// event, then send it immediately
if (conf.onKeyPress && !ignoreKeyEvent(evt)) {
Util.Debug("onKeyPress down, keysym: " + keysym +
" (onKeyDown key: " + evt.keyCode +
", which: " + evt.which + ")");
conf.onKeyPress(keysym, 1, evt);
}
suppress = true;
}
if (! ignoreKeyEvent(evt)) {
// Add it to the list of depressed keys
pushKeyEvent(fevt);
//show_keyDownList('down');
}
if (suppress) {
// Suppress bubbling/default actions
Util.stopEvent(e);
return false;
} else {
// Allow the event to bubble and become a keyPress event which
// will have the character code translated
return true;
}
}
function onKeyPress(e) {
if (! conf.focused) {
return true;
}
var evt = (e ? e : window.event),
kdlen = keyDownList.length, keysym = null;
//Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
if (((evt.which !== "undefined") && (evt.which === 0)) ||
(getKeysymSpecial(evt))) {
// Firefox and Opera generate a keyPress event even if keyDown
// is suppressed. But the keys we want to suppress will have
// either:
// - the which attribute set to 0
// - getKeysymSpecial() will identify it
Util.Debug("Ignoring special key in keyPress");
Util.stopEvent(e);
return false;
}
keysym = getKeysym(evt);
// Modify the the which attribute in the depressed keys list so
// that the keyUp event will be able to have the character code
// translation available.
if (kdlen > 0) {
keyDownList[kdlen-1].keysym = keysym;
} else {
Util.Warn("keyDownList empty when keyPress triggered");
}
//show_keyDownList('press');
// Send the translated keysym
if (conf.onKeyPress && (keysym > 0)) {
Util.Debug("onKeyPress down, keysym: " + keysym +
" (onKeyPress key: " + evt.keyCode +
", which: " + evt.which + ")");
conf.onKeyPress(keysym, 1, evt);
}
// Stop keypress events just in case
Util.stopEvent(e);
return false;
}
function onKeyUp(e) {
if (! conf.focused) {
return true;
}
var fevt = null, evt = (e ? e : window.event), keysym;
//Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which);
fevt = getKeyEvent(evt.keyCode, true);
if (fevt) {
keysym = fevt.keysym;
} else {
Util.Warn("Key event (keyCode = " + evt.keyCode +
") not found on keyDownList");
keysym = 0;
}
//show_keyDownList('up');
if (conf.onKeyPress && (keysym > 0)) {
//Util.Debug("keyPress up, keysym: " + keysym +
// " (key: " + evt.keyCode + ", which: " + evt.which + ")");
Util.Debug("onKeyPress up, keysym: " + keysym +
" (onKeyPress key: " + evt.keyCode +
", which: " + evt.which + ")");
conf.onKeyPress(keysym, 0, evt);
}
Util.stopEvent(e);
return false;
}
function allKeysUp() {
Util.Debug(">> Keyboard.allKeysUp");
if (keyDownList.length > 0) {
Util.Info("Releasing pressed/down keys");
}
var i, keysym, fevt = null;
for (i = keyDownList.length-1; i >= 0; i--) {
fevt = keyDownList.splice(i, 1)[0];
keysym = fevt.keysym;
if (conf.onKeyPress && (keysym > 0)) {
Util.Debug("allKeysUp, keysym: " + keysym +
" (keyCode: " + fevt.keyCode +
", which: " + fevt.which + ")");
conf.onKeyPress(keysym, 0, fevt);
}
}
Util.Debug("<< Keyboard.allKeysUp");
return;
}
// if (this._onMouseButton) {
// Public API interface functions Util.Debug("onMouseButton " + (down ? "down" : "up") +
// ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
this._onMouseButton(pos.x, pos.y, down, bmask);
}
Util.stopEvent(e);
return false;
},
_handleMouseDown: function (e) {
this._captureMouse();
this._handleMouseButton(e, 1);
},
that.grab = function() { _handleMouseUp: function (e) {
//Util.Debug(">> Keyboard.grab"); if (!this._mouseCaptured) { return; }
var c = conf.target;
Util.addEvent(c, 'keydown', onKeyDown); this._handleMouseButton(e, 0);
Util.addEvent(c, 'keyup', onKeyUp); this._releaseMouse();
Util.addEvent(c, 'keypress', onKeyPress); },
// Release (key up) if window loses focus _handleMouseWheel: function (e) {
Util.addEvent(window, 'blur', allKeysUp); if (!this._focused) { return true; }
//Util.Debug("<< Keyboard.grab"); if (this._notify) {
}; this._notify(e);
}
that.ungrab = function() { var evt = (e ? e : window.event);
//Util.Debug(">> Keyboard.ungrab"); var pos = Util.getEventPosition(e, this._target, this._scale);
var c = conf.target; var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
var bmask;
if (wheelData > 0) {
bmask = 1 << 3;
} else {
bmask = 1 << 4;
}
Util.removeEvent(c, 'keydown', onKeyDown); if (this._onMouseButton) {
Util.removeEvent(c, 'keyup', onKeyUp); this._onMouseButton(pos.x, pos.y, 1, bmask);
Util.removeEvent(c, 'keypress', onKeyPress); this._onMouseButton(pos.x, pos.y, 0, bmask);
Util.removeEvent(window, 'blur', allKeysUp); }
Util.stopEvent(e);
return false;
},
// Release (key up) all keys that are in a down state _handleMouseMove: function (e) {
allKeysUp(); if (! this._focused) { return true; }
//Util.Debug(">> Keyboard.ungrab"); if (this._notify) {
}; this._notify(e);
}
return that; // Return the public API interface var evt = (e ? e : window.event);
var pos = Util.getEventPosition(e, this._target, this._scale);
if (this._onMouseMove) {
this._onMouseMove(pos.x, pos.y);
}
Util.stopEvent(e);
return false;
},
_handleMouseDisable: function (e) {
if (!this._focused) { return true; }
var evt = (e ? e : window.event);
var pos = Util.getEventPosition(e, this._target, this._scale);
/* Stop propagation if inside canvas area */
if ((pos.realx >= 0) && (pos.realy >= 0) &&
(pos.realx < this._target.offsetWidth) &&
(pos.realy < this._target.offsetHeight)) {
//Util.Debug("mouse event disabled");
Util.stopEvent(e);
return false;
}
} // End of Keyboard() return true;
},
// // Public methods
// Mouse event handler grab: function () {
// var c = this._target;
function Mouse(defaults) { if ('ontouchstart' in document.documentElement) {
"use strict"; Util.addEvent(c, 'touchstart', this._eventHandlers.mousedown);
Util.addEvent(window, 'touchend', this._eventHandlers.mouseup);
Util.addEvent(c, 'touchend', this._eventHandlers.mouseup);
Util.addEvent(c, 'touchmove', this._eventHandlers.mousemove);
} else {
Util.addEvent(c, 'mousedown', this._eventHandlers.mousedown);
Util.addEvent(window, 'mouseup', this._eventHandlers.mouseup);
Util.addEvent(c, 'mouseup', this._eventHandlers.mouseup);
Util.addEvent(c, 'mousemove', this._eventHandlers.mousemove);
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
this._eventHandlers.mousewheel);
}
var that = {}, // Public API methods /* Work around right and middle click browser behaviors */
conf = {}; // Configuration attributes Util.addEvent(document, 'click', this._eventHandlers.mousedisable);
Util.addEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable);
},
// Configuration attributes ungrab: function () {
Util.conf_defaults(conf, that, defaults, [ var c = this._target;
['target', 'ro', 'dom', document, 'DOM element that captures mouse input'],
['focused', 'rw', 'bool', true, 'Capture and send mouse clicks/movement'],
['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'],
['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'], if ('ontouchstart' in document.documentElement) {
['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'], Util.removeEvent(c, 'touchstart', this._eventHandlers.mousedown);
['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)'] Util.removeEvent(window, 'touchend', this._eventHandlers.mouseup);
]); Util.removeEvent(c, 'touchend', this._eventHandlers.mouseup);
Util.removeEvent(c, 'touchmove', this._eventHandlers.mousemove);
} else {
Util.removeEvent(c, 'mousedown', this._eventHandlers.mousedown);
Util.removeEvent(window, 'mouseup', this._eventHandlers.mouseup);
Util.removeEvent(c, 'mouseup', this._eventHandlers.mouseup);
Util.removeEvent(c, 'mousemove', this._eventHandlers.mousemove);
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
this._eventHandlers.mousewheel);
}
/* Work around right and middle click browser behaviors */
Util.removeEvent(document, 'click', this._eventHandlers.mousedisable);
Util.removeEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable);
// }
// Private functions };
//
function onMouseButton(e, down) {
var evt, pos, bmask;
if (! conf.focused) {
return true;
}
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, conf.target, conf.scale);
if (e.touches || e.changedTouches) {
// Touch device
bmask = conf.touchButton;
// If bmask is set
} else if (evt.which) {
/* everything except IE */
bmask = 1 << evt.button;
} else {
/* IE including 9 */
bmask = (evt.button & 0x1) + // Left
(evt.button & 0x2) * 2 + // Right
(evt.button & 0x4) / 2; // Middle
}
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
if (bmask > 0 && conf.onMouseButton) {
Util.Debug("onMouseButton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
conf.onMouseButton(pos.x, pos.y, down, bmask);
}
Util.stopEvent(e);
return false;
}
function onMouseDown(e) {
onMouseButton(e, 1);
}
function onMouseUp(e) {
onMouseButton(e, 0);
}
function onMouseWheel(e) {
var evt, pos, bmask, wheelData;
if (! conf.focused) {
return true;
}
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, conf.target, conf.scale);
wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
if (wheelData > 0) {
bmask = 1 << 3;
} else {
bmask = 1 << 4;
}
//Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
if (conf.onMouseButton) {
conf.onMouseButton(pos.x, pos.y, 1, bmask);
conf.onMouseButton(pos.x, pos.y, 0, bmask);
}
Util.stopEvent(e);
return false;
}
function onMouseMove(e) {
var evt, pos;
if (! conf.focused) {
return true;
}
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, conf.target, conf.scale);
//Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
if (conf.onMouseMove) {
conf.onMouseMove(pos.x, pos.y);
}
Util.stopEvent(e);
return false;
}
function onMouseDisable(e) {
var evt, pos;
if (! conf.focused) {
return true;
}
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, conf.target, conf.scale);
/* Stop propagation if inside canvas area */
if ((pos.x >= 0) && (pos.y >= 0) &&
(pos.x < conf.target.offsetWidth) &&
(pos.y < conf.target.offsetHeight)) {
//Util.Debug("mouse event disabled");
Util.stopEvent(e);
return false;
}
//Util.Debug("mouse event not disabled");
return true;
}
//
// Public API interface functions
//
that.grab = function() {
//Util.Debug(">> Mouse.grab");
var c = conf.target;
if ('ontouchstart' in document.documentElement) {
Util.addEvent(c, 'touchstart', onMouseDown);
Util.addEvent(c, 'touchend', onMouseUp);
Util.addEvent(c, 'touchmove', onMouseMove);
} else {
Util.addEvent(c, 'mousedown', onMouseDown);
Util.addEvent(c, 'mouseup', onMouseUp);
Util.addEvent(c, 'mousemove', onMouseMove);
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
onMouseWheel);
}
/* Work around right and middle click browser behaviors */
Util.addEvent(document, 'click', onMouseDisable);
Util.addEvent(document.body, 'contextmenu', onMouseDisable);
//Util.Debug("<< Mouse.grab");
};
that.ungrab = function() {
//Util.Debug(">> Mouse.ungrab");
var c = conf.target;
if ('ontouchstart' in document.documentElement) {
Util.removeEvent(c, 'touchstart', onMouseDown);
Util.removeEvent(c, 'touchend', onMouseUp);
Util.removeEvent(c, 'touchmove', onMouseMove);
} else {
Util.removeEvent(c, 'mousedown', onMouseDown);
Util.removeEvent(c, 'mouseup', onMouseUp);
Util.removeEvent(c, 'mousemove', onMouseMove);
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
onMouseWheel);
}
/* Work around right and middle click browser behaviors */
Util.removeEvent(document, 'click', onMouseDisable);
Util.removeEvent(document.body, 'contextmenu', onMouseDisable);
//Util.Debug(">> Mouse.ungrab");
};
return that; // Return the public API interface
} // End of Mouse()
Util.make_properties(Mouse, [
['target', 'ro', 'dom'], // DOM element that captures mouse input
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
['scale', 'rw', 'float'], // Viewport scale factor 0.0 - 1.0
/* ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
* Browser keypress to X11 keysym for Unicode characters > U+00FF ['onMouseMove', 'rw', 'func'], // Handler for mouse movement
*/ ['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
unicodeTable = { ]);
0x0104 : 0x01a1, })();
0x02D8 : 0x01a2,
0x0141 : 0x01a3,
0x013D : 0x01a5,
0x015A : 0x01a6,
0x0160 : 0x01a9,
0x015E : 0x01aa,
0x0164 : 0x01ab,
0x0179 : 0x01ac,
0x017D : 0x01ae,
0x017B : 0x01af,
0x0105 : 0x01b1,
0x02DB : 0x01b2,
0x0142 : 0x01b3,
0x013E : 0x01b5,
0x015B : 0x01b6,
0x02C7 : 0x01b7,
0x0161 : 0x01b9,
0x015F : 0x01ba,
0x0165 : 0x01bb,
0x017A : 0x01bc,
0x02DD : 0x01bd,
0x017E : 0x01be,
0x017C : 0x01bf,
0x0154 : 0x01c0,
0x0102 : 0x01c3,
0x0139 : 0x01c5,
0x0106 : 0x01c6,
0x010C : 0x01c8,
0x0118 : 0x01ca,
0x011A : 0x01cc,
0x010E : 0x01cf,
0x0110 : 0x01d0,
0x0143 : 0x01d1,
0x0147 : 0x01d2,
0x0150 : 0x01d5,
0x0158 : 0x01d8,
0x016E : 0x01d9,
0x0170 : 0x01db,
0x0162 : 0x01de,
0x0155 : 0x01e0,
0x0103 : 0x01e3,
0x013A : 0x01e5,
0x0107 : 0x01e6,
0x010D : 0x01e8,
0x0119 : 0x01ea,
0x011B : 0x01ec,
0x010F : 0x01ef,
0x0111 : 0x01f0,
0x0144 : 0x01f1,
0x0148 : 0x01f2,
0x0151 : 0x01f5,
0x0171 : 0x01fb,
0x0159 : 0x01f8,
0x016F : 0x01f9,
0x0163 : 0x01fe,
0x02D9 : 0x01ff,
0x0126 : 0x02a1,
0x0124 : 0x02a6,
0x0130 : 0x02a9,
0x011E : 0x02ab,
0x0134 : 0x02ac,
0x0127 : 0x02b1,
0x0125 : 0x02b6,
0x0131 : 0x02b9,
0x011F : 0x02bb,
0x0135 : 0x02bc,
0x010A : 0x02c5,
0x0108 : 0x02c6,
0x0120 : 0x02d5,
0x011C : 0x02d8,
0x016C : 0x02dd,
0x015C : 0x02de,
0x010B : 0x02e5,
0x0109 : 0x02e6,
0x0121 : 0x02f5,
0x011D : 0x02f8,
0x016D : 0x02fd,
0x015D : 0x02fe,
0x0138 : 0x03a2,
0x0156 : 0x03a3,
0x0128 : 0x03a5,
0x013B : 0x03a6,
0x0112 : 0x03aa,
0x0122 : 0x03ab,
0x0166 : 0x03ac,
0x0157 : 0x03b3,
0x0129 : 0x03b5,
0x013C : 0x03b6,
0x0113 : 0x03ba,
0x0123 : 0x03bb,
0x0167 : 0x03bc,
0x014A : 0x03bd,
0x014B : 0x03bf,
0x0100 : 0x03c0,
0x012E : 0x03c7,
0x0116 : 0x03cc,
0x012A : 0x03cf,
0x0145 : 0x03d1,
0x014C : 0x03d2,
0x0136 : 0x03d3,
0x0172 : 0x03d9,
0x0168 : 0x03dd,
0x016A : 0x03de,
0x0101 : 0x03e0,
0x012F : 0x03e7,
0x0117 : 0x03ec,
0x012B : 0x03ef,
0x0146 : 0x03f1,
0x014D : 0x03f2,
0x0137 : 0x03f3,
0x0173 : 0x03f9,
0x0169 : 0x03fd,
0x016B : 0x03fe,
0x1E02 : 0x1001e02,
0x1E03 : 0x1001e03,
0x1E0A : 0x1001e0a,
0x1E80 : 0x1001e80,
0x1E82 : 0x1001e82,
0x1E0B : 0x1001e0b,
0x1EF2 : 0x1001ef2,
0x1E1E : 0x1001e1e,
0x1E1F : 0x1001e1f,
0x1E40 : 0x1001e40,
0x1E41 : 0x1001e41,
0x1E56 : 0x1001e56,
0x1E81 : 0x1001e81,
0x1E57 : 0x1001e57,
0x1E83 : 0x1001e83,
0x1E60 : 0x1001e60,
0x1EF3 : 0x1001ef3,
0x1E84 : 0x1001e84,
0x1E85 : 0x1001e85,
0x1E61 : 0x1001e61,
0x0174 : 0x1000174,
0x1E6A : 0x1001e6a,
0x0176 : 0x1000176,
0x0175 : 0x1000175,
0x1E6B : 0x1001e6b,
0x0177 : 0x1000177,
0x0152 : 0x13bc,
0x0153 : 0x13bd,
0x0178 : 0x13be,
0x203E : 0x047e,
0x3002 : 0x04a1,
0x300C : 0x04a2,
0x300D : 0x04a3,
0x3001 : 0x04a4,
0x30FB : 0x04a5,
0x30F2 : 0x04a6,
0x30A1 : 0x04a7,
0x30A3 : 0x04a8,
0x30A5 : 0x04a9,
0x30A7 : 0x04aa,
0x30A9 : 0x04ab,
0x30E3 : 0x04ac,
0x30E5 : 0x04ad,
0x30E7 : 0x04ae,
0x30C3 : 0x04af,
0x30FC : 0x04b0,
0x30A2 : 0x04b1,
0x30A4 : 0x04b2,
0x30A6 : 0x04b3,
0x30A8 : 0x04b4,
0x30AA : 0x04b5,
0x30AB : 0x04b6,
0x30AD : 0x04b7,
0x30AF : 0x04b8,
0x30B1 : 0x04b9,
0x30B3 : 0x04ba,
0x30B5 : 0x04bb,
0x30B7 : 0x04bc,
0x30B9 : 0x04bd,
0x30BB : 0x04be,
0x30BD : 0x04bf,
0x30BF : 0x04c0,
0x30C1 : 0x04c1,
0x30C4 : 0x04c2,
0x30C6 : 0x04c3,
0x30C8 : 0x04c4,
0x30CA : 0x04c5,
0x30CB : 0x04c6,
0x30CC : 0x04c7,
0x30CD : 0x04c8,
0x30CE : 0x04c9,
0x30CF : 0x04ca,
0x30D2 : 0x04cb,
0x30D5 : 0x04cc,
0x30D8 : 0x04cd,
0x30DB : 0x04ce,
0x30DE : 0x04cf,
0x30DF : 0x04d0,
0x30E0 : 0x04d1,
0x30E1 : 0x04d2,
0x30E2 : 0x04d3,
0x30E4 : 0x04d4,
0x30E6 : 0x04d5,
0x30E8 : 0x04d6,
0x30E9 : 0x04d7,
0x30EA : 0x04d8,
0x30EB : 0x04d9,
0x30EC : 0x04da,
0x30ED : 0x04db,
0x30EF : 0x04dc,
0x30F3 : 0x04dd,
0x309B : 0x04de,
0x309C : 0x04df,
0x06F0 : 0x10006f0,
0x06F1 : 0x10006f1,
0x06F2 : 0x10006f2,
0x06F3 : 0x10006f3,
0x06F4 : 0x10006f4,
0x06F5 : 0x10006f5,
0x06F6 : 0x10006f6,
0x06F7 : 0x10006f7,
0x06F8 : 0x10006f8,
0x06F9 : 0x10006f9,
0x066A : 0x100066a,
0x0670 : 0x1000670,
0x0679 : 0x1000679,
0x067E : 0x100067e,
0x0686 : 0x1000686,
0x0688 : 0x1000688,
0x0691 : 0x1000691,
0x060C : 0x05ac,
0x06D4 : 0x10006d4,
0x0660 : 0x1000660,
0x0661 : 0x1000661,
0x0662 : 0x1000662,
0x0663 : 0x1000663,
0x0664 : 0x1000664,
0x0665 : 0x1000665,
0x0666 : 0x1000666,
0x0667 : 0x1000667,
0x0668 : 0x1000668,
0x0669 : 0x1000669,
0x061B : 0x05bb,
0x061F : 0x05bf,
0x0621 : 0x05c1,
0x0622 : 0x05c2,
0x0623 : 0x05c3,
0x0624 : 0x05c4,
0x0625 : 0x05c5,
0x0626 : 0x05c6,
0x0627 : 0x05c7,
0x0628 : 0x05c8,
0x0629 : 0x05c9,
0x062A : 0x05ca,
0x062B : 0x05cb,
0x062C : 0x05cc,
0x062D : 0x05cd,
0x062E : 0x05ce,
0x062F : 0x05cf,
0x0630 : 0x05d0,
0x0631 : 0x05d1,
0x0632 : 0x05d2,
0x0633 : 0x05d3,
0x0634 : 0x05d4,
0x0635 : 0x05d5,
0x0636 : 0x05d6,
0x0637 : 0x05d7,
0x0638 : 0x05d8,
0x0639 : 0x05d9,
0x063A : 0x05da,
0x0640 : 0x05e0,
0x0641 : 0x05e1,
0x0642 : 0x05e2,
0x0643 : 0x05e3,
0x0644 : 0x05e4,
0x0645 : 0x05e5,
0x0646 : 0x05e6,
0x0647 : 0x05e7,
0x0648 : 0x05e8,
0x0649 : 0x05e9,
0x064A : 0x05ea,
0x064B : 0x05eb,
0x064C : 0x05ec,
0x064D : 0x05ed,
0x064E : 0x05ee,
0x064F : 0x05ef,
0x0650 : 0x05f0,
0x0651 : 0x05f1,
0x0652 : 0x05f2,
0x0653 : 0x1000653,
0x0654 : 0x1000654,
0x0655 : 0x1000655,
0x0698 : 0x1000698,
0x06A4 : 0x10006a4,
0x06A9 : 0x10006a9,
0x06AF : 0x10006af,
0x06BA : 0x10006ba,
0x06BE : 0x10006be,
0x06CC : 0x10006cc,
0x06D2 : 0x10006d2,
0x06C1 : 0x10006c1,
0x0492 : 0x1000492,
0x0493 : 0x1000493,
0x0496 : 0x1000496,
0x0497 : 0x1000497,
0x049A : 0x100049a,
0x049B : 0x100049b,
0x049C : 0x100049c,
0x049D : 0x100049d,
0x04A2 : 0x10004a2,
0x04A3 : 0x10004a3,
0x04AE : 0x10004ae,
0x04AF : 0x10004af,
0x04B0 : 0x10004b0,
0x04B1 : 0x10004b1,
0x04B2 : 0x10004b2,
0x04B3 : 0x10004b3,
0x04B6 : 0x10004b6,
0x04B7 : 0x10004b7,
0x04B8 : 0x10004b8,
0x04B9 : 0x10004b9,
0x04BA : 0x10004ba,
0x04BB : 0x10004bb,
0x04D8 : 0x10004d8,
0x04D9 : 0x10004d9,
0x04E2 : 0x10004e2,
0x04E3 : 0x10004e3,
0x04E8 : 0x10004e8,
0x04E9 : 0x10004e9,
0x04EE : 0x10004ee,
0x04EF : 0x10004ef,
0x0452 : 0x06a1,
0x0453 : 0x06a2,
0x0451 : 0x06a3,
0x0454 : 0x06a4,
0x0455 : 0x06a5,
0x0456 : 0x06a6,
0x0457 : 0x06a7,
0x0458 : 0x06a8,
0x0459 : 0x06a9,
0x045A : 0x06aa,
0x045B : 0x06ab,
0x045C : 0x06ac,
0x0491 : 0x06ad,
0x045E : 0x06ae,
0x045F : 0x06af,
0x2116 : 0x06b0,
0x0402 : 0x06b1,
0x0403 : 0x06b2,
0x0401 : 0x06b3,
0x0404 : 0x06b4,
0x0405 : 0x06b5,
0x0406 : 0x06b6,
0x0407 : 0x06b7,
0x0408 : 0x06b8,
0x0409 : 0x06b9,
0x040A : 0x06ba,
0x040B : 0x06bb,
0x040C : 0x06bc,
0x0490 : 0x06bd,
0x040E : 0x06be,
0x040F : 0x06bf,
0x044E : 0x06c0,
0x0430 : 0x06c1,
0x0431 : 0x06c2,
0x0446 : 0x06c3,
0x0434 : 0x06c4,
0x0435 : 0x06c5,
0x0444 : 0x06c6,
0x0433 : 0x06c7,
0x0445 : 0x06c8,
0x0438 : 0x06c9,
0x0439 : 0x06ca,
0x043A : 0x06cb,
0x043B : 0x06cc,
0x043C : 0x06cd,
0x043D : 0x06ce,
0x043E : 0x06cf,
0x043F : 0x06d0,
0x044F : 0x06d1,
0x0440 : 0x06d2,
0x0441 : 0x06d3,
0x0442 : 0x06d4,
0x0443 : 0x06d5,
0x0436 : 0x06d6,
0x0432 : 0x06d7,
0x044C : 0x06d8,
0x044B : 0x06d9,
0x0437 : 0x06da,
0x0448 : 0x06db,
0x044D : 0x06dc,
0x0449 : 0x06dd,
0x0447 : 0x06de,
0x044A : 0x06df,
0x042E : 0x06e0,
0x0410 : 0x06e1,
0x0411 : 0x06e2,
0x0426 : 0x06e3,
0x0414 : 0x06e4,
0x0415 : 0x06e5,
0x0424 : 0x06e6,
0x0413 : 0x06e7,
0x0425 : 0x06e8,
0x0418 : 0x06e9,
0x0419 : 0x06ea,
0x041A : 0x06eb,
0x041B : 0x06ec,
0x041C : 0x06ed,
0x041D : 0x06ee,
0x041E : 0x06ef,
0x041F : 0x06f0,
0x042F : 0x06f1,
0x0420 : 0x06f2,
0x0421 : 0x06f3,
0x0422 : 0x06f4,
0x0423 : 0x06f5,
0x0416 : 0x06f6,
0x0412 : 0x06f7,
0x042C : 0x06f8,
0x042B : 0x06f9,
0x0417 : 0x06fa,
0x0428 : 0x06fb,
0x042D : 0x06fc,
0x0429 : 0x06fd,
0x0427 : 0x06fe,
0x042A : 0x06ff,
0x0386 : 0x07a1,
0x0388 : 0x07a2,
0x0389 : 0x07a3,
0x038A : 0x07a4,
0x03AA : 0x07a5,
0x038C : 0x07a7,
0x038E : 0x07a8,
0x03AB : 0x07a9,
0x038F : 0x07ab,
0x0385 : 0x07ae,
0x2015 : 0x07af,
0x03AC : 0x07b1,
0x03AD : 0x07b2,
0x03AE : 0x07b3,
0x03AF : 0x07b4,
0x03CA : 0x07b5,
0x0390 : 0x07b6,
0x03CC : 0x07b7,
0x03CD : 0x07b8,
0x03CB : 0x07b9,
0x03B0 : 0x07ba,
0x03CE : 0x07bb,
0x0391 : 0x07c1,
0x0392 : 0x07c2,
0x0393 : 0x07c3,
0x0394 : 0x07c4,
0x0395 : 0x07c5,
0x0396 : 0x07c6,
0x0397 : 0x07c7,
0x0398 : 0x07c8,
0x0399 : 0x07c9,
0x039A : 0x07ca,
0x039B : 0x07cb,
0x039C : 0x07cc,
0x039D : 0x07cd,
0x039E : 0x07ce,
0x039F : 0x07cf,
0x03A0 : 0x07d0,
0x03A1 : 0x07d1,
0x03A3 : 0x07d2,
0x03A4 : 0x07d4,
0x03A5 : 0x07d5,
0x03A6 : 0x07d6,
0x03A7 : 0x07d7,
0x03A8 : 0x07d8,
0x03A9 : 0x07d9,
0x03B1 : 0x07e1,
0x03B2 : 0x07e2,
0x03B3 : 0x07e3,
0x03B4 : 0x07e4,
0x03B5 : 0x07e5,
0x03B6 : 0x07e6,
0x03B7 : 0x07e7,
0x03B8 : 0x07e8,
0x03B9 : 0x07e9,
0x03BA : 0x07ea,
0x03BB : 0x07eb,
0x03BC : 0x07ec,
0x03BD : 0x07ed,
0x03BE : 0x07ee,
0x03BF : 0x07ef,
0x03C0 : 0x07f0,
0x03C1 : 0x07f1,
0x03C3 : 0x07f2,
0x03C2 : 0x07f3,
0x03C4 : 0x07f4,
0x03C5 : 0x07f5,
0x03C6 : 0x07f6,
0x03C7 : 0x07f7,
0x03C8 : 0x07f8,
0x03C9 : 0x07f9,
0x23B7 : 0x08a1,
0x2320 : 0x08a4,
0x2321 : 0x08a5,
0x23A1 : 0x08a7,
0x23A3 : 0x08a8,
0x23A4 : 0x08a9,
0x23A6 : 0x08aa,
0x239B : 0x08ab,
0x239D : 0x08ac,
0x239E : 0x08ad,
0x23A0 : 0x08ae,
0x23A8 : 0x08af,
0x23AC : 0x08b0,
0x2264 : 0x08bc,
0x2260 : 0x08bd,
0x2265 : 0x08be,
0x222B : 0x08bf,
0x2234 : 0x08c0,
0x221D : 0x08c1,
0x221E : 0x08c2,
0x2207 : 0x08c5,
0x223C : 0x08c8,
0x2243 : 0x08c9,
0x21D4 : 0x08cd,
0x21D2 : 0x08ce,
0x2261 : 0x08cf,
//0x221A : 0x08d6,
0x2282 : 0x08da,
0x2283 : 0x08db,
0x2229 : 0x08dc,
0x222A : 0x08dd,
0x2227 : 0x08de,
0x2228 : 0x08df,
//0x2202 : 0x08ef,
0x0192 : 0x08f6,
0x2190 : 0x08fb,
0x2191 : 0x08fc,
0x2192 : 0x08fd,
0x2193 : 0x08fe,
0x25C6 : 0x09e0,
0x2592 : 0x09e1,
0x2409 : 0x09e2,
0x240C : 0x09e3,
0x240D : 0x09e4,
0x240A : 0x09e5,
0x2424 : 0x09e8,
0x240B : 0x09e9,
0x2518 : 0x09ea,
0x2510 : 0x09eb,
0x250C : 0x09ec,
0x2514 : 0x09ed,
0x253C : 0x09ee,
0x23BA : 0x09ef,
0x23BB : 0x09f0,
0x2500 : 0x09f1,
0x23BC : 0x09f2,
0x23BD : 0x09f3,
0x251C : 0x09f4,
0x2524 : 0x09f5,
0x2534 : 0x09f6,
0x252C : 0x09f7,
0x2502 : 0x09f8,
0x2003 : 0x0aa1,
0x2002 : 0x0aa2,
0x2004 : 0x0aa3,
0x2005 : 0x0aa4,
0x2007 : 0x0aa5,
0x2008 : 0x0aa6,
0x2009 : 0x0aa7,
0x200A : 0x0aa8,
0x2014 : 0x0aa9,
0x2013 : 0x0aaa,
0x2026 : 0x0aae,
0x2025 : 0x0aaf,
0x2153 : 0x0ab0,
0x2154 : 0x0ab1,
0x2155 : 0x0ab2,
0x2156 : 0x0ab3,
0x2157 : 0x0ab4,
0x2158 : 0x0ab5,
0x2159 : 0x0ab6,
0x215A : 0x0ab7,
0x2105 : 0x0ab8,
0x2012 : 0x0abb,
0x215B : 0x0ac3,
0x215C : 0x0ac4,
0x215D : 0x0ac5,
0x215E : 0x0ac6,
0x2122 : 0x0ac9,
0x2018 : 0x0ad0,
0x2019 : 0x0ad1,
0x201C : 0x0ad2,
0x201D : 0x0ad3,
0x211E : 0x0ad4,
0x2032 : 0x0ad6,
0x2033 : 0x0ad7,
0x271D : 0x0ad9,
0x2663 : 0x0aec,
0x2666 : 0x0aed,
0x2665 : 0x0aee,
0x2720 : 0x0af0,
0x2020 : 0x0af1,
0x2021 : 0x0af2,
0x2713 : 0x0af3,
0x2717 : 0x0af4,
0x266F : 0x0af5,
0x266D : 0x0af6,
0x2642 : 0x0af7,
0x2640 : 0x0af8,
0x260E : 0x0af9,
0x2315 : 0x0afa,
0x2117 : 0x0afb,
0x2038 : 0x0afc,
0x201A : 0x0afd,
0x201E : 0x0afe,
0x22A4 : 0x0bc2,
0x230A : 0x0bc4,
0x2218 : 0x0bca,
0x2395 : 0x0bcc,
0x22A5 : 0x0bce,
0x25CB : 0x0bcf,
0x2308 : 0x0bd3,
0x22A3 : 0x0bdc,
0x22A2 : 0x0bfc,
0x2017 : 0x0cdf,
0x05D0 : 0x0ce0,
0x05D1 : 0x0ce1,
0x05D2 : 0x0ce2,
0x05D3 : 0x0ce3,
0x05D4 : 0x0ce4,
0x05D5 : 0x0ce5,
0x05D6 : 0x0ce6,
0x05D7 : 0x0ce7,
0x05D8 : 0x0ce8,
0x05D9 : 0x0ce9,
0x05DA : 0x0cea,
0x05DB : 0x0ceb,
0x05DC : 0x0cec,
0x05DD : 0x0ced,
0x05DE : 0x0cee,
0x05DF : 0x0cef,
0x05E0 : 0x0cf0,
0x05E1 : 0x0cf1,
0x05E2 : 0x0cf2,
0x05E3 : 0x0cf3,
0x05E4 : 0x0cf4,
0x05E5 : 0x0cf5,
0x05E6 : 0x0cf6,
0x05E7 : 0x0cf7,
0x05E8 : 0x0cf8,
0x05E9 : 0x0cf9,
0x05EA : 0x0cfa,
0x0E01 : 0x0da1,
0x0E02 : 0x0da2,
0x0E03 : 0x0da3,
0x0E04 : 0x0da4,
0x0E05 : 0x0da5,
0x0E06 : 0x0da6,
0x0E07 : 0x0da7,
0x0E08 : 0x0da8,
0x0E09 : 0x0da9,
0x0E0A : 0x0daa,
0x0E0B : 0x0dab,
0x0E0C : 0x0dac,
0x0E0D : 0x0dad,
0x0E0E : 0x0dae,
0x0E0F : 0x0daf,
0x0E10 : 0x0db0,
0x0E11 : 0x0db1,
0x0E12 : 0x0db2,
0x0E13 : 0x0db3,
0x0E14 : 0x0db4,
0x0E15 : 0x0db5,
0x0E16 : 0x0db6,
0x0E17 : 0x0db7,
0x0E18 : 0x0db8,
0x0E19 : 0x0db9,
0x0E1A : 0x0dba,
0x0E1B : 0x0dbb,
0x0E1C : 0x0dbc,
0x0E1D : 0x0dbd,
0x0E1E : 0x0dbe,
0x0E1F : 0x0dbf,
0x0E20 : 0x0dc0,
0x0E21 : 0x0dc1,
0x0E22 : 0x0dc2,
0x0E23 : 0x0dc3,
0x0E24 : 0x0dc4,
0x0E25 : 0x0dc5,
0x0E26 : 0x0dc6,
0x0E27 : 0x0dc7,
0x0E28 : 0x0dc8,
0x0E29 : 0x0dc9,
0x0E2A : 0x0dca,
0x0E2B : 0x0dcb,
0x0E2C : 0x0dcc,
0x0E2D : 0x0dcd,
0x0E2E : 0x0dce,
0x0E2F : 0x0dcf,
0x0E30 : 0x0dd0,
0x0E31 : 0x0dd1,
0x0E32 : 0x0dd2,
0x0E33 : 0x0dd3,
0x0E34 : 0x0dd4,
0x0E35 : 0x0dd5,
0x0E36 : 0x0dd6,
0x0E37 : 0x0dd7,
0x0E38 : 0x0dd8,
0x0E39 : 0x0dd9,
0x0E3A : 0x0dda,
0x0E3F : 0x0ddf,
0x0E40 : 0x0de0,
0x0E41 : 0x0de1,
0x0E42 : 0x0de2,
0x0E43 : 0x0de3,
0x0E44 : 0x0de4,
0x0E45 : 0x0de5,
0x0E46 : 0x0de6,
0x0E47 : 0x0de7,
0x0E48 : 0x0de8,
0x0E49 : 0x0de9,
0x0E4A : 0x0dea,
0x0E4B : 0x0deb,
0x0E4C : 0x0dec,
0x0E4D : 0x0ded,
0x0E50 : 0x0df0,
0x0E51 : 0x0df1,
0x0E52 : 0x0df2,
0x0E53 : 0x0df3,
0x0E54 : 0x0df4,
0x0E55 : 0x0df5,
0x0E56 : 0x0df6,
0x0E57 : 0x0df7,
0x0E58 : 0x0df8,
0x0E59 : 0x0df9,
0x0587 : 0x1000587,
0x0589 : 0x1000589,
0x055D : 0x100055d,
0x058A : 0x100058a,
0x055C : 0x100055c,
0x055B : 0x100055b,
0x055E : 0x100055e,
0x0531 : 0x1000531,
0x0561 : 0x1000561,
0x0532 : 0x1000532,
0x0562 : 0x1000562,
0x0533 : 0x1000533,
0x0563 : 0x1000563,
0x0534 : 0x1000534,
0x0564 : 0x1000564,
0x0535 : 0x1000535,
0x0565 : 0x1000565,
0x0536 : 0x1000536,
0x0566 : 0x1000566,
0x0537 : 0x1000537,
0x0567 : 0x1000567,
0x0538 : 0x1000538,
0x0568 : 0x1000568,
0x0539 : 0x1000539,
0x0569 : 0x1000569,
0x053A : 0x100053a,
0x056A : 0x100056a,
0x053B : 0x100053b,
0x056B : 0x100056b,
0x053C : 0x100053c,
0x056C : 0x100056c,
0x053D : 0x100053d,
0x056D : 0x100056d,
0x053E : 0x100053e,
0x056E : 0x100056e,
0x053F : 0x100053f,
0x056F : 0x100056f,
0x0540 : 0x1000540,
0x0570 : 0x1000570,
0x0541 : 0x1000541,
0x0571 : 0x1000571,
0x0542 : 0x1000542,
0x0572 : 0x1000572,
0x0543 : 0x1000543,
0x0573 : 0x1000573,
0x0544 : 0x1000544,
0x0574 : 0x1000574,
0x0545 : 0x1000545,
0x0575 : 0x1000575,
0x0546 : 0x1000546,
0x0576 : 0x1000576,
0x0547 : 0x1000547,
0x0577 : 0x1000577,
0x0548 : 0x1000548,
0x0578 : 0x1000578,
0x0549 : 0x1000549,
0x0579 : 0x1000579,
0x054A : 0x100054a,
0x057A : 0x100057a,
0x054B : 0x100054b,
0x057B : 0x100057b,
0x054C : 0x100054c,
0x057C : 0x100057c,
0x054D : 0x100054d,
0x057D : 0x100057d,
0x054E : 0x100054e,
0x057E : 0x100057e,
0x054F : 0x100054f,
0x057F : 0x100057f,
0x0550 : 0x1000550,
0x0580 : 0x1000580,
0x0551 : 0x1000551,
0x0581 : 0x1000581,
0x0552 : 0x1000552,
0x0582 : 0x1000582,
0x0553 : 0x1000553,
0x0583 : 0x1000583,
0x0554 : 0x1000554,
0x0584 : 0x1000584,
0x0555 : 0x1000555,
0x0585 : 0x1000585,
0x0556 : 0x1000556,
0x0586 : 0x1000586,
0x055A : 0x100055a,
0x10D0 : 0x10010d0,
0x10D1 : 0x10010d1,
0x10D2 : 0x10010d2,
0x10D3 : 0x10010d3,
0x10D4 : 0x10010d4,
0x10D5 : 0x10010d5,
0x10D6 : 0x10010d6,
0x10D7 : 0x10010d7,
0x10D8 : 0x10010d8,
0x10D9 : 0x10010d9,
0x10DA : 0x10010da,
0x10DB : 0x10010db,
0x10DC : 0x10010dc,
0x10DD : 0x10010dd,
0x10DE : 0x10010de,
0x10DF : 0x10010df,
0x10E0 : 0x10010e0,
0x10E1 : 0x10010e1,
0x10E2 : 0x10010e2,
0x10E3 : 0x10010e3,
0x10E4 : 0x10010e4,
0x10E5 : 0x10010e5,
0x10E6 : 0x10010e6,
0x10E7 : 0x10010e7,
0x10E8 : 0x10010e8,
0x10E9 : 0x10010e9,
0x10EA : 0x10010ea,
0x10EB : 0x10010eb,
0x10EC : 0x10010ec,
0x10ED : 0x10010ed,
0x10EE : 0x10010ee,
0x10EF : 0x10010ef,
0x10F0 : 0x10010f0,
0x10F1 : 0x10010f1,
0x10F2 : 0x10010f2,
0x10F3 : 0x10010f3,
0x10F4 : 0x10010f4,
0x10F5 : 0x10010f5,
0x10F6 : 0x10010f6,
0x1E8A : 0x1001e8a,
0x012C : 0x100012c,
0x01B5 : 0x10001b5,
0x01E6 : 0x10001e6,
0x01D2 : 0x10001d1,
0x019F : 0x100019f,
0x1E8B : 0x1001e8b,
0x012D : 0x100012d,
0x01B6 : 0x10001b6,
0x01E7 : 0x10001e7,
//0x01D2 : 0x10001d2,
0x0275 : 0x1000275,
0x018F : 0x100018f,
0x0259 : 0x1000259,
0x1E36 : 0x1001e36,
0x1E37 : 0x1001e37,
0x1EA0 : 0x1001ea0,
0x1EA1 : 0x1001ea1,
0x1EA2 : 0x1001ea2,
0x1EA3 : 0x1001ea3,
0x1EA4 : 0x1001ea4,
0x1EA5 : 0x1001ea5,
0x1EA6 : 0x1001ea6,
0x1EA7 : 0x1001ea7,
0x1EA8 : 0x1001ea8,
0x1EA9 : 0x1001ea9,
0x1EAA : 0x1001eaa,
0x1EAB : 0x1001eab,
0x1EAC : 0x1001eac,
0x1EAD : 0x1001ead,
0x1EAE : 0x1001eae,
0x1EAF : 0x1001eaf,
0x1EB0 : 0x1001eb0,
0x1EB1 : 0x1001eb1,
0x1EB2 : 0x1001eb2,
0x1EB3 : 0x1001eb3,
0x1EB4 : 0x1001eb4,
0x1EB5 : 0x1001eb5,
0x1EB6 : 0x1001eb6,
0x1EB7 : 0x1001eb7,
0x1EB8 : 0x1001eb8,
0x1EB9 : 0x1001eb9,
0x1EBA : 0x1001eba,
0x1EBB : 0x1001ebb,
0x1EBC : 0x1001ebc,
0x1EBD : 0x1001ebd,
0x1EBE : 0x1001ebe,
0x1EBF : 0x1001ebf,
0x1EC0 : 0x1001ec0,
0x1EC1 : 0x1001ec1,
0x1EC2 : 0x1001ec2,
0x1EC3 : 0x1001ec3,
0x1EC4 : 0x1001ec4,
0x1EC5 : 0x1001ec5,
0x1EC6 : 0x1001ec6,
0x1EC7 : 0x1001ec7,
0x1EC8 : 0x1001ec8,
0x1EC9 : 0x1001ec9,
0x1ECA : 0x1001eca,
0x1ECB : 0x1001ecb,
0x1ECC : 0x1001ecc,
0x1ECD : 0x1001ecd,
0x1ECE : 0x1001ece,
0x1ECF : 0x1001ecf,
0x1ED0 : 0x1001ed0,
0x1ED1 : 0x1001ed1,
0x1ED2 : 0x1001ed2,
0x1ED3 : 0x1001ed3,
0x1ED4 : 0x1001ed4,
0x1ED5 : 0x1001ed5,
0x1ED6 : 0x1001ed6,
0x1ED7 : 0x1001ed7,
0x1ED8 : 0x1001ed8,
0x1ED9 : 0x1001ed9,
0x1EDA : 0x1001eda,
0x1EDB : 0x1001edb,
0x1EDC : 0x1001edc,
0x1EDD : 0x1001edd,
0x1EDE : 0x1001ede,
0x1EDF : 0x1001edf,
0x1EE0 : 0x1001ee0,
0x1EE1 : 0x1001ee1,
0x1EE2 : 0x1001ee2,
0x1EE3 : 0x1001ee3,
0x1EE4 : 0x1001ee4,
0x1EE5 : 0x1001ee5,
0x1EE6 : 0x1001ee6,
0x1EE7 : 0x1001ee7,
0x1EE8 : 0x1001ee8,
0x1EE9 : 0x1001ee9,
0x1EEA : 0x1001eea,
0x1EEB : 0x1001eeb,
0x1EEC : 0x1001eec,
0x1EED : 0x1001eed,
0x1EEE : 0x1001eee,
0x1EEF : 0x1001eef,
0x1EF0 : 0x1001ef0,
0x1EF1 : 0x1001ef1,
0x1EF4 : 0x1001ef4,
0x1EF5 : 0x1001ef5,
0x1EF6 : 0x1001ef6,
0x1EF7 : 0x1001ef7,
0x1EF8 : 0x1001ef8,
0x1EF9 : 0x1001ef9,
0x01A0 : 0x10001a0,
0x01A1 : 0x10001a1,
0x01AF : 0x10001af,
0x01B0 : 0x10001b0,
0x20A0 : 0x10020a0,
0x20A1 : 0x10020a1,
0x20A2 : 0x10020a2,
0x20A3 : 0x10020a3,
0x20A4 : 0x10020a4,
0x20A5 : 0x10020a5,
0x20A6 : 0x10020a6,
0x20A7 : 0x10020a7,
0x20A8 : 0x10020a8,
0x20A9 : 0x10020a9,
0x20AA : 0x10020aa,
0x20AB : 0x10020ab,
0x20AC : 0x20ac,
0x2070 : 0x1002070,
0x2074 : 0x1002074,
0x2075 : 0x1002075,
0x2076 : 0x1002076,
0x2077 : 0x1002077,
0x2078 : 0x1002078,
0x2079 : 0x1002079,
0x2080 : 0x1002080,
0x2081 : 0x1002081,
0x2082 : 0x1002082,
0x2083 : 0x1002083,
0x2084 : 0x1002084,
0x2085 : 0x1002085,
0x2086 : 0x1002086,
0x2087 : 0x1002087,
0x2088 : 0x1002088,
0x2089 : 0x1002089,
0x2202 : 0x1002202,
0x2205 : 0x1002205,
0x2208 : 0x1002208,
0x2209 : 0x1002209,
0x220B : 0x100220B,
0x221A : 0x100221A,
0x221B : 0x100221B,
0x221C : 0x100221C,
0x222C : 0x100222C,
0x222D : 0x100222D,
0x2235 : 0x1002235,
0x2245 : 0x1002248,
0x2247 : 0x1002247,
0x2262 : 0x1002262,
0x2263 : 0x1002263,
0x2800 : 0x1002800,
0x2801 : 0x1002801,
0x2802 : 0x1002802,
0x2803 : 0x1002803,
0x2804 : 0x1002804,
0x2805 : 0x1002805,
0x2806 : 0x1002806,
0x2807 : 0x1002807,
0x2808 : 0x1002808,
0x2809 : 0x1002809,
0x280a : 0x100280a,
0x280b : 0x100280b,
0x280c : 0x100280c,
0x280d : 0x100280d,
0x280e : 0x100280e,
0x280f : 0x100280f,
0x2810 : 0x1002810,
0x2811 : 0x1002811,
0x2812 : 0x1002812,
0x2813 : 0x1002813,
0x2814 : 0x1002814,
0x2815 : 0x1002815,
0x2816 : 0x1002816,
0x2817 : 0x1002817,
0x2818 : 0x1002818,
0x2819 : 0x1002819,
0x281a : 0x100281a,
0x281b : 0x100281b,
0x281c : 0x100281c,
0x281d : 0x100281d,
0x281e : 0x100281e,
0x281f : 0x100281f,
0x2820 : 0x1002820,
0x2821 : 0x1002821,
0x2822 : 0x1002822,
0x2823 : 0x1002823,
0x2824 : 0x1002824,
0x2825 : 0x1002825,
0x2826 : 0x1002826,
0x2827 : 0x1002827,
0x2828 : 0x1002828,
0x2829 : 0x1002829,
0x282a : 0x100282a,
0x282b : 0x100282b,
0x282c : 0x100282c,
0x282d : 0x100282d,
0x282e : 0x100282e,
0x282f : 0x100282f,
0x2830 : 0x1002830,
0x2831 : 0x1002831,
0x2832 : 0x1002832,
0x2833 : 0x1002833,
0x2834 : 0x1002834,
0x2835 : 0x1002835,
0x2836 : 0x1002836,
0x2837 : 0x1002837,
0x2838 : 0x1002838,
0x2839 : 0x1002839,
0x283a : 0x100283a,
0x283b : 0x100283b,
0x283c : 0x100283c,
0x283d : 0x100283d,
0x283e : 0x100283e,
0x283f : 0x100283f,
0x2840 : 0x1002840,
0x2841 : 0x1002841,
0x2842 : 0x1002842,
0x2843 : 0x1002843,
0x2844 : 0x1002844,
0x2845 : 0x1002845,
0x2846 : 0x1002846,
0x2847 : 0x1002847,
0x2848 : 0x1002848,
0x2849 : 0x1002849,
0x284a : 0x100284a,
0x284b : 0x100284b,
0x284c : 0x100284c,
0x284d : 0x100284d,
0x284e : 0x100284e,
0x284f : 0x100284f,
0x2850 : 0x1002850,
0x2851 : 0x1002851,
0x2852 : 0x1002852,
0x2853 : 0x1002853,
0x2854 : 0x1002854,
0x2855 : 0x1002855,
0x2856 : 0x1002856,
0x2857 : 0x1002857,
0x2858 : 0x1002858,
0x2859 : 0x1002859,
0x285a : 0x100285a,
0x285b : 0x100285b,
0x285c : 0x100285c,
0x285d : 0x100285d,
0x285e : 0x100285e,
0x285f : 0x100285f,
0x2860 : 0x1002860,
0x2861 : 0x1002861,
0x2862 : 0x1002862,
0x2863 : 0x1002863,
0x2864 : 0x1002864,
0x2865 : 0x1002865,
0x2866 : 0x1002866,
0x2867 : 0x1002867,
0x2868 : 0x1002868,
0x2869 : 0x1002869,
0x286a : 0x100286a,
0x286b : 0x100286b,
0x286c : 0x100286c,
0x286d : 0x100286d,
0x286e : 0x100286e,
0x286f : 0x100286f,
0x2870 : 0x1002870,
0x2871 : 0x1002871,
0x2872 : 0x1002872,
0x2873 : 0x1002873,
0x2874 : 0x1002874,
0x2875 : 0x1002875,
0x2876 : 0x1002876,
0x2877 : 0x1002877,
0x2878 : 0x1002878,
0x2879 : 0x1002879,
0x287a : 0x100287a,
0x287b : 0x100287b,
0x287c : 0x100287c,
0x287d : 0x100287d,
0x287e : 0x100287e,
0x287f : 0x100287f,
0x2880 : 0x1002880,
0x2881 : 0x1002881,
0x2882 : 0x1002882,
0x2883 : 0x1002883,
0x2884 : 0x1002884,
0x2885 : 0x1002885,
0x2886 : 0x1002886,
0x2887 : 0x1002887,
0x2888 : 0x1002888,
0x2889 : 0x1002889,
0x288a : 0x100288a,
0x288b : 0x100288b,
0x288c : 0x100288c,
0x288d : 0x100288d,
0x288e : 0x100288e,
0x288f : 0x100288f,
0x2890 : 0x1002890,
0x2891 : 0x1002891,
0x2892 : 0x1002892,
0x2893 : 0x1002893,
0x2894 : 0x1002894,
0x2895 : 0x1002895,
0x2896 : 0x1002896,
0x2897 : 0x1002897,
0x2898 : 0x1002898,
0x2899 : 0x1002899,
0x289a : 0x100289a,
0x289b : 0x100289b,
0x289c : 0x100289c,
0x289d : 0x100289d,
0x289e : 0x100289e,
0x289f : 0x100289f,
0x28a0 : 0x10028a0,
0x28a1 : 0x10028a1,
0x28a2 : 0x10028a2,
0x28a3 : 0x10028a3,
0x28a4 : 0x10028a4,
0x28a5 : 0x10028a5,
0x28a6 : 0x10028a6,
0x28a7 : 0x10028a7,
0x28a8 : 0x10028a8,
0x28a9 : 0x10028a9,
0x28aa : 0x10028aa,
0x28ab : 0x10028ab,
0x28ac : 0x10028ac,
0x28ad : 0x10028ad,
0x28ae : 0x10028ae,
0x28af : 0x10028af,
0x28b0 : 0x10028b0,
0x28b1 : 0x10028b1,
0x28b2 : 0x10028b2,
0x28b3 : 0x10028b3,
0x28b4 : 0x10028b4,
0x28b5 : 0x10028b5,
0x28b6 : 0x10028b6,
0x28b7 : 0x10028b7,
0x28b8 : 0x10028b8,
0x28b9 : 0x10028b9,
0x28ba : 0x10028ba,
0x28bb : 0x10028bb,
0x28bc : 0x10028bc,
0x28bd : 0x10028bd,
0x28be : 0x10028be,
0x28bf : 0x10028bf,
0x28c0 : 0x10028c0,
0x28c1 : 0x10028c1,
0x28c2 : 0x10028c2,
0x28c3 : 0x10028c3,
0x28c4 : 0x10028c4,
0x28c5 : 0x10028c5,
0x28c6 : 0x10028c6,
0x28c7 : 0x10028c7,
0x28c8 : 0x10028c8,
0x28c9 : 0x10028c9,
0x28ca : 0x10028ca,
0x28cb : 0x10028cb,
0x28cc : 0x10028cc,
0x28cd : 0x10028cd,
0x28ce : 0x10028ce,
0x28cf : 0x10028cf,
0x28d0 : 0x10028d0,
0x28d1 : 0x10028d1,
0x28d2 : 0x10028d2,
0x28d3 : 0x10028d3,
0x28d4 : 0x10028d4,
0x28d5 : 0x10028d5,
0x28d6 : 0x10028d6,
0x28d7 : 0x10028d7,
0x28d8 : 0x10028d8,
0x28d9 : 0x10028d9,
0x28da : 0x10028da,
0x28db : 0x10028db,
0x28dc : 0x10028dc,
0x28dd : 0x10028dd,
0x28de : 0x10028de,
0x28df : 0x10028df,
0x28e0 : 0x10028e0,
0x28e1 : 0x10028e1,
0x28e2 : 0x10028e2,
0x28e3 : 0x10028e3,
0x28e4 : 0x10028e4,
0x28e5 : 0x10028e5,
0x28e6 : 0x10028e6,
0x28e7 : 0x10028e7,
0x28e8 : 0x10028e8,
0x28e9 : 0x10028e9,
0x28ea : 0x10028ea,
0x28eb : 0x10028eb,
0x28ec : 0x10028ec,
0x28ed : 0x10028ed,
0x28ee : 0x10028ee,
0x28ef : 0x10028ef,
0x28f0 : 0x10028f0,
0x28f1 : 0x10028f1,
0x28f2 : 0x10028f2,
0x28f3 : 0x10028f3,
0x28f4 : 0x10028f4,
0x28f5 : 0x10028f5,
0x28f6 : 0x10028f6,
0x28f7 : 0x10028f7,
0x28f8 : 0x10028f8,
0x28f9 : 0x10028f9,
0x28fa : 0x10028fa,
0x28fb : 0x10028fb,
0x28fc : 0x10028fc,
0x28fd : 0x10028fd,
0x28fe : 0x10028fe,
0x28ff : 0x10028ff
};
...@@ -352,20 +352,28 @@ this.getbit = function(d) ...@@ -352,20 +352,28 @@ this.getbit = function(d)
} }
/* read a num bit value from a stream and add base */ /* read a num bit value from a stream and add base */
function read_bits_direct(source, bitcount, tag, idx, num)
{
var val = 0;
while (bitcount < 24) {
tag = tag | (source[idx++] & 0xff) << bitcount;
bitcount += 8;
}
val = tag & (0xffff >> (16 - num));
tag >>= num;
bitcount -= num;
return [bitcount, tag, idx, val];
}
this.read_bits = function(d, num, base) this.read_bits = function(d, num, base)
{ {
if (!num) if (!num)
return base; return base;
var val = 0; var ret = read_bits_direct(d.source, d.bitcount, d.tag, d.sourceIndex, num);
while (d.bitcount < 24) { d.bitcount = ret[0];
d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount; d.tag = ret[1];
d.bitcount += 8; d.sourceIndex = ret[2];
} return ret[3] + base;
val = d.tag & (0xffff >> (16 - num));
d.tag >>= num;
d.bitcount -= num;
return val + base;
} }
/* given a data stream and a tree, decode a symbol */ /* given a data stream and a tree, decode a symbol */
......
var kbdUtil = (function() {
"use strict";
function substituteCodepoint(cp) {
// Any Unicode code points which do not have corresponding keysym entries
// can be swapped out for another code point by adding them to this table
var substitutions = {
// {S,s} with comma below -> {S,s} with cedilla
0x218 : 0x15e,
0x219 : 0x15f,
// {T,t} with comma below -> {T,t} with cedilla
0x21a : 0x162,
0x21b : 0x163
};
var sub = substitutions[cp];
return sub ? sub : cp;
}
function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
}
function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
}
function isLinux() {
return navigator && !!(/linux/i).exec(navigator.platform);
}
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
function hasShortcutModifier(charModifier, currentModifiers) {
var mods = {};
for (var key in currentModifiers) {
if (parseInt(key) !== 0xffe1) {
mods[key] = currentModifiers[key];
}
}
var sum = 0;
for (var k in currentModifiers) {
if (mods[k]) {
++sum;
}
}
if (hasCharModifier(charModifier, mods)) {
return sum > charModifier.length;
}
else {
return sum > 0;
}
}
// Return true if the specified char modifier is currently down
function hasCharModifier(charModifier, currentModifiers) {
if (charModifier.length === 0) { return false; }
for (var i = 0; i < charModifier.length; ++i) {
if (!currentModifiers[charModifier[i]]) {
return false;
}
}
return true;
}
// Helper object tracking modifier key state
// and generates fake key events to compensate if it gets out of sync
function ModifierSync(charModifier) {
var ctrl = 0xffe3;
var alt = 0xffe9;
var altGr = 0xfe03;
var shift = 0xffe1;
var meta = 0xffe7;
if (!charModifier) {
if (isMac()) {
// on Mac, Option (AKA Alt) is used as a char modifier
charModifier = [alt];
}
else if (isWindows()) {
// on Windows, Ctrl+Alt is used as a char modifier
charModifier = [alt, ctrl];
}
else if (isLinux()) {
// on Linux, AltGr is used as a char modifier
charModifier = [altGr];
}
else {
charModifier = [];
}
}
var state = {};
state[ctrl] = false;
state[alt] = false;
state[altGr] = false;
state[shift] = false;
state[meta] = false;
function sync(evt, keysym) {
var result = [];
function syncKey(keysym) {
return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'};
}
if (evt.ctrlKey !== undefined && evt.ctrlKey !== state[ctrl] && keysym !== ctrl) {
state[ctrl] = evt.ctrlKey;
result.push(syncKey(ctrl));
}
if (evt.altKey !== undefined && evt.altKey !== state[alt] && keysym !== alt) {
state[alt] = evt.altKey;
result.push(syncKey(alt));
}
if (evt.altGraphKey !== undefined && evt.altGraphKey !== state[altGr] && keysym !== altGr) {
state[altGr] = evt.altGraphKey;
result.push(syncKey(altGr));
}
if (evt.shiftKey !== undefined && evt.shiftKey !== state[shift] && keysym !== shift) {
state[shift] = evt.shiftKey;
result.push(syncKey(shift));
}
if (evt.metaKey !== undefined && evt.metaKey !== state[meta] && keysym !== meta) {
state[meta] = evt.metaKey;
result.push(syncKey(meta));
}
return result;
}
function syncKeyEvent(evt, down) {
var obj = getKeysym(evt);
var keysym = obj ? obj.keysym : null;
// first, apply the event itself, if relevant
if (keysym !== null && state[keysym] !== undefined) {
state[keysym] = down;
}
return sync(evt, keysym);
}
return {
// sync on the appropriate keyboard event
keydown: function(evt) { return syncKeyEvent(evt, true);},
keyup: function(evt) { return syncKeyEvent(evt, false);},
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
syncAny: function(evt) { return sync(evt);},
// is a shortcut modifier down?
hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
// if a char modifier is down, return the keys it consists of, otherwise return null
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
};
}
// Get a key ID from a keyboard event
// May be a string or an integer depending on the available properties
function getKey(evt){
if ('keyCode' in evt && 'key' in evt) {
return evt.key + ':' + evt.keyCode;
}
else if ('keyCode' in evt) {
return evt.keyCode;
}
else {
return evt.key;
}
}
// Get the most reliable keysym value we can get from a key event
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
function getKeysym(evt){
var codepoint;
if (evt.char && evt.char.length === 1) {
codepoint = evt.char.charCodeAt();
}
else if (evt.charCode) {
codepoint = evt.charCode;
}
else if (evt.keyCode && evt.type === 'keypress') {
// IE10 stores the char code as keyCode, and has no other useful properties
codepoint = evt.keyCode;
}
if (codepoint) {
var res = keysyms.fromUnicode(substituteCodepoint(codepoint));
if (res) {
return res;
}
}
// we could check evt.key here.
// Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list,
// so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key
// so we don't *need* it yet
if (evt.keyCode) {
return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey));
}
if (evt.which) {
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
}
return null;
}
// Given a keycode, try to predict which keysym it might be.
// If the keycode is unknown, null is returned.
function keysymFromKeyCode(keycode, shiftPressed) {
if (typeof(keycode) !== 'number') {
return null;
}
// won't be accurate for azerty
if (keycode >= 0x30 && keycode <= 0x39) {
return keycode; // digit
}
if (keycode >= 0x41 && keycode <= 0x5a) {
// remap to lowercase unless shift is down
return shiftPressed ? keycode : keycode + 32; // A-Z
}
if (keycode >= 0x60 && keycode <= 0x69) {
return 0xffb0 + (keycode - 0x60); // numpad 0-9
}
switch(keycode) {
case 0x20: return 0x20; // space
case 0x6a: return 0xffaa; // multiply
case 0x6b: return 0xffab; // add
case 0x6c: return 0xffac; // separator
case 0x6d: return 0xffad; // subtract
case 0x6e: return 0xffae; // decimal
case 0x6f: return 0xffaf; // divide
case 0xbb: return 0x2b; // +
case 0xbc: return 0x2c; // ,
case 0xbd: return 0x2d; // -
case 0xbe: return 0x2e; // .
}
return nonCharacterKey({keyCode: keycode});
}
// if the key is a known non-character key (any key which doesn't generate character data)
// return its keysym value. Otherwise return null
function nonCharacterKey(evt) {
// evt.key not implemented yet
if (!evt.keyCode) { return null; }
var keycode = evt.keyCode;
if (keycode >= 0x70 && keycode <= 0x87) {
return 0xffbe + keycode - 0x70; // F1-F24
}
switch (keycode) {
case 8 : return 0xFF08; // BACKSPACE
case 13 : return 0xFF0D; // ENTER
case 9 : return 0xFF09; // TAB
case 27 : return 0xFF1B; // ESCAPE
case 46 : return 0xFFFF; // DELETE
case 36 : return 0xFF50; // HOME
case 35 : return 0xFF57; // END
case 33 : return 0xFF55; // PAGE_UP
case 34 : return 0xFF56; // PAGE_DOWN
case 45 : return 0xFF63; // INSERT
case 37 : return 0xFF51; // LEFT
case 38 : return 0xFF52; // UP
case 39 : return 0xFF53; // RIGHT
case 40 : return 0xFF54; // DOWN
case 16 : return 0xFFE1; // SHIFT
case 17 : return 0xFFE3; // CONTROL
case 18 : return 0xFFE9; // Left ALT (Mac Option)
case 224 : return 0xFE07; // Meta
case 225 : return 0xFE03; // AltGr
case 91 : return 0xFFEC; // Super_L (Win Key)
case 92 : return 0xFFED; // Super_R (Win Key)
case 93 : return 0xFF67; // Menu (Win Menu), Mac Command
default: return null;
}
}
return {
hasShortcutModifier : hasShortcutModifier,
hasCharModifier : hasCharModifier,
ModifierSync : ModifierSync,
getKey : getKey,
getKeysym : getKeysym,
keysymFromKeyCode : keysymFromKeyCode,
nonCharacterKey : nonCharacterKey,
substituteCodepoint : substituteCodepoint
};
})();
// Takes a DOM keyboard event and:
// - determines which keysym it represents
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
// This information is collected into an object which is passed to the next() function. (one call per event)
function KeyEventDecoder(modifierState, next) {
"use strict";
function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
next(evts[i]);
}
}
function process(evt, type) {
var result = {type: type};
var keyId = kbdUtil.getKey(evt);
if (keyId) {
result.keyId = keyId;
}
var keysym = kbdUtil.getKeysym(evt);
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
// "special" keys like enter, tab or backspace don't send keypress events,
// and some browsers don't send keypresses at all if a modifier is down
if (keysym && (type !== 'keydown' || kbdUtil.nonCharacterKey(evt) || hasModifier)) {
result.keysym = keysym;
}
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
// Should we prevent the browser from handling the event?
// Doing so on a keydown (in most browsers) prevents keypress from being generated
// so only do that if we have to.
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));
// If a char modifier is down on a keydown, we need to insert a stall,
// so VerifyCharModifier knows to wait and see if a keypress is comnig
var stall = type === 'keydown' && modifierState.activeCharModifier() && !kbdUtil.nonCharacterKey(evt);
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
var active = modifierState.activeCharModifier();
// If we have a char modifier down, and we're able to determine a keysym reliably
// then (a) we know to treat the modifier as a char modifier,
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
if (active && keysym) {
var isCharModifier = false;
for (var i = 0; i < active.length; ++i) {
if (active[i] === keysym.keysym) {
isCharModifier = true;
}
}
if (type === 'keypress' && !isCharModifier) {
result.escape = modifierState.activeCharModifier();
}
}
if (stall) {
// insert a fake "stall" event
next({type: 'stall'});
}
next(result);
return suppress;
}
return {
keydown: function(evt) {
sendAll(modifierState.keydown(evt));
return process(evt, 'keydown');
},
keypress: function(evt) {
return process(evt, 'keypress');
},
keyup: function(evt) {
sendAll(modifierState.keyup(evt));
return process(evt, 'keyup');
},
syncModifiers: function(evt) {
sendAll(modifierState.syncAny(evt));
},
releaseAll: function() { next({type: 'releaseall'}); }
};
}
// Combines keydown and keypress events where necessary to handle char modifiers.
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
function VerifyCharModifier(next) {
"use strict";
var queue = [];
var timer = null;
function process() {
if (timer) {
return;
}
var delayProcess = function () {
clearTimeout(timer);
timer = null;
process();
};
while (queue.length !== 0) {
var cur = queue[0];
queue = queue.splice(1);
switch (cur.type) {
case 'stall':
// insert a delay before processing available events.
/* jshint loopfunc: true */
timer = setTimeout(delayProcess, 5);
/* jshint loopfunc: false */
return;
case 'keydown':
// is the next element a keypress? Then we should merge the two
if (queue.length !== 0 && queue[0].type === 'keypress') {
// Firefox sends keypress even when no char is generated.
// so, if keypress keysym is the same as we'd have guessed from keydown,
// the modifier didn't have any effect, and should not be escaped
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
cur.escape = queue[0].escape;
}
cur.keysym = queue[0].keysym;
queue = queue.splice(1);
}
break;
}
// swallow stall events, and pass all others to the next stage
if (cur.type !== 'stall') {
next(cur);
}
}
}
return function(evt) {
queue.push(evt);
process();
};
}
// Keeps track of which keys we (and the server) believe are down
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
// key repeat events should be merged into a single entry.
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
function TrackKeyState(next) {
"use strict";
var state = [];
return function (evt) {
var last = state.length !== 0 ? state[state.length-1] : null;
switch (evt.type) {
case 'keydown':
// insert a new entry if last seen key was different.
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
last = {keyId: evt.keyId, keysyms: {}};
state.push(last);
}
if (evt.keysym) {
// make sure last event contains this keysym (a single "logical" keyevent
// can cause multiple key events to be sent to the VNC server)
last.keysyms[evt.keysym.keysym] = evt.keysym;
last.ignoreKeyPress = true;
next(evt);
}
break;
case 'keypress':
if (!last) {
last = {keyId: evt.keyId, keysyms: {}};
state.push(last);
}
if (!evt.keysym) {
console.log('keypress with no keysym:', evt);
}
// If we didn't expect a keypress, and already sent a keydown to the VNC server
// based on the keydown, make sure to skip this event.
if (evt.keysym && !last.ignoreKeyPress) {
last.keysyms[evt.keysym.keysym] = evt.keysym;
evt.type = 'keydown';
next(evt);
}
break;
case 'keyup':
if (state.length === 0) {
return;
}
var idx = null;
// do we have a matching key tracked as being down?
for (var i = 0; i !== state.length; ++i) {
if (state[i].keyId === evt.keyId) {
idx = i;
break;
}
}
// if we couldn't find a match (it happens), assume it was the last key pressed
if (idx === null) {
idx = state.length - 1;
}
var item = state.splice(idx, 1)[0];
// for each keysym tracked by this key entry, clone the current event and override the keysym
var clone = (function(){
function Clone(){}
return function (obj) { Clone.prototype=obj; return new Clone(); };
}());
for (var key in item.keysyms) {
var out = clone(evt);
out.keysym = item.keysyms[key];
next(out);
}
break;
case 'releaseall':
/* jshint shadow: true */
for (var i = 0; i < state.length; ++i) {
for (var key in state[i].keysyms) {
var keysym = state[i].keysyms[key];
next({keyId: 0, keysym: keysym, type: 'keyup'});
}
}
/* jshint shadow: false */
state = [];
}
};
}
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
function EscapeModifiers(next) {
"use strict";
return function(evt) {
if (evt.type !== 'keydown' || evt.escape === undefined) {
next(evt);
return;
}
// undo modifiers
for (var i = 0; i < evt.escape.length; ++i) {
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
}
// send the character event
next(evt);
// redo modifiers
/* jshint shadow: true */
for (var i = 0; i < evt.escape.length; ++i) {
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
}
/* jshint shadow: false */
};
}
var XK_VoidSymbol = 0xffffff, /* Void symbol */
XK_BackSpace = 0xff08, /* Back space, back char */
XK_Tab = 0xff09,
XK_Linefeed = 0xff0a, /* Linefeed, LF */
XK_Clear = 0xff0b,
XK_Return = 0xff0d, /* Return, enter */
XK_Pause = 0xff13, /* Pause, hold */
XK_Scroll_Lock = 0xff14,
XK_Sys_Req = 0xff15,
XK_Escape = 0xff1b,
XK_Delete = 0xffff, /* Delete, rubout */
/* Cursor control & motion */
XK_Home = 0xff50,
XK_Left = 0xff51, /* Move left, left arrow */
XK_Up = 0xff52, /* Move up, up arrow */
XK_Right = 0xff53, /* Move right, right arrow */
XK_Down = 0xff54, /* Move down, down arrow */
XK_Prior = 0xff55, /* Prior, previous */
XK_Page_Up = 0xff55,
XK_Next = 0xff56, /* Next */
XK_Page_Down = 0xff56,
XK_End = 0xff57, /* EOL */
XK_Begin = 0xff58, /* BOL */
/* Misc functions */
XK_Select = 0xff60, /* Select, mark */
XK_Print = 0xff61,
XK_Execute = 0xff62, /* Execute, run, do */
XK_Insert = 0xff63, /* Insert, insert here */
XK_Undo = 0xff65,
XK_Redo = 0xff66, /* Redo, again */
XK_Menu = 0xff67,
XK_Find = 0xff68, /* Find, search */
XK_Cancel = 0xff69, /* Cancel, stop, abort, exit */
XK_Help = 0xff6a, /* Help */
XK_Break = 0xff6b,
XK_Mode_switch = 0xff7e, /* Character set switch */
XK_script_switch = 0xff7e, /* Alias for mode_switch */
XK_Num_Lock = 0xff7f,
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
XK_KP_Space = 0xff80, /* Space */
XK_KP_Tab = 0xff89,
XK_KP_Enter = 0xff8d, /* Enter */
XK_KP_F1 = 0xff91, /* PF1, KP_A, ... */
XK_KP_F2 = 0xff92,
XK_KP_F3 = 0xff93,
XK_KP_F4 = 0xff94,
XK_KP_Home = 0xff95,
XK_KP_Left = 0xff96,
XK_KP_Up = 0xff97,
XK_KP_Right = 0xff98,
XK_KP_Down = 0xff99,
XK_KP_Prior = 0xff9a,
XK_KP_Page_Up = 0xff9a
XK_KP_Next = 0xff9b,
XK_KP_Page_Down = 0xff9b,
XK_KP_End = 0xff9c,
XK_KP_Begin = 0xff9d,
XK_KP_Insert = 0xff9e,
XK_KP_Delete = 0xff9f,
XK_KP_Equal = 0xffbd, /* Equals */
XK_KP_Multiply = 0xffaa,
XK_KP_Add = 0xffab,
XK_KP_Separator = 0xffac, /* Separator, often comma */
XK_KP_Subtract = 0xffad,
XK_KP_Decimal = 0xffae,
XK_KP_Divide = 0xffaf,
XK_KP_0 = 0xffb0,
XK_KP_1 = 0xffb1,
XK_KP_2 = 0xffb2,
XK_KP_3 = 0xffb3,
XK_KP_4 = 0xffb4,
XK_KP_5 = 0xffb5,
XK_KP_6 = 0xffb6,
XK_KP_7 = 0xffb7,
XK_KP_8 = 0xffb8,
XK_KP_9 = 0xffb9,
/*
* Auxiliary functions; note the duplicate definitions for left and right
* function keys; Sun keyboards and a few other manufacturers have such
* function key groups on the left and/or right sides of the keyboard.
* We've not found a keyboard with more than 35 function keys total.
*/
XK_F1 = 0xffbe,
XK_F2 = 0xffbf,
XK_F3 = 0xffc0,
XK_F4 = 0xffc1,
XK_F5 = 0xffc2,
XK_F6 = 0xffc3,
XK_F7 = 0xffc4,
XK_F8 = 0xffc5,
XK_F9 = 0xffc6,
XK_F10 = 0xffc7,
XK_F11 = 0xffc8,
XK_L1 = 0xffc8,
XK_F12 = 0xffc9,
XK_L2 = 0xffc9,
XK_F13 = 0xffca,
XK_L3 = 0xffca,
XK_F14 = 0xffcb,
XK_L4 = 0xffcb,
XK_F15 = 0xffcc,
XK_L5 = 0xffcc,
XK_F16 = 0xffcd,
XK_L6 = 0xffcd,
XK_F17 = 0xffce,
XK_L7 = 0xffce,
XK_F18 = 0xffcf,
XK_L8 = 0xffcf,
XK_F19 = 0xffd0,
XK_L9 = 0xffd0,
XK_F20 = 0xffd1,
XK_L10 = 0xffd1,
XK_F21 = 0xffd2,
XK_R1 = 0xffd2,
XK_F22 = 0xffd3,
XK_R2 = 0xffd3,
XK_F23 = 0xffd4,
XK_R3 = 0xffd4,
XK_F24 = 0xffd5,
XK_R4 = 0xffd5,
XK_F25 = 0xffd6,
XK_R5 = 0xffd6,
XK_F26 = 0xffd7,
XK_R6 = 0xffd7,
XK_F27 = 0xffd8,
XK_R7 = 0xffd8,
XK_F28 = 0xffd9,
XK_R8 = 0xffd9,
XK_F29 = 0xffda,
XK_R9 = 0xffda,
XK_F30 = 0xffdb,
XK_R10 = 0xffdb,
XK_F31 = 0xffdc,
XK_R11 = 0xffdc,
XK_F32 = 0xffdd,
XK_R12 = 0xffdd,
XK_F33 = 0xffde,
XK_R13 = 0xffde,
XK_F34 = 0xffdf,
XK_R14 = 0xffdf,
XK_F35 = 0xffe0,
XK_R15 = 0xffe0,
/* Modifiers */
XK_Shift_L = 0xffe1, /* Left shift */
XK_Shift_R = 0xffe2, /* Right shift */
XK_Control_L = 0xffe3, /* Left control */
XK_Control_R = 0xffe4, /* Right control */
XK_Caps_Lock = 0xffe5, /* Caps lock */
XK_Shift_Lock = 0xffe6, /* Shift lock */
XK_Meta_L = 0xffe7, /* Left meta */
XK_Meta_R = 0xffe8, /* Right meta */
XK_Alt_L = 0xffe9, /* Left alt */
XK_Alt_R = 0xffea, /* Right alt */
XK_Super_L = 0xffeb, /* Left super */
XK_Super_R = 0xffec, /* Right super */
XK_Hyper_L = 0xffed, /* Left hyper */
XK_Hyper_R = 0xffee, /* Right hyper */
/*
* Latin 1
* (ISO/IEC 8859-1 = Unicode U+0020..U+00FF)
* Byte 3 = 0
*/
XK_space = 0x0020, /* U+0020 SPACE */
XK_exclam = 0x0021, /* U+0021 EXCLAMATION MARK */
XK_quotedbl = 0x0022, /* U+0022 QUOTATION MARK */
XK_numbersign = 0x0023, /* U+0023 NUMBER SIGN */
XK_dollar = 0x0024, /* U+0024 DOLLAR SIGN */
XK_percent = 0x0025, /* U+0025 PERCENT SIGN */
XK_ampersand = 0x0026, /* U+0026 AMPERSAND */
XK_apostrophe = 0x0027, /* U+0027 APOSTROPHE */
XK_quoteright = 0x0027, /* deprecated */
XK_parenleft = 0x0028, /* U+0028 LEFT PARENTHESIS */
XK_parenright = 0x0029, /* U+0029 RIGHT PARENTHESIS */
XK_asterisk = 0x002a, /* U+002A ASTERISK */
XK_plus = 0x002b, /* U+002B PLUS SIGN */
XK_comma = 0x002c, /* U+002C COMMA */
XK_minus = 0x002d, /* U+002D HYPHEN-MINUS */
XK_period = 0x002e, /* U+002E FULL STOP */
XK_slash = 0x002f, /* U+002F SOLIDUS */
XK_0 = 0x0030, /* U+0030 DIGIT ZERO */
XK_1 = 0x0031, /* U+0031 DIGIT ONE */
XK_2 = 0x0032, /* U+0032 DIGIT TWO */
XK_3 = 0x0033, /* U+0033 DIGIT THREE */
XK_4 = 0x0034, /* U+0034 DIGIT FOUR */
XK_5 = 0x0035, /* U+0035 DIGIT FIVE */
XK_6 = 0x0036, /* U+0036 DIGIT SIX */
XK_7 = 0x0037, /* U+0037 DIGIT SEVEN */
XK_8 = 0x0038, /* U+0038 DIGIT EIGHT */
XK_9 = 0x0039, /* U+0039 DIGIT NINE */
XK_colon = 0x003a, /* U+003A COLON */
XK_semicolon = 0x003b, /* U+003B SEMICOLON */
XK_less = 0x003c, /* U+003C LESS-THAN SIGN */
XK_equal = 0x003d, /* U+003D EQUALS SIGN */
XK_greater = 0x003e, /* U+003E GREATER-THAN SIGN */
XK_question = 0x003f, /* U+003F QUESTION MARK */
XK_at = 0x0040, /* U+0040 COMMERCIAL AT */
XK_A = 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
XK_B = 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
XK_C = 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
XK_D = 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
XK_E = 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
XK_F = 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
XK_G = 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
XK_H = 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
XK_I = 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
XK_J = 0x004a, /* U+004A LATIN CAPITAL LETTER J */
XK_K = 0x004b, /* U+004B LATIN CAPITAL LETTER K */
XK_L = 0x004c, /* U+004C LATIN CAPITAL LETTER L */
XK_M = 0x004d, /* U+004D LATIN CAPITAL LETTER M */
XK_N = 0x004e, /* U+004E LATIN CAPITAL LETTER N */
XK_O = 0x004f, /* U+004F LATIN CAPITAL LETTER O */
XK_P = 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
XK_Q = 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
XK_R = 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
XK_S = 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
XK_T = 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
XK_U = 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
XK_V = 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
XK_W = 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
XK_X = 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
XK_Y = 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
XK_Z = 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
XK_bracketleft = 0x005b, /* U+005B LEFT SQUARE BRACKET */
XK_backslash = 0x005c, /* U+005C REVERSE SOLIDUS */
XK_bracketright = 0x005d, /* U+005D RIGHT SQUARE BRACKET */
XK_asciicircum = 0x005e, /* U+005E CIRCUMFLEX ACCENT */
XK_underscore = 0x005f, /* U+005F LOW LINE */
XK_grave = 0x0060, /* U+0060 GRAVE ACCENT */
XK_quoteleft = 0x0060, /* deprecated */
XK_a = 0x0061, /* U+0061 LATIN SMALL LETTER A */
XK_b = 0x0062, /* U+0062 LATIN SMALL LETTER B */
XK_c = 0x0063, /* U+0063 LATIN SMALL LETTER C */
XK_d = 0x0064, /* U+0064 LATIN SMALL LETTER D */
XK_e = 0x0065, /* U+0065 LATIN SMALL LETTER E */
XK_f = 0x0066, /* U+0066 LATIN SMALL LETTER F */
XK_g = 0x0067, /* U+0067 LATIN SMALL LETTER G */
XK_h = 0x0068, /* U+0068 LATIN SMALL LETTER H */
XK_i = 0x0069, /* U+0069 LATIN SMALL LETTER I */
XK_j = 0x006a, /* U+006A LATIN SMALL LETTER J */
XK_k = 0x006b, /* U+006B LATIN SMALL LETTER K */
XK_l = 0x006c, /* U+006C LATIN SMALL LETTER L */
XK_m = 0x006d, /* U+006D LATIN SMALL LETTER M */
XK_n = 0x006e, /* U+006E LATIN SMALL LETTER N */
XK_o = 0x006f, /* U+006F LATIN SMALL LETTER O */
XK_p = 0x0070, /* U+0070 LATIN SMALL LETTER P */
XK_q = 0x0071, /* U+0071 LATIN SMALL LETTER Q */
XK_r = 0x0072, /* U+0072 LATIN SMALL LETTER R */
XK_s = 0x0073, /* U+0073 LATIN SMALL LETTER S */
XK_t = 0x0074, /* U+0074 LATIN SMALL LETTER T */
XK_u = 0x0075, /* U+0075 LATIN SMALL LETTER U */
XK_v = 0x0076, /* U+0076 LATIN SMALL LETTER V */
XK_w = 0x0077, /* U+0077 LATIN SMALL LETTER W */
XK_x = 0x0078, /* U+0078 LATIN SMALL LETTER X */
XK_y = 0x0079, /* U+0079 LATIN SMALL LETTER Y */
XK_z = 0x007a, /* U+007A LATIN SMALL LETTER Z */
XK_braceleft = 0x007b, /* U+007B LEFT CURLY BRACKET */
XK_bar = 0x007c, /* U+007C VERTICAL LINE */
XK_braceright = 0x007d, /* U+007D RIGHT CURLY BRACKET */
XK_asciitilde = 0x007e, /* U+007E TILDE */
XK_nobreakspace = 0x00a0, /* U+00A0 NO-BREAK SPACE */
XK_exclamdown = 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
XK_cent = 0x00a2, /* U+00A2 CENT SIGN */
XK_sterling = 0x00a3, /* U+00A3 POUND SIGN */
XK_currency = 0x00a4, /* U+00A4 CURRENCY SIGN */
XK_yen = 0x00a5, /* U+00A5 YEN SIGN */
XK_brokenbar = 0x00a6, /* U+00A6 BROKEN BAR */
XK_section = 0x00a7, /* U+00A7 SECTION SIGN */
XK_diaeresis = 0x00a8, /* U+00A8 DIAERESIS */
XK_copyright = 0x00a9, /* U+00A9 COPYRIGHT SIGN */
XK_ordfeminine = 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
XK_guillemotleft = 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_notsign = 0x00ac, /* U+00AC NOT SIGN */
XK_hyphen = 0x00ad, /* U+00AD SOFT HYPHEN */
XK_registered = 0x00ae, /* U+00AE REGISTERED SIGN */
XK_macron = 0x00af, /* U+00AF MACRON */
XK_degree = 0x00b0, /* U+00B0 DEGREE SIGN */
XK_plusminus = 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
XK_twosuperior = 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
XK_threesuperior = 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
XK_acute = 0x00b4, /* U+00B4 ACUTE ACCENT */
XK_mu = 0x00b5, /* U+00B5 MICRO SIGN */
XK_paragraph = 0x00b6, /* U+00B6 PILCROW SIGN */
XK_periodcentered = 0x00b7, /* U+00B7 MIDDLE DOT */
XK_cedilla = 0x00b8, /* U+00B8 CEDILLA */
XK_onesuperior = 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
XK_masculine = 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
XK_guillemotright = 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_onequarter = 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
XK_onehalf = 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
XK_threequarters = 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
XK_questiondown = 0x00bf, /* U+00BF INVERTED QUESTION MARK */
XK_Agrave = 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
XK_Aacute = 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
XK_Acircumflex = 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
XK_Atilde = 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
XK_Adiaeresis = 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
XK_Aring = 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
XK_AE = 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
XK_Ccedilla = 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
XK_Egrave = 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
XK_Eacute = 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
XK_Ecircumflex = 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
XK_Ediaeresis = 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
XK_Igrave = 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
XK_Iacute = 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
XK_Icircumflex = 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
XK_Idiaeresis = 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
XK_ETH = 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
XK_Eth = 0x00d0, /* deprecated */
XK_Ntilde = 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
XK_Ograve = 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
XK_Oacute = 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
XK_Ocircumflex = 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
XK_Otilde = 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
XK_Odiaeresis = 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
XK_multiply = 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
XK_Oslash = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
XK_Ooblique = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
XK_Ugrave = 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */
XK_Uacute = 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
XK_Ucircumflex = 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
XK_Udiaeresis = 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
XK_Yacute = 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
XK_THORN = 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
XK_Thorn = 0x00de, /* deprecated */
XK_ssharp = 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
XK_agrave = 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
XK_aacute = 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
XK_acircumflex = 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
XK_atilde = 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
XK_adiaeresis = 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
XK_aring = 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
XK_ae = 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
XK_ccedilla = 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
XK_egrave = 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
XK_eacute = 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
XK_ecircumflex = 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
XK_ediaeresis = 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
XK_igrave = 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
XK_iacute = 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
XK_icircumflex = 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
XK_idiaeresis = 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
XK_eth = 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
XK_ntilde = 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
XK_ograve = 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
XK_oacute = 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
XK_ocircumflex = 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
XK_otilde = 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
XK_odiaeresis = 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
XK_division = 0x00f7, /* U+00F7 DIVISION SIGN */
XK_oslash = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
XK_ooblique = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
XK_ugrave = 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */
XK_uacute = 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
XK_ucircumflex = 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
XK_udiaeresis = 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
XK_yacute = 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
XK_thorn = 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
// This file describes mappings from Unicode codepoints to the keysym values
// (and optionally, key names) expected by the RFB protocol
// How this file was generated:
// node /Users/jalf/dev/mi/novnc/utils/parse.js /opt/X11/include/X11/keysymdef.h
var keysyms = (function(){
"use strict";
var keynames = null;
var codepoints = {"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"60":60,"61":61,"62":62,"63":63,"64":64,"65":65,"66":66,"67":67,"68":68,"69":69,"70":70,"71":71,"72":72,"73":73,"74":74,"75":75,"76":76,"77":77,"78":78,"79":79,"80":80,"81":81,"82":82,"83":83,"84":84,"85":85,"86":86,"87":87,"88":88,"89":89,"90":90,"91":91,"92":92,"93":93,"94":94,"95":95,"96":96,"97":97,"98":98,"99":99,"100":100,"101":101,"102":102,"103":103,"104":104,"105":105,"106":106,"107":107,"108":108,"109":109,"110":110,"111":111,"112":112,"113":113,"114":114,"115":115,"116":116,"117":117,"118":118,"119":119,"120":120,"121":121,"122":122,"123":123,"124":124,"125":125,"126":126,"160":160,"161":161,"162":162,"163":163,"164":164,"165":165,"166":166,"167":167,"168":168,"169":169,"170":170,"171":171,"172":172,"173":173,"174":174,"175":175,"176":176,"177":177,"178":178,"179":179,"180":180,"181":181,"182":182,"183":183,"184":184,"185":185,"186":186,"187":187,"188":188,"189":189,"190":190,"191":191,"192":192,"193":193,"194":194,"195":195,"196":196,"197":197,"198":198,"199":199,"200":200,"201":201,"202":202,"203":203,"204":204,"205":205,"206":206,"207":207,"208":208,"209":209,"210":210,"211":211,"212":212,"213":213,"214":214,"215":215,"216":216,"217":217,"218":218,"219":219,"220":220,"221":221,"222":222,"223":223,"224":224,"225":225,"226":226,"227":227,"228":228,"229":229,"230":230,"231":231,"232":232,"233":233,"234":234,"235":235,"236":236,"237":237,"238":238,"239":239,"240":240,"241":241,"242":242,"243":243,"244":244,"245":245,"246":246,"247":247,"248":248,"249":249,"250":250,"251":251,"252":252,"253":253,"254":254,"255":255,"256":960,"257":992,"258":451,"259":483,"260":417,"261":433,"262":454,"263":486,"264":710,"265":742,"266":709,"267":741,"268":456,"269":488,"270":463,"271":495,"272":464,"273":496,"274":938,"275":954,"278":972,"279":1004,"280":458,"281":490,"282":460,"283":492,"284":728,"285":760,"286":683,"287":699,"288":725,"289":757,"290":939,"291":955,"292":678,"293":694,"294":673,"295":689,"296":933,"297":949,"298":975,"299":1007,"300":16777516,"301":16777517,"302":967,"303":999,"304":681,"305":697,"308":684,"309":700,"310":979,"311":1011,"312":930,"313":453,"314":485,"315":934,"316":950,"317":421,"318":437,"321":419,"322":435,"323":465,"324":497,"325":977,"326":1009,"327":466,"328":498,"330":957,"331":959,"332":978,"333":1010,"336":469,"337":501,"338":5052,"339":5053,"340":448,"341":480,"342":931,"343":947,"344":472,"345":504,"346":422,"347":438,"348":734,"349":766,"350":426,"351":442,"352":425,"353":441,"354":478,"355":510,"356":427,"357":443,"358":940,"359":956,"360":989,"361":1021,"362":990,"363":1022,"364":733,"365":765,"366":473,"367":505,"368":475,"369":507,"370":985,"371":1017,"372":16777588,"373":16777589,"374":16777590,"375":16777591,"376":5054,"377":428,"378":444,"379":431,"380":447,"381":430,"382":446,"399":16777615,"402":2294,"415":16777631,"416":16777632,"417":16777633,"431":16777647,"432":16777648,"437":16777653,"438":16777654,"439":16777655,"466":16777681,"486":16777702,"487":16777703,"601":16777817,"629":16777845,"658":16777874,"711":439,"728":418,"729":511,"731":434,"733":445,"901":1966,"902":1953,"904":1954,"905":1955,"906":1956,"908":1959,"910":1960,"911":1963,"912":1974,"913":1985,"914":1986,"915":1987,"916":1988,"917":1989,"918":1990,"919":1991,"920":1992,"921":1993,"922":1994,"923":1995,"924":1996,"925":1997,"926":1998,"927":1999,"928":2000,"929":2001,"931":2002,"932":2004,"933":2005,"934":2006,"935":2007,"936":2008,"937":2009,"938":1957,"939":1961,"940":1969,"941":1970,"942":1971,"943":1972,"944":1978,"945":2017,"946":2018,"947":2019,"948":2020,"949":2021,"950":2022,"951":2023,"952":2024,"953":2025,"954":2026,"955":2027,"956":2028,"957":2029,"958":2030,"959":2031,"960":2032,"961":2033,"962":2035,"963":2034,"964":2036,"965":2037,"966":2038,"967":2039,"968":2040,"969":2041,"970":1973,"971":1977,"972":1975,"973":1976,"974":1979,"1025":1715,"1026":1713,"1027":1714,"1028":1716,"1029":1717,"1030":1718,"1031":1719,"1032":1720,"1033":1721,"1034":1722,"1035":1723,"1036":1724,"1038":1726,"1039":1727,"1040":1761,"1041":1762,"1042":1783,"1043":1767,"1044":1764,"1045":1765,"1046":1782,"1047":1786,"1048":1769,"1049":1770,"1050":1771,"1051":1772,"1052":1773,"1053":1774,"1054":1775,"1055":1776,"1056":1778,"1057":1779,"1058":1780,"1059":1781,"1060":1766,"1061":1768,"1062":1763,"1063":1790,"1064":1787,"1065":1789,"1066":1791,"1067":1785,"1068":1784,"1069":1788,"1070":1760,"1071":1777,"1072":1729,"1073":1730,"1074":1751,"1075":1735,"1076":1732,"1077":1733,"1078":1750,"1079":1754,"1080":1737,"1081":1738,"1082":1739,"1083":1740,"1084":1741,"1085":1742,"1086":1743,"1087":1744,"1088":1746,"1089":1747,"1090":1748,"1091":1749,"1092":1734,"1093":1736,"1094":1731,"1095":1758,"1096":1755,"1097":1757,"1098":1759,"1099":1753,"1100":1752,"1101":1756,"1102":1728,"1103":1745,"1105":1699,"1106":1697,"1107":1698,"1108":1700,"1109":1701,"1110":1702,"1111":1703,"1112":1704,"1113":1705,"1114":1706,"1115":1707,"1116":1708,"1118":1710,"1119":1711,"1168":1725,"1169":1709,"1170":16778386,"1171":16778387,"1174":16778390,"1175":16778391,"1178":16778394,"1179":16778395,"1180":16778396,"1181":16778397,"1186":16778402,"1187":16778403,"1198":16778414,"1199":16778415,"1200":16778416,"1201":16778417,"1202":16778418,"1203":16778419,"1206":16778422,"1207":16778423,"1208":16778424,"1209":16778425,"1210":16778426,"1211":16778427,"1240":16778456,"1241":16778457,"1250":16778466,"1251":16778467,"1256":16778472,"1257":16778473,"1262":16778478,"1263":16778479,"1329":16778545,"1330":16778546,"1331":16778547,"1332":16778548,"1333":16778549,"1334":16778550,"1335":16778551,"1336":16778552,"1337":16778553,"1338":16778554,"1339":16778555,"1340":16778556,"1341":16778557,"1342":16778558,"1343":16778559,"1344":16778560,"1345":16778561,"1346":16778562,"1347":16778563,"1348":16778564,"1349":16778565,"1350":16778566,"1351":16778567,"1352":16778568,"1353":16778569,"1354":16778570,"1355":16778571,"1356":16778572,"1357":16778573,"1358":16778574,"1359":16778575,"1360":16778576,"1361":16778577,"1362":16778578,"1363":16778579,"1364":16778580,"1365":16778581,"1366":16778582,"1370":16778586,"1371":16778587,"1372":16778588,"1373":16778589,"1374":16778590,"1377":16778593,"1378":16778594,"1379":16778595,"1380":16778596,"1381":16778597,"1382":16778598,"1383":16778599,"1384":16778600,"1385":16778601,"1386":16778602,"1387":16778603,"1388":16778604,"1389":16778605,"1390":16778606,"1391":16778607,"1392":16778608,"1393":16778609,"1394":16778610,"1395":16778611,"1396":16778612,"1397":16778613,"1398":16778614,"1399":16778615,"1400":16778616,"1401":16778617,"1402":16778618,"1403":16778619,"1404":16778620,"1405":16778621,"1406":16778622,"1407":16778623,"1408":16778624,"1409":16778625,"1410":16778626,"1411":16778627,"1412":16778628,"1413":16778629,"1414":16778630,"1415":16778631,"1417":16778633,"1418":16778634,"1488":3296,"1489":3297,"1490":3298,"1491":3299,"1492":3300,"1493":3301,"1494":3302,"1495":3303,"1496":3304,"1497":3305,"1498":3306,"1499":3307,"1500":3308,"1501":3309,"1502":3310,"1503":3311,"1504":3312,"1505":3313,"1506":3314,"1507":3315,"1508":3316,"1509":3317,"1510":3318,"1511":3319,"1512":3320,"1513":3321,"1514":3322,"1548":1452,"1563":1467,"1567":1471,"1569":1473,"1570":1474,"1571":1475,"1572":1476,"1573":1477,"1574":1478,"1575":1479,"1576":1480,"1577":1481,"1578":1482,"1579":1483,"1580":1484,"1581":1485,"1582":1486,"1583":1487,"1584":1488,"1585":1489,"1586":1490,"1587":1491,"1588":1492,"1589":1493,"1590":1494,"1591":1495,"1592":1496,"1593":1497,"1594":1498,"1600":1504,"1601":1505,"1602":1506,"1603":1507,"1604":1508,"1605":1509,"1606":1510,"1607":1511,"1608":1512,"1609":1513,"1610":1514,"1611":1515,"1612":1516,"1613":1517,"1614":1518,"1615":1519,"1616":1520,"1617":1521,"1618":1522,"1619":16778835,"1620":16778836,"1621":16778837,"1632":16778848,"1633":16778849,"1634":16778850,"1635":16778851,"1636":16778852,"1637":16778853,"1638":16778854,"1639":16778855,"1640":16778856,"1641":16778857,"1642":16778858,"1648":16778864,"1657":16778873,"1662":16778878,"1670":16778886,"1672":16778888,"1681":16778897,"1688":16778904,"1700":16778916,"1705":16778921,"1711":16778927,"1722":16778938,"1726":16778942,"1729":16778945,"1740":16778956,"1746":16778962,"1748":16778964,"1776":16778992,"1777":16778993,"1778":16778994,"1779":16778995,"1780":16778996,"1781":16778997,"1782":16778998,"1783":16778999,"1784":16779000,"1785":16779001,"3458":16780674,"3459":16780675,"3461":16780677,"3462":16780678,"3463":16780679,"3464":16780680,"3465":16780681,"3466":16780682,"3467":16780683,"3468":16780684,"3469":16780685,"3470":16780686,"3471":16780687,"3472":16780688,"3473":16780689,"3474":16780690,"3475":16780691,"3476":16780692,"3477":16780693,"3478":16780694,"3482":16780698,"3483":16780699,"3484":16780700,"3485":16780701,"3486":16780702,"3487":16780703,"3488":16780704,"3489":16780705,"3490":16780706,"3491":16780707,"3492":16780708,"3493":16780709,"3494":16780710,"3495":16780711,"3496":16780712,"3497":16780713,"3498":16780714,"3499":16780715,"3500":16780716,"3501":16780717,"3502":16780718,"3503":16780719,"3504":16780720,"3505":16780721,"3507":16780723,"3508":16780724,"3509":16780725,"3510":16780726,"3511":16780727,"3512":16780728,"3513":16780729,"3514":16780730,"3515":16780731,"3517":16780733,"3520":16780736,"3521":16780737,"3522":16780738,"3523":16780739,"3524":16780740,"3525":16780741,"3526":16780742,"3530":16780746,"3535":16780751,"3536":16780752,"3537":16780753,"3538":16780754,"3539":16780755,"3540":16780756,"3542":16780758,"3544":16780760,"3545":16780761,"3546":16780762,"3547":16780763,"3548":16780764,"3549":16780765,"3550":16780766,"3551":16780767,"3570":16780786,"3571":16780787,"3572":16780788,"3585":3489,"3586":3490,"3587":3491,"3588":3492,"3589":3493,"3590":3494,"3591":3495,"3592":3496,"3593":3497,"3594":3498,"3595":3499,"3596":3500,"3597":3501,"3598":3502,"3599":3503,"3600":3504,"3601":3505,"3602":3506,"3603":3507,"3604":3508,"3605":3509,"3606":3510,"3607":3511,"3608":3512,"3609":3513,"3610":3514,"3611":3515,"3612":3516,"3613":3517,"3614":3518,"3615":3519,"3616":3520,"3617":3521,"3618":3522,"3619":3523,"3620":3524,"3621":3525,"3622":3526,"3623":3527,"3624":3528,"3625":3529,"3626":3530,"3627":3531,"3628":3532,"3629":3533,"3630":3534,"3631":3535,"3632":3536,"3633":3537,"3634":3538,"3635":3539,"3636":3540,"3637":3541,"3638":3542,"3639":3543,"3640":3544,"3641":3545,"3642":3546,"3647":3551,"3648":3552,"3649":3553,"3650":3554,"3651":3555,"3652":3556,"3653":3557,"3654":3558,"3655":3559,"3656":3560,"3657":3561,"3658":3562,"3659":3563,"3660":3564,"3661":3565,"3664":3568,"3665":3569,"3666":3570,"3667":3571,"3668":3572,"3669":3573,"3670":3574,"3671":3575,"3672":3576,"3673":3577,"4304":16781520,"4305":16781521,"4306":16781522,"4307":16781523,"4308":16781524,"4309":16781525,"4310":16781526,"4311":16781527,"4312":16781528,"4313":16781529,"4314":16781530,"4315":16781531,"4316":16781532,"4317":16781533,"4318":16781534,"4319":16781535,"4320":16781536,"4321":16781537,"4322":16781538,"4323":16781539,"4324":16781540,"4325":16781541,"4326":16781542,"4327":16781543,"4328":16781544,"4329":16781545,"4330":16781546,"4331":16781547,"4332":16781548,"4333":16781549,"4334":16781550,"4335":16781551,"4336":16781552,"4337":16781553,"4338":16781554,"4339":16781555,"4340":16781556,"4341":16781557,"4342":16781558,"7682":16784898,"7683":16784899,"7690":16784906,"7691":16784907,"7710":16784926,"7711":16784927,"7734":16784950,"7735":16784951,"7744":16784960,"7745":16784961,"7766":16784982,"7767":16784983,"7776":16784992,"7777":16784993,"7786":16785002,"7787":16785003,"7808":16785024,"7809":16785025,"7810":16785026,"7811":16785027,"7812":16785028,"7813":16785029,"7818":16785034,"7819":16785035,"7840":16785056,"7841":16785057,"7842":16785058,"7843":16785059,"7844":16785060,"7845":16785061,"7846":16785062,"7847":16785063,"7848":16785064,"7849":16785065,"7850":16785066,"7851":16785067,"7852":16785068,"7853":16785069,"7854":16785070,"7855":16785071,"7856":16785072,"7857":16785073,"7858":16785074,"7859":16785075,"7860":16785076,"7861":16785077,"7862":16785078,"7863":16785079,"7864":16785080,"7865":16785081,"7866":16785082,"7867":16785083,"7868":16785084,"7869":16785085,"7870":16785086,"7871":16785087,"7872":16785088,"7873":16785089,"7874":16785090,"7875":16785091,"7876":16785092,"7877":16785093,"7878":16785094,"7879":16785095,"7880":16785096,"7881":16785097,"7882":16785098,"7883":16785099,"7884":16785100,"7885":16785101,"7886":16785102,"7887":16785103,"7888":16785104,"7889":16785105,"7890":16785106,"7891":16785107,"7892":16785108,"7893":16785109,"7894":16785110,"7895":16785111,"7896":16785112,"7897":16785113,"7898":16785114,"7899":16785115,"7900":16785116,"7901":16785117,"7902":16785118,"7903":16785119,"7904":16785120,"7905":16785121,"7906":16785122,"7907":16785123,"7908":16785124,"7909":16785125,"7910":16785126,"7911":16785127,"7912":16785128,"7913":16785129,"7914":16785130,"7915":16785131,"7916":16785132,"7917":16785133,"7918":16785134,"7919":16785135,"7920":16785136,"7921":16785137,"7922":16785138,"7923":16785139,"7924":16785140,"7925":16785141,"7926":16785142,"7927":16785143,"7928":16785144,"7929":16785145,"8194":2722,"8195":2721,"8196":2723,"8197":2724,"8199":2725,"8200":2726,"8201":2727,"8202":2728,"8210":2747,"8211":2730,"8212":2729,"8213":1967,"8215":3295,"8216":2768,"8217":2769,"8218":2813,"8220":2770,"8221":2771,"8222":2814,"8224":2801,"8225":2802,"8226":2790,"8229":2735,"8230":2734,"8240":2773,"8242":2774,"8243":2775,"8248":2812,"8254":1150,"8304":16785520,"8308":16785524,"8309":16785525,"8310":16785526,"8311":16785527,"8312":16785528,"8313":16785529,"8320":16785536,"8321":16785537,"8322":16785538,"8323":16785539,"8324":16785540,"8325":16785541,"8326":16785542,"8327":16785543,"8328":16785544,"8329":16785545,"8352":16785568,"8353":16785569,"8354":16785570,"8355":16785571,"8356":16785572,"8357":16785573,"8358":16785574,"8359":16785575,"8360":16785576,"8361":3839,"8362":16785578,"8363":16785579,"8364":8364,"8453":2744,"8470":1712,"8471":2811,"8478":2772,"8482":2761,"8531":2736,"8532":2737,"8533":2738,"8534":2739,"8535":2740,"8536":2741,"8537":2742,"8538":2743,"8539":2755,"8540":2756,"8541":2757,"8542":2758,"8592":2299,"8593":2300,"8594":2301,"8595":2302,"8658":2254,"8660":2253,"8706":2287,"8709":16785925,"8711":2245,"8712":16785928,"8713":16785929,"8715":16785931,"8728":3018,"8730":2262,"8731":16785947,"8732":16785948,"8733":2241,"8734":2242,"8743":2270,"8744":2271,"8745":2268,"8746":2269,"8747":2239,"8748":16785964,"8749":16785965,"8756":2240,"8757":16785973,"8764":2248,"8771":2249,"8773":16785992,"8775":16785991,"8800":2237,"8801":2255,"8802":16786018,"8803":16786019,"8804":2236,"8805":2238,"8834":2266,"8835":2267,"8866":3068,"8867":3036,"8868":3010,"8869":3022,"8968":3027,"8970":3012,"8981":2810,"8992":2212,"8993":2213,"9109":3020,"9115":2219,"9117":2220,"9118":2221,"9120":2222,"9121":2215,"9123":2216,"9124":2217,"9126":2218,"9128":2223,"9132":2224,"9143":2209,"9146":2543,"9147":2544,"9148":2546,"9149":2547,"9225":2530,"9226":2533,"9227":2537,"9228":2531,"9229":2532,"9251":2732,"9252":2536,"9472":2211,"9474":2214,"9484":2210,"9488":2539,"9492":2541,"9496":2538,"9500":2548,"9508":2549,"9516":2551,"9524":2550,"9532":2542,"9618":2529,"9642":2791,"9643":2785,"9644":2779,"9645":2786,"9646":2783,"9647":2767,"9650":2792,"9651":2787,"9654":2781,"9655":2765,"9660":2793,"9661":2788,"9664":2780,"9665":2764,"9670":2528,"9675":2766,"9679":2782,"9702":2784,"9734":2789,"9742":2809,"9747":2762,"9756":2794,"9758":2795,"9792":2808,"9794":2807,"9827":2796,"9829":2798,"9830":2797,"9837":2806,"9839":2805,"10003":2803,"10007":2804,"10013":2777,"10016":2800,"10216":2748,"10217":2750,"10240":16787456,"10241":16787457,"10242":16787458,"10243":16787459,"10244":16787460,"10245":16787461,"10246":16787462,"10247":16787463,"10248":16787464,"10249":16787465,"10250":16787466,"10251":16787467,"10252":16787468,"10253":16787469,"10254":16787470,"10255":16787471,"10256":16787472,"10257":16787473,"10258":16787474,"10259":16787475,"10260":16787476,"10261":16787477,"10262":16787478,"10263":16787479,"10264":16787480,"10265":16787481,"10266":16787482,"10267":16787483,"10268":16787484,"10269":16787485,"10270":16787486,"10271":16787487,"10272":16787488,"10273":16787489,"10274":16787490,"10275":16787491,"10276":16787492,"10277":16787493,"10278":16787494,"10279":16787495,"10280":16787496,"10281":16787497,"10282":16787498,"10283":16787499,"10284":16787500,"10285":16787501,"10286":16787502,"10287":16787503,"10288":16787504,"10289":16787505,"10290":16787506,"10291":16787507,"10292":16787508,"10293":16787509,"10294":16787510,"10295":16787511,"10296":16787512,"10297":16787513,"10298":16787514,"10299":16787515,"10300":16787516,"10301":16787517,"10302":16787518,"10303":16787519,"10304":16787520,"10305":16787521,"10306":16787522,"10307":16787523,"10308":16787524,"10309":16787525,"10310":16787526,"10311":16787527,"10312":16787528,"10313":16787529,"10314":16787530,"10315":16787531,"10316":16787532,"10317":16787533,"10318":16787534,"10319":16787535,"10320":16787536,"10321":16787537,"10322":16787538,"10323":16787539,"10324":16787540,"10325":16787541,"10326":16787542,"10327":16787543,"10328":16787544,"10329":16787545,"10330":16787546,"10331":16787547,"10332":16787548,"10333":16787549,"10334":16787550,"10335":16787551,"10336":16787552,"10337":16787553,"10338":16787554,"10339":16787555,"10340":16787556,"10341":16787557,"10342":16787558,"10343":16787559,"10344":16787560,"10345":16787561,"10346":16787562,"10347":16787563,"10348":16787564,"10349":16787565,"10350":16787566,"10351":16787567,"10352":16787568,"10353":16787569,"10354":16787570,"10355":16787571,"10356":16787572,"10357":16787573,"10358":16787574,"10359":16787575,"10360":16787576,"10361":16787577,"10362":16787578,"10363":16787579,"10364":16787580,"10365":16787581,"10366":16787582,"10367":16787583,"10368":16787584,"10369":16787585,"10370":16787586,"10371":16787587,"10372":16787588,"10373":16787589,"10374":16787590,"10375":16787591,"10376":16787592,"10377":16787593,"10378":16787594,"10379":16787595,"10380":16787596,"10381":16787597,"10382":16787598,"10383":16787599,"10384":16787600,"10385":16787601,"10386":16787602,"10387":16787603,"10388":16787604,"10389":16787605,"10390":16787606,"10391":16787607,"10392":16787608,"10393":16787609,"10394":16787610,"10395":16787611,"10396":16787612,"10397":16787613,"10398":16787614,"10399":16787615,"10400":16787616,"10401":16787617,"10402":16787618,"10403":16787619,"10404":16787620,"10405":16787621,"10406":16787622,"10407":16787623,"10408":16787624,"10409":16787625,"10410":16787626,"10411":16787627,"10412":16787628,"10413":16787629,"10414":16787630,"10415":16787631,"10416":16787632,"10417":16787633,"10418":16787634,"10419":16787635,"10420":16787636,"10421":16787637,"10422":16787638,"10423":16787639,"10424":16787640,"10425":16787641,"10426":16787642,"10427":16787643,"10428":16787644,"10429":16787645,"10430":16787646,"10431":16787647,"10432":16787648,"10433":16787649,"10434":16787650,"10435":16787651,"10436":16787652,"10437":16787653,"10438":16787654,"10439":16787655,"10440":16787656,"10441":16787657,"10442":16787658,"10443":16787659,"10444":16787660,"10445":16787661,"10446":16787662,"10447":16787663,"10448":16787664,"10449":16787665,"10450":16787666,"10451":16787667,"10452":16787668,"10453":16787669,"10454":16787670,"10455":16787671,"10456":16787672,"10457":16787673,"10458":16787674,"10459":16787675,"10460":16787676,"10461":16787677,"10462":16787678,"10463":16787679,"10464":16787680,"10465":16787681,"10466":16787682,"10467":16787683,"10468":16787684,"10469":16787685,"10470":16787686,"10471":16787687,"10472":16787688,"10473":16787689,"10474":16787690,"10475":16787691,"10476":16787692,"10477":16787693,"10478":16787694,"10479":16787695,"10480":16787696,"10481":16787697,"10482":16787698,"10483":16787699,"10484":16787700,"10485":16787701,"10486":16787702,"10487":16787703,"10488":16787704,"10489":16787705,"10490":16787706,"10491":16787707,"10492":16787708,"10493":16787709,"10494":16787710,"10495":16787711,"12289":1188,"12290":1185,"12300":1186,"12301":1187,"12443":1246,"12444":1247,"12449":1191,"12450":1201,"12451":1192,"12452":1202,"12453":1193,"12454":1203,"12455":1194,"12456":1204,"12457":1195,"12458":1205,"12459":1206,"12461":1207,"12463":1208,"12465":1209,"12467":1210,"12469":1211,"12471":1212,"12473":1213,"12475":1214,"12477":1215,"12479":1216,"12481":1217,"12483":1199,"12484":1218,"12486":1219,"12488":1220,"12490":1221,"12491":1222,"12492":1223,"12493":1224,"12494":1225,"12495":1226,"12498":1227,"12501":1228,"12504":1229,"12507":1230,"12510":1231,"12511":1232,"12512":1233,"12513":1234,"12514":1235,"12515":1196,"12516":1236,"12517":1197,"12518":1237,"12519":1198,"12520":1238,"12521":1239,"12522":1240,"12523":1241,"12524":1242,"12525":1243,"12527":1244,"12530":1190,"12531":1245,"12539":1189,"12540":1200};
function lookup(k) { return k ? {keysym: k, keyname: keynames ? keynames[k] : k} : undefined; }
return {
fromUnicode : function(u) { return lookup(codepoints[u]); },
lookup : lookup
};
})();
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.LGPL-3) * Licensed under MPL 2.0 (see LICENSE.txt)
*/ */
"use strict"; "use strict";
...@@ -79,10 +79,22 @@ queue_next_packet = function () { ...@@ -79,10 +79,22 @@ queue_next_packet = function () {
} }
}; };
var bytes_processed = 0;
do_packet = function () { do_packet = function () {
//Util.Debug("Processing frame: " + frame_idx); //Util.Debug("Processing frame: " + frame_idx);
var frame = VNC_frame_data[frame_idx]; var frame = VNC_frame_data[frame_idx],
rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)}); start = frame.indexOf('{', 1) + 1;
bytes_processed += frame.length - start;
if (VNC_frame_encoding === 'binary') {
var u8 = new Uint8Array(frame.length - start);
for (var i = 0; i < frame.length - start; i++) {
u8[i] = frame.charCodeAt(start + i);
}
rfb.recv_message({'data' : u8});
} else {
rfb.recv_message({'data' : frame.slice(start)});
}
frame_idx += 1; frame_idx += 1;
queue_next_packet(); queue_next_packet();
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt) * Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
"use strict"; /* jslint white: false, browser: true */
/*jslint white: false, browser: true */ /* global window, $D, Util, WebUtil, RFB, Display */
/*global window, $D, Util, WebUtil, RFB, Display */
var UI;
var UI = {
(function () {
rfb_state : 'loaded', "use strict";
settingsOpen : false,
connSettingsOpen : false, // Load supporting scripts
clipboardOpen: false, window.onscriptsload = function () { UI.load(); };
keyboardVisible: false, window.onload = function () { UI.keyboardinputReset(); };
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
// Render default UI and initialize settings menu "keysymdef.js", "keyboard.js", "input.js", "display.js",
load: function() { "jsunzip.js", "rfb.js", "keysym.js"]);
var html = '', i, sheet, sheets, llevels;
var UI = {
// Stylesheet selection dropdown
sheet = WebUtil.selectStylesheet(); rfb_state : 'loaded',
sheets = WebUtil.getStylesheets(); settingsOpen : false,
for (i = 0; i < sheets.length; i += 1) { connSettingsOpen : false,
UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title); popupStatusOpen : false,
} clipboardOpen: false,
keyboardVisible: false,
// Logging selection dropdown hideKeyboardTimeout: null,
llevels = ['error', 'warn', 'info', 'debug']; lastKeyboardinput: null,
for (i = 0; i < llevels.length; i += 1) { defaultKeyboardinputLen: 100,
UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]); extraKeysVisible: false,
} ctrlOn: false,
altOn: false,
// Settings with immediate effects isTouchDevice: false,
UI.initSetting('logging', 'warn');
WebUtil.init_logging(UI.getSetting('logging')); // Setup rfb object, load settings from browser storage, then call
// UI.init to setup the UI/menus
UI.initSetting('stylesheet', 'default'); load: function (callback) {
WebUtil.selectStylesheet(null); WebUtil.initSettings(UI.start, callback);
// call twice to get around webkit bug },
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
// Render default UI and initialize settings menu
/* Populate the controls if defaults are provided in the URL */ start: function(callback) {
UI.initSetting('host', window.location.hostname); UI.isTouchDevice = 'ontouchstart' in document.documentElement;
UI.initSetting('port', window.location.port);
UI.initSetting('password', ''); // Stylesheet selection dropdown
UI.initSetting('encrypt', (window.location.protocol === "https:")); var sheet = WebUtil.selectStylesheet();
UI.initSetting('true_color', true); var sheets = WebUtil.getStylesheets();
UI.initSetting('cursor', false); var i;
UI.initSetting('shared', true); for (i = 0; i < sheets.length; i += 1) {
UI.initSetting('view_only', false); UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
UI.initSetting('connectTimeout', 2); }
UI.initSetting('path', 'websockify');
UI.initSetting('repeaterID', '');
UI.rfb = RFB({'target': $D('noVNC_canvas'),
'onUpdateState': UI.updateState,
'onClipboard': UI.clipReceive});
UI.updateVisualState();
// Unfocus clipboard when over the VNC area
//$D('VNC_screen').onmousemove = function () {
// var keyboard = UI.rfb.get_keyboard();
// if ((! keyboard) || (! keyboard.get_focused())) {
// $D('VNC_clipboard_text').blur();
// }
// };
// Show mouse selector buttons on touch screen devices
if ('ontouchstart' in document.documentElement) {
// Show mobile buttons
$D('noVNC_mobile_buttons').style.display = "inline";
UI.setMouseButton();
// Remove the address bar
setTimeout(function() { window.scrollTo(0, 1); }, 100);
UI.forceSetting('clip', true);
$D('noVNC_clip').disabled = true;
} else {
UI.initSetting('clip', false);
}
//iOS Safari does not support CSS position:fixed.
//This detects iOS devices and enables javascript workaround.
if ((navigator.userAgent.match(/iPhone/i)) ||
(navigator.userAgent.match(/iPod/i)) ||
(navigator.userAgent.match(/iPad/i))) {
//UI.setOnscroll();
//UI.setResize();
}
$D('noVNC_host').focus();
UI.setViewClip();
Util.addEvent(window, 'resize', UI.setViewClip);
Util.addEvent(window, 'beforeunload', function () {
if (UI.rfb_state === 'normal') {
return "You are currently connected.";
}
} );
// Show description by default when hosted at for kanaka.github.com
if (location.host === "kanaka.github.com") {
// Open the description dialog
$D('noVNC_description').style.display = "block";
} else {
// Open the connect panel on first load
UI.toggleConnectPanel();
}
},
// Read form control compatible setting from cookie
getSetting: function(name) {
var val, ctrl = $D('noVNC_' + name);
val = WebUtil.readCookie(name);
if (ctrl.type === 'checkbox') {
if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
val = false;
} else {
val = true;
}
}
return val;
},
// Update cookie and form control setting. If value is not set, then // Logging selection dropdown
// updates from control to current cookie setting. var llevels = ['error', 'warn', 'info', 'debug'];
updateSetting: function(name, value) { for (i = 0; i < llevels.length; i += 1) {
UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
}
var i, ctrl = $D('noVNC_' + name); // Settings with immediate effects
// Save the cookie for this session UI.initSetting('logging', 'warn');
if (typeof value !== 'undefined') { WebUtil.init_logging(UI.getSetting('logging'));
WebUtil.createCookie(name, value);
} UI.initSetting('stylesheet', 'default');
WebUtil.selectStylesheet(null);
// call twice to get around webkit bug
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
// if port == 80 (or 443) then it won't be present and should be
// set manually
var port = window.location.port;
if (!port) {
if (window.location.protocol.substring(0,5) == 'https') {
port = 443;
}
else if (window.location.protocol.substring(0,4) == 'http') {
port = 80;
}
}
// Update the settings control /* Populate the controls if defaults are provided in the URL */
value = UI.getSetting(name); UI.initSetting('host', window.location.hostname);
UI.initSetting('port', port);
UI.initSetting('password', '');
UI.initSetting('encrypt', (window.location.protocol === "https:"));
UI.initSetting('true_color', true);
UI.initSetting('cursor', !UI.isTouchDevice);
UI.initSetting('shared', true);
UI.initSetting('view_only', false);
UI.initSetting('path', 'websockify');
UI.initSetting('repeaterID', '');
UI.rfb = new RFB({'target': $D('noVNC_canvas'),
'onUpdateState': UI.updateState,
'onXvpInit': UI.updateXvpVisualState,
'onClipboard': UI.clipReceive,
'onDesktopName': UI.updateDocumentTitle});
var autoconnect = WebUtil.getQueryVar('autoconnect', false);
if (autoconnect === 'true' || autoconnect == '1') {
autoconnect = true;
UI.connect();
} else {
autoconnect = false;
}
if (ctrl.type === 'checkbox') { UI.updateVisualState();
ctrl.checked = value;
// Show mouse selector buttons on touch screen devices
if (UI.isTouchDevice) {
// Show mobile buttons
$D('noVNC_mobile_buttons').style.display = "inline";
UI.setMouseButton();
// Remove the address bar
setTimeout(function() { window.scrollTo(0, 1); }, 100);
UI.forceSetting('clip', true);
$D('noVNC_clip').disabled = true;
} else {
UI.initSetting('clip', false);
}
} else if (typeof ctrl.options !== 'undefined') { //iOS Safari does not support CSS position:fixed.
for (i = 0; i < ctrl.options.length; i += 1) { //This detects iOS devices and enables javascript workaround.
if (ctrl.options[i].value === value) { if ((navigator.userAgent.match(/iPhone/i)) ||
ctrl.selectedIndex = i; (navigator.userAgent.match(/iPod/i)) ||
break; (navigator.userAgent.match(/iPad/i))) {
//UI.setOnscroll();
//UI.setResize();
} }
} UI.setBarPosition();
} else {
/*Weird IE9 error leads to 'null' appearring $D('noVNC_host').focus();
in textboxes instead of ''.*/
if (value === null) { UI.setViewClip();
value = ""; Util.addEvent(window, 'resize', UI.setViewClip);
}
ctrl.value = value; Util.addEvent(window, 'beforeunload', function () {
} if (UI.rfb_state === 'normal') {
}, return "You are currently connected.";
}
// Save control setting to cookie } );
saveSetting: function(name) {
var val, ctrl = $D('noVNC_' + name); // Show description by default when hosted at for kanaka.github.com
if (ctrl.type === 'checkbox') { if (location.host === "kanaka.github.io") {
val = ctrl.checked; // Open the description dialog
} else if (typeof ctrl.options !== 'undefined') { $D('noVNC_description').style.display = "block";
val = ctrl.options[ctrl.selectedIndex].value; } else {
} else { // Show the connect panel on first load unless autoconnecting
val = ctrl.value; if (autoconnect === UI.connSettingsOpen) {
} UI.toggleConnectPanel();
WebUtil.createCookie(name, val); }
//Util.Debug("Setting saved '" + name + "=" + val + "'"); }
return val;
}, // Add mouse event click/focus/blur event handlers to the UI
UI.addMouseHandlers();
// Initial page load read/initialization of settings
initSetting: function(name, defVal) { if (typeof callback === "function") {
var val; callback(UI.rfb);
}
// Check Query string followed by cookie },
val = WebUtil.getQueryVar(name);
if (val === null) { addMouseHandlers: function() {
val = WebUtil.readCookie(name, defVal); // Setup interface handlers that can't be inline
} $D("noVNC_view_drag_button").onclick = UI.setViewDrag;
UI.updateSetting(name, val); $D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
//Util.Debug("Setting '" + name + "' initialized to '" + val + "'"); $D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
return val; $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
}, $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
$D("showKeyboard").onclick = UI.showKeyboard;
// Force a setting to be a certain value
forceSetting: function(name, val) { $D("keyboardinput").oninput = UI.keyInput;
UI.updateSetting(name, val); $D("keyboardinput").onblur = UI.keyInputBlur;
return val;
}, $D("showExtraKeysButton").onclick = UI.showExtraKeys;
$D("toggleCtrlButton").onclick = UI.toggleCtrl;
$D("toggleAltButton").onclick = UI.toggleAlt;
// Show the clipboard panel $D("sendTabButton").onclick = UI.sendTab;
toggleClipboardPanel: function() { $D("sendEscButton").onclick = UI.sendEsc;
// Close the description panel
$D('noVNC_description').style.display = "none"; $D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
//Close settings if open $D("xvpShutdownButton").onclick = UI.xvpShutdown;
if (UI.settingsOpen === true) { $D("xvpRebootButton").onclick = UI.xvpReboot;
UI.settingsApply(); $D("xvpResetButton").onclick = UI.xvpReset;
UI.closeSettingsMenu(); $D("noVNC_status").onclick = UI.togglePopupStatusPanel;
} $D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
//Close connection settings if open $D("xvpButton").onclick = UI.toggleXvpPanel;
if (UI.connSettingsOpen === true) { $D("clipboardButton").onclick = UI.toggleClipboardPanel;
UI.toggleConnectPanel(); $D("settingsButton").onclick = UI.toggleSettingsPanel;
} $D("connectButton").onclick = UI.toggleConnectPanel;
//Toggle Clipboard Panel $D("disconnectButton").onclick = UI.disconnect;
if (UI.clipboardOpen === true) { $D("descriptionButton").onclick = UI.toggleConnectPanel;
$D('noVNC_clipboard').style.display = "none";
$D('clipboardButton').className = "noVNC_status_button"; $D("noVNC_clipboard_text").onfocus = UI.displayBlur;
UI.clipboardOpen = false; $D("noVNC_clipboard_text").onblur = UI.displayFocus;
} else { $D("noVNC_clipboard_text").onchange = UI.clipSend;
$D('noVNC_clipboard').style.display = "block"; $D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
$D('clipboardButton').className = "noVNC_status_button_selected";
UI.clipboardOpen = true; $D("noVNC_settings_menu").onmouseover = UI.displayBlur;
} $D("noVNC_settings_menu").onmouseover = UI.displayFocus;
}, $D("noVNC_apply").onclick = UI.settingsApply;
// Show the connection settings panel/menu $D("noVNC_connect_button").onclick = UI.connect;
toggleConnectPanel: function() { },
// Close the description panel
$D('noVNC_description').style.display = "none"; // Read form control compatible setting from cookie
//Close connection settings if open getSetting: function(name) {
if (UI.settingsOpen === true) { var ctrl = $D('noVNC_' + name);
UI.settingsApply(); var val = WebUtil.readSetting(name);
UI.closeSettingsMenu(); if (val !== null && ctrl.type === 'checkbox') {
$D('connectButton').className = "noVNC_status_button"; if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
} val = false;
if (UI.clipboardOpen === true) { } else {
UI.toggleClipboardPanel(); val = true;
} }
}
//Toggle Connection Panel return val;
if (UI.connSettingsOpen === true) { },
$D('noVNC_controls').style.display = "none";
$D('connectButton').className = "noVNC_status_button"; // Update cookie and form control setting. If value is not set, then
UI.connSettingsOpen = false; // updates from control to current cookie setting.
} else { updateSetting: function(name, value) {
$D('noVNC_controls').style.display = "block";
$D('connectButton').className = "noVNC_status_button_selected"; // Save the cookie for this session
UI.connSettingsOpen = true; if (typeof value !== 'undefined') {
$D('noVNC_host').focus(); WebUtil.writeSetting(name, value);
} }
},
// Update the settings control
// Toggle the settings menu: value = UI.getSetting(name);
// On open, settings are refreshed from saved cookies.
// On close, settings are applied var ctrl = $D('noVNC_' + name);
toggleSettingsPanel: function() { if (ctrl.type === 'checkbox') {
// Close the description panel ctrl.checked = value;
$D('noVNC_description').style.display = "none";
if (UI.settingsOpen) { } else if (typeof ctrl.options !== 'undefined') {
UI.settingsApply(); for (var i = 0; i < ctrl.options.length; i += 1) {
UI.closeSettingsMenu(); if (ctrl.options[i].value === value) {
} else { ctrl.selectedIndex = i;
UI.updateSetting('encrypt'); break;
UI.updateSetting('true_color'); }
if (UI.rfb.get_display().get_cursor_uri()) { }
UI.updateSetting('cursor'); } else {
} else { /*Weird IE9 error leads to 'null' appearring
UI.updateSetting('cursor', false); in textboxes instead of ''.*/
$D('noVNC_cursor').disabled = true; if (value === null) {
} value = "";
UI.updateSetting('clip'); }
UI.updateSetting('shared'); ctrl.value = value;
UI.updateSetting('view_only'); }
UI.updateSetting('connectTimeout'); },
UI.updateSetting('path');
UI.updateSetting('repeaterID'); // Save control setting to cookie
UI.updateSetting('stylesheet'); saveSetting: function(name) {
UI.updateSetting('logging'); var val, ctrl = $D('noVNC_' + name);
if (ctrl.type === 'checkbox') {
UI.openSettingsMenu(); val = ctrl.checked;
} } else if (typeof ctrl.options !== 'undefined') {
}, val = ctrl.options[ctrl.selectedIndex].value;
} else {
// Open menu val = ctrl.value;
openSettingsMenu: function() { }
// Close the description panel WebUtil.writeSetting(name, val);
$D('noVNC_description').style.display = "none"; //Util.Debug("Setting saved '" + name + "=" + val + "'");
if (UI.clipboardOpen === true) { return val;
UI.toggleClipboardPanel(); },
}
//Close connection settings if open // Initial page load read/initialization of settings
if (UI.connSettingsOpen === true) { initSetting: function(name, defVal) {
UI.toggleConnectPanel(); // Check Query string followed by cookie
} var val = WebUtil.getQueryVar(name);
$D('noVNC_settings').style.display = "block"; if (val === null) {
$D('settingsButton').className = "noVNC_status_button_selected"; val = WebUtil.readSetting(name, defVal);
UI.settingsOpen = true; }
}, UI.updateSetting(name, val);
return val;
// Close menu (without applying settings) },
closeSettingsMenu: function() {
$D('noVNC_settings').style.display = "none"; // Force a setting to be a certain value
$D('settingsButton').className = "noVNC_status_button"; forceSetting: function(name, val) {
UI.settingsOpen = false; UI.updateSetting(name, val);
}, return val;
},
// Save/apply settings when 'Apply' button is pressed
settingsApply: function() {
//Util.Debug(">> settingsApply"); // Show the popup status panel
UI.saveSetting('encrypt'); togglePopupStatusPanel: function() {
UI.saveSetting('true_color'); var psp = $D('noVNC_popup_status_panel');
if (UI.rfb.get_display().get_cursor_uri()) { if (UI.popupStatusOpen === true) {
UI.saveSetting('cursor'); psp.style.display = "none";
} UI.popupStatusOpen = false;
UI.saveSetting('clip'); } else {
UI.saveSetting('shared'); psp.innerHTML = $D('noVNC_status').innerHTML;
UI.saveSetting('view_only'); psp.style.display = "block";
UI.saveSetting('connectTimeout'); psp.style.left = window.innerWidth/2 -
UI.saveSetting('path'); parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
UI.saveSetting('repeaterID'); UI.popupStatusOpen = true;
UI.saveSetting('stylesheet'); }
UI.saveSetting('logging'); },
// Settings with immediate (non-connected related) effect // Show the XVP panel
WebUtil.selectStylesheet(UI.getSetting('stylesheet')); toggleXvpPanel: function() {
WebUtil.init_logging(UI.getSetting('logging')); // Close the description panel
UI.setViewClip(); $D('noVNC_description').style.display = "none";
UI.setViewDrag(UI.rfb.get_viewportDrag()); // Close settings if open
//Util.Debug("<< settingsApply"); if (UI.settingsOpen === true) {
}, UI.settingsApply();
UI.closeSettingsMenu();
}
// Close connection settings if open
setPassword: function() { if (UI.connSettingsOpen === true) {
UI.rfb.sendPassword($D('noVNC_password').value); UI.toggleConnectPanel();
//Reset connect button. }
$D('noVNC_connect_button').value = "Connect"; // Close popup status panel if open
$D('noVNC_connect_button').onclick = UI.Connect; if (UI.popupStatusOpen === true) {
//Hide connection panel. UI.togglePopupStatusPanel();
UI.toggleConnectPanel(); }
return false; // Close clipboard panel if open
}, if (UI.clipboardOpen === true) {
UI.toggleClipboardPanel();
sendCtrlAltDel: function() { }
UI.rfb.sendCtrlAltDel(); // Toggle XVP panel
}, if (UI.xvpOpen === true) {
$D('noVNC_xvp').style.display = "none";
setMouseButton: function(num) { $D('xvpButton').className = "noVNC_status_button";
var b, blist = [0, 1,2,4], button; UI.xvpOpen = false;
} else {
if (typeof num === 'undefined') { $D('noVNC_xvp').style.display = "block";
// Disable mouse buttons $D('xvpButton').className = "noVNC_status_button_selected";
num = -1; UI.xvpOpen = true;
} }
if (UI.rfb) { },
UI.rfb.get_mouse().set_touchButton(num);
} // Show the clipboard panel
toggleClipboardPanel: function() {
for (b = 0; b < blist.length; b++) { // Close the description panel
button = $D('noVNC_mouse_button' + blist[b]); $D('noVNC_description').style.display = "none";
if (blist[b] === num) { // Close settings if open
button.style.display = ""; if (UI.settingsOpen === true) {
} else { UI.settingsApply();
button.style.display = "none"; UI.closeSettingsMenu();
/* }
button.style.backgroundColor = "black"; // Close connection settings if open
button.style.color = "lightgray"; if (UI.connSettingsOpen === true) {
button.style.backgroundColor = ""; UI.toggleConnectPanel();
button.style.color = ""; }
*/ // Close popup status panel if open
} if (UI.popupStatusOpen === true) {
} UI.togglePopupStatusPanel();
}, }
// Close XVP panel if open
updateState: function(rfb, state, oldstate, msg) { if (UI.xvpOpen === true) {
var s, sb, c, d, cad, vd, klass; UI.toggleXvpPanel();
UI.rfb_state = state; }
s = $D('noVNC_status'); // Toggle Clipboard Panel
sb = $D('noVNC_status_bar'); if (UI.clipboardOpen === true) {
switch (state) { $D('noVNC_clipboard').style.display = "none";
case 'failed': $D('clipboardButton').className = "noVNC_status_button";
case 'fatal': UI.clipboardOpen = false;
klass = "noVNC_status_error"; } else {
break; $D('noVNC_clipboard').style.display = "block";
case 'normal': $D('clipboardButton').className = "noVNC_status_button_selected";
klass = "noVNC_status_normal"; UI.clipboardOpen = true;
break; }
case 'disconnected': },
$D('noVNC_logo').style.display = "block";
// Fall through // Show the connection settings panel/menu
case 'loaded': toggleConnectPanel: function() {
klass = "noVNC_status_normal"; // Close the description panel
break; $D('noVNC_description').style.display = "none";
case 'password': // Close connection settings if open
if (UI.settingsOpen === true) {
UI.settingsApply();
UI.closeSettingsMenu();
$D('connectButton').className = "noVNC_status_button";
}
// Close clipboard panel if open
if (UI.clipboardOpen === true) {
UI.toggleClipboardPanel();
}
// Close popup status panel if open
if (UI.popupStatusOpen === true) {
UI.togglePopupStatusPanel();
}
// Close XVP panel if open
if (UI.xvpOpen === true) {
UI.toggleXvpPanel();
}
// Toggle Connection Panel
if (UI.connSettingsOpen === true) {
$D('noVNC_controls').style.display = "none";
$D('connectButton').className = "noVNC_status_button";
UI.connSettingsOpen = false;
UI.saveSetting('host');
UI.saveSetting('port');
//UI.saveSetting('password');
} else {
$D('noVNC_controls').style.display = "block";
$D('connectButton').className = "noVNC_status_button_selected";
UI.connSettingsOpen = true;
$D('noVNC_host').focus();
}
},
// Toggle the settings menu:
// On open, settings are refreshed from saved cookies.
// On close, settings are applied
toggleSettingsPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
if (UI.settingsOpen) {
UI.settingsApply();
UI.closeSettingsMenu();
} else {
UI.updateSetting('encrypt');
UI.updateSetting('true_color');
if (UI.rfb.get_display().get_cursor_uri()) {
UI.updateSetting('cursor');
} else {
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
}
UI.updateSetting('clip');
UI.updateSetting('shared');
UI.updateSetting('view_only');
UI.updateSetting('path');
UI.updateSetting('repeaterID');
UI.updateSetting('stylesheet');
UI.updateSetting('logging');
UI.openSettingsMenu();
}
},
// Open menu
openSettingsMenu: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
// Close clipboard panel if open
if (UI.clipboardOpen === true) {
UI.toggleClipboardPanel();
}
// Close connection settings if open
if (UI.connSettingsOpen === true) {
UI.toggleConnectPanel();
}
// Close popup status panel if open
if (UI.popupStatusOpen === true) {
UI.togglePopupStatusPanel();
}
// Close XVP panel if open
if (UI.xvpOpen === true) {
UI.toggleXvpPanel();
}
$D('noVNC_settings').style.display = "block";
$D('settingsButton').className = "noVNC_status_button_selected";
UI.settingsOpen = true;
},
// Close menu (without applying settings)
closeSettingsMenu: function() {
$D('noVNC_settings').style.display = "none";
$D('settingsButton').className = "noVNC_status_button";
UI.settingsOpen = false;
},
// Save/apply settings when 'Apply' button is pressed
settingsApply: function() {
//Util.Debug(">> settingsApply");
UI.saveSetting('encrypt');
UI.saveSetting('true_color');
if (UI.rfb.get_display().get_cursor_uri()) {
UI.saveSetting('cursor');
}
UI.saveSetting('clip');
UI.saveSetting('shared');
UI.saveSetting('view_only');
UI.saveSetting('path');
UI.saveSetting('repeaterID');
UI.saveSetting('stylesheet');
UI.saveSetting('logging');
// Settings with immediate (non-connected related) effect
WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
WebUtil.init_logging(UI.getSetting('logging'));
UI.setViewClip();
UI.setViewDrag(UI.rfb.get_viewportDrag());
//Util.Debug("<< settingsApply");
},
setPassword: function() {
UI.rfb.sendPassword($D('noVNC_password').value);
//Reset connect button.
$D('noVNC_connect_button').value = "Connect";
$D('noVNC_connect_button').onclick = UI.Connect;
//Hide connection panel.
UI.toggleConnectPanel(); UI.toggleConnectPanel();
return false;
},
$D('noVNC_connect_button').value = "Send Password"; sendCtrlAltDel: function() {
$D('noVNC_connect_button').onclick = UI.setPassword; UI.rfb.sendCtrlAltDel();
$D('noVNC_password').focus(); },
klass = "noVNC_status_warn";
break;
default:
klass = "noVNC_status_warn";
break;
}
if (typeof(msg) !== 'undefined') {
s.setAttribute("class", klass);
sb.setAttribute("class", klass);
s.innerHTML = msg;
}
UI.updateVisualState();
},
// Disable/enable controls depending on connection state
updateVisualState: function() {
var connected = UI.rfb_state === 'normal' ? true : false;
//Util.Debug(">> updateVisualState");
$D('noVNC_encrypt').disabled = connected;
$D('noVNC_true_color').disabled = connected;
if (UI.rfb && UI.rfb.get_display() &&
UI.rfb.get_display().get_cursor_uri()) {
$D('noVNC_cursor').disabled = connected;
} else {
UI.updateSetting('cursor', false);
$D('noVNC_cursor').disabled = true;
}
$D('noVNC_shared').disabled = connected;
$D('noVNC_view_only').disabled = connected;
$D('noVNC_connectTimeout').disabled = connected;
$D('noVNC_path').disabled = connected;
$D('noVNC_repeaterID').disabled = connected;
if (connected) {
UI.setViewClip();
UI.setMouseButton(1);
$D('clipboardButton').style.display = "inline";
$D('showKeyboard').style.display = "inline";
$D('sendCtrlAltDelButton').style.display = "inline";
} else {
UI.setMouseButton();
$D('clipboardButton').style.display = "none";
$D('showKeyboard').style.display = "none";
$D('sendCtrlAltDelButton').style.display = "none";
}
// State change disables viewport dragging.
// It is enabled (toggled) by direct click on the button
UI.setViewDrag(false);
switch (UI.rfb_state) {
case 'fatal':
case 'failed':
case 'loaded':
case 'disconnected':
$D('connectButton').style.display = "";
$D('disconnectButton').style.display = "none";
break;
default:
$D('connectButton').style.display = "none";
$D('disconnectButton').style.display = "";
break;
}
//Util.Debug("<< updateVisualState");
},
clipReceive: function(rfb, text) {
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
$D('noVNC_clipboard_text').value = text;
Util.Debug("<< UI.clipReceive");
},
connect: function() {
var host, port, password, path;
UI.closeSettingsMenu();
UI.toggleConnectPanel();
host = $D('noVNC_host').value;
port = $D('noVNC_port').value;
password = $D('noVNC_password').value;
path = $D('noVNC_path').value;
if ((!host) || (!port)) {
throw("Must set host and port");
}
UI.rfb.set_encrypt(UI.getSetting('encrypt'));
UI.rfb.set_true_color(UI.getSetting('true_color'));
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
UI.rfb.set_shared(UI.getSetting('shared'));
UI.rfb.set_view_only(UI.getSetting('view_only'));
UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
UI.rfb.connect(host, port, password, path);
//Close dialog.
setTimeout(UI.setBarPosition, 100);
$D('noVNC_logo').style.display = "none";
},
disconnect: function() {
UI.closeSettingsMenu();
UI.rfb.disconnect();
$D('noVNC_logo').style.display = "block";
UI.connSettingsOpen = false;
UI.toggleConnectPanel();
},
displayBlur: function() {
UI.rfb.get_keyboard().set_focused(false);
UI.rfb.get_mouse().set_focused(false);
},
displayFocus: function() {
UI.rfb.get_keyboard().set_focused(true);
UI.rfb.get_mouse().set_focused(true);
},
clipClear: function() {
$D('noVNC_clipboard_text').value = "";
UI.rfb.clipboardPasteFrom("");
},
clipSend: function() {
var text = $D('noVNC_clipboard_text').value;
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
UI.rfb.clipboardPasteFrom(text);
Util.Debug("<< UI.clipSend");
},
// Enable/disable and configure viewport clipping
setViewClip: function(clip) {
var display, cur_clip, pos, new_w, new_h;
if (UI.rfb) {
display = UI.rfb.get_display();
} else {
return;
}
cur_clip = display.get_viewport();
if (typeof(clip) !== 'boolean') {
// Use current setting
clip = UI.getSetting('clip');
}
if (clip && !cur_clip) {
// Turn clipping on
UI.updateSetting('clip', true);
} else if (!clip && cur_clip) {
// Turn clipping off
UI.updateSetting('clip', false);
display.set_viewport(false);
$D('noVNC_canvas').style.position = 'static';
display.viewportChange();
}
if (UI.getSetting('clip')) {
// If clipping, update clipping settings
$D('noVNC_canvas').style.position = 'absolute';
pos = Util.getPosition($D('noVNC_canvas'));
new_w = window.innerWidth - pos.x;
new_h = window.innerHeight - pos.y;
display.set_viewport(true);
display.viewportChange(0, 0, new_w, new_h);
}
},
// Toggle/set/unset the viewport drag/move button
setViewDrag: function(drag) {
var vmb = $D('noVNC_view_drag_button');
if (!UI.rfb) { return; }
if (UI.rfb_state === 'normal' &&
UI.rfb.get_display().get_viewport()) {
vmb.style.display = "inline";
} else {
vmb.style.display = "none";
}
if (typeof(drag) === "undefined") {
// If not specified, then toggle
drag = !UI.rfb.get_viewportDrag();
}
if (drag) {
vmb.className = "noVNC_status_button_selected";
UI.rfb.set_viewportDrag(true);
} else {
vmb.className = "noVNC_status_button";
UI.rfb.set_viewportDrag(false);
}
},
// On touch devices, show the OS keyboard
showKeyboard: function() {
if(UI.keyboardVisible === false) {
$D('keyboardinput').focus();
UI.keyboardVisible = true;
$D('showKeyboard').className = "noVNC_status_button_selected";
} else if(UI.keyboardVisible === true) {
$D('keyboardinput').blur();
$D('showKeyboard').className = "noVNC_status_button";
UI.keyboardVisible = false;
}
},
keyInputBlur: function() {
$D('showKeyboard').className = "noVNC_status_button";
//Weird bug in iOS if you change keyboardVisible
//here it does not actually occur so next time
//you click keyboard icon it doesnt work.
setTimeout(function() { UI.setKeyboard(); },100);
},
setKeyboard: function() {
UI.keyboardVisible = false;
},
// iOS < Version 5 does not support position fixed. Javascript workaround:
setOnscroll: function() {
window.onscroll = function() {
UI.setBarPosition();
};
},
setResize: function () { xvpShutdown: function() {
window.onResize = function() { UI.rfb.xvpShutdown();
UI.setBarPosition(); },
};
},
//Helper to add options to dropdown. xvpReboot: function() {
addOption: function(selectbox,text,value ) UI.rfb.xvpReboot();
{ },
var optn = document.createElement("OPTION");
optn.text = text;
optn.value = value;
selectbox.options.add(optn);
},
setBarPosition: function() { xvpReset: function() {
$D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px'; UI.rfb.xvpReset();
$D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; },
var vncwidth = $D('noVNC_screen').style.offsetWidth; setMouseButton: function(num) {
$D('noVNC-control-bar').style.width = vncwidth + 'px'; if (typeof num === 'undefined') {
} // Disable mouse buttons
num = -1;
}
if (UI.rfb) {
UI.rfb.get_mouse().set_touchButton(num);
}
}; var blist = [0, 1,2,4];
for (var b = 0; b < blist.length; b++) {
var button = $D('noVNC_mouse_button' + blist[b]);
if (blist[b] === num) {
button.style.display = "";
} else {
button.style.display = "none";
}
}
},
updateState: function(rfb, state, oldstate, msg) {
UI.rfb_state = state;
var klass;
switch (state) {
case 'failed':
case 'fatal':
klass = "noVNC_status_error";
break;
case 'normal':
klass = "noVNC_status_normal";
break;
case 'disconnected':
$D('noVNC_logo').style.display = "block";
/* falls through */
case 'loaded':
klass = "noVNC_status_normal";
break;
case 'password':
UI.toggleConnectPanel();
$D('noVNC_connect_button').value = "Send Password";
$D('noVNC_connect_button').onclick = UI.setPassword;
$D('noVNC_password').focus();
klass = "noVNC_status_warn";
break;
default:
klass = "noVNC_status_warn";
break;
}
if (typeof(msg) !== 'undefined') {
$D('noVNC-control-bar').setAttribute("class", klass);
$D('noVNC_status').innerHTML = msg;
}
UI.updateVisualState();
},
// Disable/enable controls depending on connection state
updateVisualState: function() {
var connected = UI.rfb_state === 'normal' ? true : false;
//Util.Debug(">> updateVisualState");
$D('noVNC_encrypt').disabled = connected;
$D('noVNC_true_color').disabled = connected;
if (UI.rfb && UI.rfb.get_display() &&
UI.rfb.get_display().get_cursor_uri()) {
$D('noVNC_cursor').disabled = connected;
} else {
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
}
$D('noVNC_shared').disabled = connected;
$D('noVNC_view_only').disabled = connected;
$D('noVNC_path').disabled = connected;
$D('noVNC_repeaterID').disabled = connected;
if (connected) {
UI.setViewClip();
UI.setMouseButton(1);
$D('clipboardButton').style.display = "inline";
$D('showKeyboard').style.display = "inline";
$D('noVNC_extra_keys').style.display = "";
$D('sendCtrlAltDelButton').style.display = "inline";
} else {
UI.setMouseButton();
$D('clipboardButton').style.display = "none";
$D('showKeyboard').style.display = "none";
$D('noVNC_extra_keys').style.display = "none";
$D('sendCtrlAltDelButton').style.display = "none";
UI.updateXvpVisualState(0);
}
// State change disables viewport dragging.
// It is enabled (toggled) by direct click on the button
UI.setViewDrag(false);
switch (UI.rfb_state) {
case 'fatal':
case 'failed':
case 'loaded':
case 'disconnected':
$D('connectButton').style.display = "";
$D('disconnectButton').style.display = "none";
break;
default:
$D('connectButton').style.display = "none";
$D('disconnectButton').style.display = "";
break;
}
//Util.Debug("<< updateVisualState");
},
// Disable/enable XVP button
updateXvpVisualState: function(ver) {
if (ver >= 1) {
$D('xvpButton').style.display = 'inline';
} else {
$D('xvpButton').style.display = 'none';
// Close XVP panel if open
if (UI.xvpOpen === true) {
UI.toggleXvpPanel();
}
}
},
// Display the desktop name in the document title
updateDocumentTitle: function(rfb, name) {
document.title = name + " - noVNC";
},
clipReceive: function(rfb, text) {
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
$D('noVNC_clipboard_text').value = text;
Util.Debug("<< UI.clipReceive");
},
connect: function() {
UI.closeSettingsMenu();
UI.toggleConnectPanel();
var host = $D('noVNC_host').value;
var port = $D('noVNC_port').value;
var password = $D('noVNC_password').value;
var path = $D('noVNC_path').value;
if ((!host) || (!port)) {
throw new Error("Must set host and port");
}
UI.rfb.set_encrypt(UI.getSetting('encrypt'));
UI.rfb.set_true_color(UI.getSetting('true_color'));
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
UI.rfb.set_shared(UI.getSetting('shared'));
UI.rfb.set_view_only(UI.getSetting('view_only'));
UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
UI.rfb.connect(host, port, password, path);
//Close dialog.
setTimeout(UI.setBarPosition, 100);
$D('noVNC_logo').style.display = "none";
},
disconnect: function() {
UI.closeSettingsMenu();
UI.rfb.disconnect();
$D('noVNC_logo').style.display = "block";
UI.connSettingsOpen = false;
UI.toggleConnectPanel();
},
displayBlur: function() {
UI.rfb.get_keyboard().set_focused(false);
UI.rfb.get_mouse().set_focused(false);
},
displayFocus: function() {
UI.rfb.get_keyboard().set_focused(true);
UI.rfb.get_mouse().set_focused(true);
},
clipClear: function() {
$D('noVNC_clipboard_text').value = "";
UI.rfb.clipboardPasteFrom("");
},
clipSend: function() {
var text = $D('noVNC_clipboard_text').value;
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
UI.rfb.clipboardPasteFrom(text);
Util.Debug("<< UI.clipSend");
},
// Enable/disable and configure viewport clipping
setViewClip: function(clip) {
var display;
if (UI.rfb) {
display = UI.rfb.get_display();
} else {
return;
}
var cur_clip = display.get_viewport();
if (typeof(clip) !== 'boolean') {
// Use current setting
clip = UI.getSetting('clip');
}
if (clip && !cur_clip) {
// Turn clipping on
UI.updateSetting('clip', true);
} else if (!clip && cur_clip) {
// Turn clipping off
UI.updateSetting('clip', false);
display.set_viewport(false);
$D('noVNC_canvas').style.position = 'static';
display.viewportChange();
}
if (UI.getSetting('clip')) {
// If clipping, update clipping settings
$D('noVNC_canvas').style.position = 'absolute';
var pos = Util.getPosition($D('noVNC_canvas'));
var new_w = window.innerWidth - pos.x;
var new_h = window.innerHeight - pos.y;
display.set_viewport(true);
display.viewportChange(0, 0, new_w, new_h);
}
},
// Toggle/set/unset the viewport drag/move button
setViewDrag: function(drag) {
var vmb = $D('noVNC_view_drag_button');
if (!UI.rfb) { return; }
if (UI.rfb_state === 'normal' &&
UI.rfb.get_display().get_viewport()) {
vmb.style.display = "inline";
} else {
vmb.style.display = "none";
}
if (typeof(drag) === "undefined" ||
typeof(drag) === "object") {
// If not specified, then toggle
drag = !UI.rfb.get_viewportDrag();
}
if (drag) {
vmb.className = "noVNC_status_button_selected";
UI.rfb.set_viewportDrag(true);
} else {
vmb.className = "noVNC_status_button";
UI.rfb.set_viewportDrag(false);
}
},
// On touch devices, show the OS keyboard
showKeyboard: function() {
var kbi = $D('keyboardinput');
var skb = $D('showKeyboard');
var l = kbi.value.length;
if(UI.keyboardVisible === false) {
kbi.focus();
try { kbi.setSelectionRange(l, l); } // Move the caret to the end
catch (err) {} // setSelectionRange is undefined in Google Chrome
UI.keyboardVisible = true;
skb.className = "noVNC_status_button_selected";
} else if(UI.keyboardVisible === true) {
kbi.blur();
skb.className = "noVNC_status_button";
UI.keyboardVisible = false;
}
},
keepKeyboard: function() {
clearTimeout(UI.hideKeyboardTimeout);
if(UI.keyboardVisible === true) {
$D('keyboardinput').focus();
$D('showKeyboard').className = "noVNC_status_button_selected";
} else if(UI.keyboardVisible === false) {
$D('keyboardinput').blur();
$D('showKeyboard').className = "noVNC_status_button";
}
},
keyboardinputReset: function() {
var kbi = $D('keyboardinput');
kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
UI.lastKeyboardinput = kbi.value;
},
// When normal keyboard events are left uncought, use the input events from
// the keyboardinput element instead and generate the corresponding key events.
// This code is required since some browsers on Android are inconsistent in
// sending keyCodes in the normal keyboard events when using on screen keyboards.
keyInput: function(event) {
var newValue = event.target.value;
var oldValue = UI.lastKeyboardinput;
var newLen;
try {
// Try to check caret position since whitespace at the end
// will not be considered by value.length in some browsers
newLen = Math.max(event.target.selectionStart, newValue.length);
} catch (err) {
// selectionStart is undefined in Google Chrome
newLen = newValue.length;
}
var oldLen = oldValue.length;
var backspaces;
var inputs = newLen - oldLen;
if (inputs < 0) {
backspaces = -inputs;
} else {
backspaces = 0;
}
// Compare the old string with the new to account for
// text-corrections or other input that modify existing text
var i;
for (i = 0; i < Math.min(oldLen, newLen); i++) {
if (newValue.charAt(i) != oldValue.charAt(i)) {
inputs = newLen - i;
backspaces = oldLen - i;
break;
}
}
// Send the key events
for (i = 0; i < backspaces; i++) {
UI.rfb.sendKey(XK_BackSpace);
}
for (i = newLen - inputs; i < newLen; i++) {
UI.rfb.sendKey(newValue.charCodeAt(i));
}
// Control the text content length in the keyboardinput element
if (newLen > 2 * UI.defaultKeyboardinputLen) {
UI.keyboardinputReset();
} else if (newLen < 1) {
// There always have to be some text in the keyboardinput
// element with which backspace can interact.
UI.keyboardinputReset();
// This sometimes causes the keyboard to disappear for a second
// but it is required for the android keyboard to recognize that
// text has been added to the field
event.target.blur();
// This has to be ran outside of the input handler in order to work
setTimeout(function() { UI.keepKeyboard(); }, 0);
} else {
UI.lastKeyboardinput = newValue;
}
},
keyInputBlur: function() {
$D('showKeyboard').className = "noVNC_status_button";
//Weird bug in iOS if you change keyboardVisible
//here it does not actually occur so next time
//you click keyboard icon it doesnt work.
UI.hideKeyboardTimeout = setTimeout(function() { UI.setKeyboard(); },100);
},
showExtraKeys: function() {
UI.keepKeyboard();
if(UI.extraKeysVisible === false) {
$D('toggleCtrlButton').style.display = "inline";
$D('toggleAltButton').style.display = "inline";
$D('sendTabButton').style.display = "inline";
$D('sendEscButton').style.display = "inline";
$D('showExtraKeysButton').className = "noVNC_status_button_selected";
UI.extraKeysVisible = true;
} else if(UI.extraKeysVisible === true) {
$D('toggleCtrlButton').style.display = "";
$D('toggleAltButton').style.display = "";
$D('sendTabButton').style.display = "";
$D('sendEscButton').style.display = "";
$D('showExtraKeysButton').className = "noVNC_status_button";
UI.extraKeysVisible = false;
}
},
toggleCtrl: function() {
UI.keepKeyboard();
if(UI.ctrlOn === false) {
UI.rfb.sendKey(XK_Control_L, true);
$D('toggleCtrlButton').className = "noVNC_status_button_selected";
UI.ctrlOn = true;
} else if(UI.ctrlOn === true) {
UI.rfb.sendKey(XK_Control_L, false);
$D('toggleCtrlButton').className = "noVNC_status_button";
UI.ctrlOn = false;
}
},
toggleAlt: function() {
UI.keepKeyboard();
if(UI.altOn === false) {
UI.rfb.sendKey(XK_Alt_L, true);
$D('toggleAltButton').className = "noVNC_status_button_selected";
UI.altOn = true;
} else if(UI.altOn === true) {
UI.rfb.sendKey(XK_Alt_L, false);
$D('toggleAltButton').className = "noVNC_status_button";
UI.altOn = false;
}
},
sendTab: function() {
UI.keepKeyboard();
UI.rfb.sendKey(XK_Tab);
},
sendEsc: function() {
UI.keepKeyboard();
UI.rfb.sendKey(XK_Escape);
},
setKeyboard: function() {
UI.keyboardVisible = false;
},
// iOS < Version 5 does not support position fixed. Javascript workaround:
setOnscroll: function() {
window.onscroll = function() {
UI.setBarPosition();
};
},
setResize: function () {
window.onResize = function() {
UI.setBarPosition();
};
},
//Helper to add options to dropdown.
addOption: function(selectbox, text, value) {
var optn = document.createElement("OPTION");
optn.text = text;
optn.value = value;
selectbox.options.add(optn);
},
setBarPosition: function() {
$D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
$D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
var vncwidth = $D('noVNC_screen').style.offsetWidth;
$D('noVNC-control-bar').style.width = vncwidth + 'px';
}
};
})();
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
"use strict"; /* jshint white: false, nonstandard: true */
/*jslint bitwise: false, white: false */ /*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
/*global window, console, document, navigator, ActiveXObject */
// Globals defined here // Globals defined here
var Util = {}; var Util = {};
...@@ -18,61 +17,163 @@ var Util = {}; ...@@ -18,61 +17,163 @@ var Util = {};
* Make arrays quack * Make arrays quack
*/ */
Array.prototype.push8 = function (num) { var addFunc = function (cl, name, func) {
this.push(num & 0xFF); if (!cl.prototype[name]) {
Object.defineProperty(cl.prototype, name, { enumerable: false, value: func });
}
}; };
Array.prototype.push16 = function (num) { addFunc(Array, 'push8', function (num) {
"use strict";
this.push(num & 0xFF);
});
addFunc(Array, 'push16', function (num) {
"use strict";
this.push((num >> 8) & 0xFF, this.push((num >> 8) & 0xFF,
(num ) & 0xFF ); num & 0xFF);
}; });
Array.prototype.push32 = function (num) {
addFunc(Array, 'push32', function (num) {
"use strict";
this.push((num >> 24) & 0xFF, this.push((num >> 24) & 0xFF,
(num >> 16) & 0xFF, (num >> 16) & 0xFF,
(num >> 8) & 0xFF, (num >> 8) & 0xFF,
(num ) & 0xFF ); num & 0xFF);
}; });
// IE does not support map (even in IE9) // IE does not support map (even in IE9)
//This prototype is provided by the Mozilla foundation and //This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license. //is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.map) addFunc(Array, 'map', function (fun /*, thisp*/) {
{ "use strict";
Array.prototype.map = function(fun /*, thisp*/)
{
var len = this.length; var len = this.length;
if (typeof fun != "function") if (typeof fun != "function") {
throw new TypeError(); throw new TypeError();
}
var res = new Array(len); var res = new Array(len);
var thisp = arguments[1]; var thisp = arguments[1];
for (var i = 0; i < len; i++) for (var i = 0; i < len; i++) {
{ if (i in this) {
if (i in this) res[i] = fun.call(thisp, this[i], i, this);
res[i] = fun.call(thisp, this[i], i, this); }
} }
return res; return res;
}; });
// IE <9 does not support indexOf
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
addFunc(Array, 'indexOf', function (elt /*, from*/) {
"use strict";
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
if (from < 0) {
from += len;
}
for (; from < len; from++) {
if (from in this &&
this[from] === elt) {
return from;
}
}
return -1;
});
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function () {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
})();
} }
// // PhantomJS 1.x doesn't support bind,
// so leave this in until PhantomJS 2.0 is released
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
addFunc(Function, 'bind', function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - " +
"what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis ? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
});
//
// requestAnimationFrame shim with setTimeout fallback // requestAnimationFrame shim with setTimeout fallback
// //
window.requestAnimFrame = (function(){ window.requestAnimFrame = (function () {
return window.requestAnimationFrame || "use strict";
window.webkitRequestAnimationFrame || return window.requestAnimationFrame ||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.oRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame || window.oRequestAnimationFrame ||
function(callback){ window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60); window.setTimeout(callback, 1000 / 60);
}; };
})(); })();
/* /*
* ------------------------------------------------------ * ------------------------------------------------------
* Namespaced in Util * Namespaced in Util
* ------------------------------------------------------ * ------------------------------------------------------
...@@ -84,6 +185,7 @@ window.requestAnimFrame = (function(){ ...@@ -84,6 +185,7 @@ window.requestAnimFrame = (function(){
Util._log_level = 'warn'; Util._log_level = 'warn';
Util.init_logging = function (level) { Util.init_logging = function (level) {
"use strict";
if (typeof level === 'undefined') { if (typeof level === 'undefined') {
level = Util._log_level; level = Util._log_level;
} else { } else {
...@@ -94,26 +196,34 @@ Util.init_logging = function (level) { ...@@ -94,26 +196,34 @@ Util.init_logging = function (level) {
window.console = { window.console = {
'log' : window.opera.postError, 'log' : window.opera.postError,
'warn' : window.opera.postError, 'warn' : window.opera.postError,
'error': window.opera.postError }; 'error': window.opera.postError
};
} else { } else {
window.console = { window.console = {
'log' : function(m) {}, 'log' : function (m) {},
'warn' : function(m) {}, 'warn' : function (m) {},
'error': function(m) {}}; 'error': function (m) {}
};
} }
} }
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
/* jshint -W086 */
switch (level) { switch (level) {
case 'debug': Util.Debug = function (msg) { console.log(msg); }; case 'debug':
case 'info': Util.Info = function (msg) { console.log(msg); }; Util.Debug = function (msg) { console.log(msg); };
case 'warn': Util.Warn = function (msg) { console.warn(msg); }; case 'info':
case 'error': Util.Error = function (msg) { console.error(msg); }; Util.Info = function (msg) { console.log(msg); };
case 'warn':
Util.Warn = function (msg) { console.warn(msg); };
case 'error':
Util.Error = function (msg) { console.error(msg); };
case 'none': case 'none':
break; break;
default: default:
throw("invalid logging type '" + level + "'"); throw new Error("invalid logging type '" + level + "'");
} }
/* jshint +W086 */
}; };
Util.get_logging = function () { Util.get_logging = function () {
return Util._log_level; return Util._log_level;
...@@ -121,107 +231,279 @@ Util.get_logging = function () { ...@@ -121,107 +231,279 @@ Util.get_logging = function () {
// Initialize logging level // Initialize logging level
Util.init_logging(); Util.init_logging();
Util.make_property = function (proto, name, mode, type) {
"use strict";
// Set configuration default for Crockford style function namespaces var getter;
Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { if (type === 'arr') {
var getter, setter; getter = function (idx) {
if (typeof idx !== 'undefined') {
return this['_' + name][idx];
} else {
return this['_' + name];
}
};
} else {
getter = function () {
return this['_' + name];
};
}
// Default getter function var make_setter = function (process_val) {
getter = function (idx) { if (process_val) {
if ((type in {'arr':1, 'array':1}) && return function (val, idx) {
(typeof idx !== 'undefined')) { if (typeof idx !== 'undefined') {
return cfg[v][idx]; this['_' + name][idx] = process_val(val);
} else {
this['_' + name] = process_val(val);
}
};
} else { } else {
return cfg[v]; return function (val, idx) {
if (typeof idx !== 'undefined') {
this['_' + name][idx] = val;
} else {
this['_' + name] = val;
}
};
} }
}; };
// Default setter function var setter;
setter = function (val, idx) { if (type === 'bool') {
if (type in {'boolean':1, 'bool':1}) { setter = make_setter(function (val) {
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
val = false; return false;
} else { } else {
val = true; return true;
} }
} else if (type in {'integer':1, 'int':1}) { });
val = parseInt(val, 10); } else if (type === 'int') {
} else if (type === 'str') { setter = make_setter(function (val) { return parseInt(val, 10); });
val = String(val); } else if (type === 'float') {
} else if (type === 'func') { setter = make_setter(parseFloat);
} else if (type === 'str') {
setter = make_setter(String);
} else if (type === 'func') {
setter = make_setter(function (val) {
if (!val) { if (!val) {
val = function () {}; return function () {};
} else {
return val;
} }
} });
if (typeof idx !== 'undefined') { } else if (type === 'arr' || type === 'dom' || type == 'raw') {
cfg[v][idx] = val; setter = make_setter();
} else { } else {
cfg[v] = val; throw new Error('Unknown property type ' + type); // some sanity checking
} }
};
// Set the description
api[v + '_description'] = desc;
// Set the getter function // set the getter
if (typeof api['get_' + v] === 'undefined') { if (typeof proto['get_' + name] === 'undefined') {
api['get_' + v] = getter; proto['get_' + name] = getter;
} }
// Set the setter function with extra sanity checks // set the setter if needed
if (typeof api['set_' + v] === 'undefined') { if (typeof proto['set_' + name] === 'undefined') {
api['set_' + v] = function (val, idx) { if (mode === 'rw') {
if (mode in {'RO':1, 'ro':1}) { proto['set_' + name] = setter;
throw(v + " is read-only"); } else if (mode === 'wo') {
} else if ((mode in {'WO':1, 'wo':1}) && proto['set_' + name] = function (val, idx) {
(typeof cfg[v] !== 'undefined')) { if (typeof this['_' + name] !== 'undefined') {
throw(v + " can only be set once"); throw new Error(name + " can only be set once");
} }
setter(val, idx); setter.call(this, val, idx);
}; };
}
} }
// Set the default value // make a special setter that we can use in set defaults
if (typeof defaults[v] !== 'undefined') { proto['_raw_set_' + name] = function (val, idx) {
defval = defaults[v]; setter.call(this, val, idx);
} else if ((type in {'arr':1, 'array':1}) && //delete this['_init_set_' + name]; // remove it after use
(! (defval instanceof Array))) { };
defval = []; };
Util.make_properties = function (constructor, arr) {
"use strict";
for (var i = 0; i < arr.length; i++) {
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
} }
// Coerce existing setting to the right type
//Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
setter(defval);
}; };
// Set group of configuration defaults Util.set_defaults = function (obj, conf, defaults) {
Util.conf_defaults = function(cfg, api, defaults, arr) { var defaults_keys = Object.keys(defaults);
var conf_keys = Object.keys(conf);
var keys_obj = {};
var i; var i;
for (i = 0; i < arr.length; i++) { for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
arr[i][2], arr[i][3], arr[i][4]); var keys = Object.keys(keys_obj);
for (i = 0; i < keys.length; i++) {
var setter = obj['_raw_set_' + keys[i]];
if (!setter) {
Util.Warn('Invalid property ' + keys[i]);
continue;
}
if (keys[i] in conf) {
setter.call(obj, conf[keys[i]]);
} else {
setter.call(obj, defaults[keys[i]]);
}
} }
}; };
/*
* Decode from UTF-8
*/
Util.decodeUTF8 = function (utf8string) {
"use strict";
return decodeURIComponent(escape(utf8string));
};
/* /*
* Cross-browser routines * Cross-browser routines
*/ */
// Get DOM element position on page
Util.getPosition = function (obj) { // Dynamically load scripts without using document.write()
var x = 0, y = 0; // Reference: http://unixpapa.com/js/dyna.html
if (obj.offsetParent) { //
do { // Handles the case where load_scripts is invoked from a script that
x += obj.offsetLeft; // itself is loaded via load_scripts. Once all scripts are loaded the
y += obj.offsetTop; // window.onscriptsloaded handler is called (if set).
obj = obj.offsetParent; Util.get_include_uri = function () {
} while (obj); return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
};
Util._loading_scripts = [];
Util._pending_scripts = [];
Util.load_scripts = function (files) {
"use strict";
var head = document.getElementsByTagName('head')[0], script,
ls = Util._loading_scripts, ps = Util._pending_scripts;
var loadFunc = function (e) {
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
ls[0].readyState === 'complete')) {
// For IE, append the script to trigger execution
var s = ls.shift();
//console.log("loaded script: " + s.src);
head.appendChild(s);
}
if (!this.readyState ||
(Util.Engine.presto && this.readyState === 'loaded') ||
this.readyState === 'complete') {
if (ps.indexOf(this) >= 0) {
this.onload = this.onreadystatechange = null;
//console.log("completed script: " + this.src);
ps.splice(ps.indexOf(this), 1);
// Call window.onscriptsload after last script loads
if (ps.length === 0 && window.onscriptsload) {
window.onscriptsload();
}
}
}
};
for (var f = 0; f < files.length; f++) {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = Util.get_include_uri() + files[f];
//console.log("loading script: " + script.src);
script.onload = script.onreadystatechange = loadFunc;
// In-order script execution tricks
if (Util.Engine.trident) {
// For IE wait until readyState is 'loaded' before
// appending it which will trigger execution
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
ls.push(script);
} else {
// For webkit and firefox set async=false and append now
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
script.async = false;
head.appendChild(script);
}
ps.push(script);
} }
return {'x': x, 'y': y};
}; };
// Get DOM element position on page
// This solution is based based on http://www.greywyvern.com/?post=331
// Thanks to Brian Huisman AKA GreyWyvern!
Util.getPosition = (function () {
"use strict";
function getStyle(obj, styleProp) {
var y;
if (obj.currentStyle) {
y = obj.currentStyle[styleProp];
} else if (window.getComputedStyle)
y = window.getComputedStyle(obj, null)[styleProp];
return y;
}
function scrollDist() {
var myScrollTop = 0, myScrollLeft = 0;
var html = document.getElementsByTagName('html')[0];
// get the scrollTop part
if (html.scrollTop && document.documentElement.scrollTop) {
myScrollTop = html.scrollTop;
} else if (html.scrollTop || document.documentElement.scrollTop) {
myScrollTop = html.scrollTop + document.documentElement.scrollTop;
} else if (document.body.scrollTop) {
myScrollTop = document.body.scrollTop;
} else {
myScrollTop = 0;
}
// get the scrollLeft part
if (html.scrollLeft && document.documentElement.scrollLeft) {
myScrollLeft = html.scrollLeft;
} else if (html.scrollLeft || document.documentElement.scrollLeft) {
myScrollLeft = html.scrollLeft + document.documentElement.scrollLeft;
} else if (document.body.scrollLeft) {
myScrollLeft = document.body.scrollLeft;
} else {
myScrollLeft = 0;
}
return [myScrollLeft, myScrollTop];
}
return function (obj) {
var curleft = 0, curtop = 0, scr = obj, fixed = false;
while ((scr = scr.parentNode) && scr != document.body) {
curleft -= scr.scrollLeft || 0;
curtop -= scr.scrollTop || 0;
if (getStyle(scr, "position") == "fixed") {
fixed = true;
}
}
if (fixed && !window.opera) {
var scrDist = scrollDist();
curleft += scrDist[0];
curtop += scrDist[1];
}
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while ((obj = obj.offsetParent));
return {'x': curleft, 'y': curtop};
};
})();
// Get mouse event position in DOM element // Get mouse event position in DOM element
Util.getEventPosition = function (e, obj, scale) { Util.getEventPosition = function (e, obj, scale) {
"use strict";
var evt, docX, docY, pos; var evt, docX, docY, pos;
//if (!e) evt = window.event; //if (!e) evt = window.event;
evt = (e ? e : window.event); evt = (e ? e : window.event);
...@@ -239,36 +521,43 @@ Util.getEventPosition = function (e, obj, scale) { ...@@ -239,36 +521,43 @@ Util.getEventPosition = function (e, obj, scale) {
if (typeof scale === "undefined") { if (typeof scale === "undefined") {
scale = 1; scale = 1;
} }
return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; var realx = docX - pos.x;
var realy = docY - pos.y;
var x = Math.max(Math.min(realx, obj.width - 1), 0);
var y = Math.max(Math.min(realy, obj.height - 1), 0);
return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale};
}; };
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
Util.addEvent = function (obj, evType, fn){ Util.addEvent = function (obj, evType, fn) {
if (obj.attachEvent){ "use strict";
var r = obj.attachEvent("on"+evType, fn); if (obj.attachEvent) {
var r = obj.attachEvent("on" + evType, fn);
return r; return r;
} else if (obj.addEventListener){ } else if (obj.addEventListener) {
obj.addEventListener(evType, fn, false); obj.addEventListener(evType, fn, false);
return true; return true;
} else { } else {
throw("Handler could not be attached"); throw new Error("Handler could not be attached");
} }
}; };
Util.removeEvent = function(obj, evType, fn){ Util.removeEvent = function (obj, evType, fn) {
if (obj.detachEvent){ "use strict";
var r = obj.detachEvent("on"+evType, fn); if (obj.detachEvent) {
var r = obj.detachEvent("on" + evType, fn);
return r; return r;
} else if (obj.removeEventListener){ } else if (obj.removeEventListener) {
obj.removeEventListener(evType, fn, false); obj.removeEventListener(evType, fn, false);
return true; return true;
} else { } else {
throw("Handler could not be removed"); throw new Error("Handler could not be removed");
} }
}; };
Util.stopEvent = function(e) { Util.stopEvent = function (e) {
"use strict";
if (e.stopPropagation) { e.stopPropagation(); } if (e.stopPropagation) { e.stopPropagation(); }
else { e.cancelBubble = true; } else { e.cancelBubble = true; }
...@@ -280,41 +569,88 @@ Util.stopEvent = function(e) { ...@@ -280,41 +569,88 @@ Util.stopEvent = function(e) {
// Set browser engine versions. Based on mootools. // Set browser engine versions. Based on mootools.
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
Util.Engine = { (function () {
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference) "use strict";
//'presto': (function() { // 'presto': (function () { return (!window.opera) ? false : true; }()),
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), var detectPresto = function () {
'presto': (function() { return (!window.opera) ? false : true; }()), return !!window.opera;
};
'trident': (function() {
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), // 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
'webkit': (function() { var detectTrident = function () {
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), if (!window.ActiveXObject) {
//'webkit': (function() { return false;
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), } else {
'gecko': (function() { if (window.XMLHttpRequest) {
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) return (document.querySelectorAll) ? 6 : 5;
}; } else {
if (Util.Engine.webkit) { return 4;
// Extract actual webkit version if available }
Util.Engine.webkit = (function(v) { }
var re = new RegExp('WebKit/([0-9\.]*) '); };
v = (navigator.userAgent.match(re) || ['', v])[1];
return parseFloat(v, 10); // 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
})(Util.Engine.webkit); var detectInitialWebkit = function () {
} try {
if (navigator.taintEnabled) {
return false;
} else {
if (Util.Features.xpath) {
return (Util.Features.query) ? 525 : 420;
} else {
return 419;
}
}
} catch (e) {
return false;
}
};
var detectActualWebkit = function (initial_ver) {
var re = /WebKit\/([0-9\.]*) /;
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
return parseFloat(str_ver, 10);
};
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
var detectGecko = function () {
/* jshint -W041 */
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
return false;
} else {
return (document.getElementsByClassName) ? 19 : 18;
}
/* jshint +W041 */
};
Util.Engine = {
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
//'presto': (function() {
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
'presto': detectPresto(),
'trident': detectTrident(),
'webkit': detectInitialWebkit(),
'gecko': detectGecko(),
};
if (Util.Engine.webkit) {
// Extract actual webkit version if available
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
}
})();
Util.Flash = (function(){ Util.Flash = (function () {
"use strict";
var v, version; var v, version;
try { try {
v = navigator.plugins['Shockwave Flash'].description; v = navigator.plugins['Shockwave Flash'].description;
} catch(err1) { } catch (err1) {
try { try {
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
} catch(err2) { } catch (err2) {
v = '0 r0'; v = '0 r0';
} }
} }
version = v.match(/\d+/g); version = v.match(/\d+/g);
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
}()); }());
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/> // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
// License: New BSD License // License: New BSD License
// Reference: http://dev.w3.org/html5/websockets/ // Reference: http://dev.w3.org/html5/websockets/
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol // Reference: http://tools.ietf.org/html/rfc6455
(function() { (function() {
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) return; if (window.WEB_SOCKET_FORCE_FLASH) {
// Keeps going.
var console = window.console; } else if (window.WebSocket) {
if (!console || !console.log || !console.error) { return;
console = {log: function(){ }, error: function(){ }}; } else if (window.MozWebSocket) {
// Firefox.
window.WebSocket = MozWebSocket;
return;
} }
if (!swfobject.hasFlashPlayerVersion("10.0.0")) { var logger;
console.error("Flash Player >= 10.0.0 is required."); if (window.WEB_SOCKET_LOGGER) {
logger = WEB_SOCKET_LOGGER;
} else if (window.console && window.console.log && window.console.error) {
// In some environment, console is defined but console.log or console.error is missing.
logger = window.console;
} else {
logger = {log: function(){ }, error: function(){ }};
}
// swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
if (swfobject.getFlashPlayerVersion().major < 10) {
logger.error("Flash Player >= 10.0.0 is required.");
return; return;
} }
if (location.protocol == "file:") { if (location.protocol == "file:") {
console.error( logger.error(
"WARNING: web-socket-js doesn't work in file:///... URL " + "WARNING: web-socket-js doesn't work in file:///... URL " +
"unless you set Flash Security Settings properly. " + "unless you set Flash Security Settings properly. " +
"Open the page via Web server i.e. http://..."); "Open the page via Web server i.e. http://...");
} }
/** /**
* This class represents a faux web socket. * Our own implementation of WebSocket class using Flash.
* @param {string} url * @param {string} url
* @param {string} protocol * @param {array or string} protocols
* @param {string} proxyHost * @param {string} proxyHost
* @param {int} proxyPort * @param {int} proxyPort
* @param {string} headers * @param {string} headers
*/ */
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) { window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this; var self = this;
self.__id = WebSocket.__nextId++; self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self; WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING; self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0; self.bufferedAmount = 0;
self.__events = {}; self.__events = {};
if (!protocols) {
protocols = [];
} else if (typeof protocols == "string") {
protocols = [protocols];
}
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
// Otherwise, when onopen fires immediately, onopen is called before it is set. // Otherwise, when onopen fires immediately, onopen is called before it is set.
setTimeout(function() { self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() { WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create( WebSocket.__flash.create(
self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null); self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
}); });
}, 0); }, 0);
}; };
...@@ -78,6 +98,12 @@ ...@@ -78,6 +98,12 @@
* Close this web socket gracefully. * Close this web socket gracefully.
*/ */
WebSocket.prototype.close = function() { WebSocket.prototype.close = function() {
if (this.__createTask) {
clearTimeout(this.__createTask);
this.__createTask = null;
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
return; return;
} }
...@@ -131,7 +157,7 @@ ...@@ -131,7 +157,7 @@
events[i](event); events[i](event);
} }
var handler = this["on" + event.type]; var handler = this["on" + event.type];
if (handler) handler(event); if (handler) handler.apply(this, [event]);
}; };
/** /**
...@@ -139,16 +165,22 @@ ...@@ -139,16 +165,22 @@
* @param {Object} flashEvent * @param {Object} flashEvent
*/ */
WebSocket.prototype.__handleEvent = function(flashEvent) { WebSocket.prototype.__handleEvent = function(flashEvent) {
if ("readyState" in flashEvent) { if ("readyState" in flashEvent) {
this.readyState = flashEvent.readyState; this.readyState = flashEvent.readyState;
} }
if ("protocol" in flashEvent) {
this.protocol = flashEvent.protocol;
}
var jsEvent; var jsEvent;
if (flashEvent.type == "open" || flashEvent.type == "error") { if (flashEvent.type == "open" || flashEvent.type == "error") {
jsEvent = this.__createSimpleEvent(flashEvent.type); jsEvent = this.__createSimpleEvent(flashEvent.type);
} else if (flashEvent.type == "close") { } else if (flashEvent.type == "close") {
// TODO implement jsEvent.wasClean
jsEvent = this.__createSimpleEvent("close"); jsEvent = this.__createSimpleEvent("close");
jsEvent.wasClean = flashEvent.wasClean ? true : false;
jsEvent.code = flashEvent.code;
jsEvent.reason = flashEvent.reason;
} else if (flashEvent.type == "message") { } else if (flashEvent.type == "message") {
var data = decodeURIComponent(flashEvent.message); var data = decodeURIComponent(flashEvent.message);
jsEvent = this.__createMessageEvent("message", data); jsEvent = this.__createMessageEvent("message", data);
...@@ -157,6 +189,7 @@ ...@@ -157,6 +189,7 @@
} }
this.dispatchEvent(jsEvent); this.dispatchEvent(jsEvent);
}; };
WebSocket.prototype.__createSimpleEvent = function(type) { WebSocket.prototype.__createSimpleEvent = function(type) {
...@@ -188,6 +221,9 @@ ...@@ -188,6 +221,9 @@
WebSocket.CLOSING = 2; WebSocket.CLOSING = 2;
WebSocket.CLOSED = 3; WebSocket.CLOSED = 3;
// Field to check implementation of WebSocket.
WebSocket.__isFlashImplementation = true;
WebSocket.__initialized = false;
WebSocket.__flash = null; WebSocket.__flash = null;
WebSocket.__instances = {}; WebSocket.__instances = {};
WebSocket.__tasks = []; WebSocket.__tasks = [];
...@@ -207,16 +243,31 @@ ...@@ -207,16 +243,31 @@
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash. * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
*/ */
WebSocket.__initialize = function() { WebSocket.__initialize = function() {
if (WebSocket.__flash) return;
if (WebSocket.__initialized) return;
WebSocket.__initialized = true;
if (WebSocket.__swfLocation) { if (WebSocket.__swfLocation) {
// For backword compatibility. // For backword compatibility.
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
} }
if (!window.WEB_SOCKET_SWF_LOCATION) { if (!window.WEB_SOCKET_SWF_LOCATION) {
console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
return; return;
} }
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
var swfHost = RegExp.$1;
if (location.host != swfHost) {
logger.error(
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
"('" + location.host + "' != '" + swfHost + "'). " +
"See also 'How to host HTML file and SWF file in different domains' section " +
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
}
}
var container = document.createElement("div"); var container = document.createElement("div");
container.id = "webSocketContainer"; container.id = "webSocketContainer";
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
...@@ -250,9 +301,11 @@ ...@@ -250,9 +301,11 @@
null, null,
function(e) { function(e) {
if (!e.success) { if (!e.success) {
console.error("[WebSocket] swfobject.embedSWF failed"); logger.error("[WebSocket] swfobject.embedSWF failed");
} }
}); }
);
}; };
/** /**
...@@ -287,7 +340,7 @@ ...@@ -287,7 +340,7 @@
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
} }
} catch (e) { } catch (e) {
console.error(e); logger.error(e);
} }
}, 0); }, 0);
return true; return true;
...@@ -295,12 +348,12 @@ ...@@ -295,12 +348,12 @@
// Called by Flash. // Called by Flash.
WebSocket.__log = function(message) { WebSocket.__log = function(message) {
console.log(decodeURIComponent(message)); logger.log(decodeURIComponent(message));
}; };
// Called by Flash. // Called by Flash.
WebSocket.__error = function(message) { WebSocket.__error = function(message) {
console.error(decodeURIComponent(message)); logger.error(decodeURIComponent(message));
}; };
WebSocket.__addTask = function(task) { WebSocket.__addTask = function(task) {
...@@ -327,15 +380,12 @@ ...@@ -327,15 +380,12 @@
}; };
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
if (window.addEventListener) { // NOTE:
window.addEventListener("load", function(){ // This fires immediately if web_socket.js is dynamically loaded after
WebSocket.__initialize(); // the document is loaded.
}, false); swfobject.addDomLoadEvent(function() {
} else { WebSocket.__initialize();
window.attachEvent("onload", function(){ });
WebSocket.__initialize();
});
}
} }
})(); })();
/* /*
* Websock: high-performance binary WebSockets * Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* Websock is similar to the standard WebSocket object but Websock * Websock is similar to the standard WebSocket object but Websock
* enables communication with raw TCP sockets (i.e. the binary stream) * enables communication with raw TCP sockets (i.e. the binary stream)
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* read binary data off of the receive queue. * read binary data off of the receive queue.
*/ */
/*jslint browser: true, bitwise: false, plusplus: false */ /*jslint browser: true, bitwise: true */
/*global Util, Base64 */ /*global Util, Base64 */
...@@ -35,326 +35,350 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) { ...@@ -35,326 +35,350 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
Websock_native = false; Websock_native = false;
(function () { (function () {
function get_INCLUDE_URI() { window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
return (typeof INCLUDE_URI !== "undefined") ?
INCLUDE_URI : "include/";
}
var start = "<script src='" + get_INCLUDE_URI(),
end = "'><\/script>", extra = "";
window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
"web-socket-js/WebSocketMain.swf"; "web-socket-js/WebSocketMain.swf";
if (Util.Engine.trident) { if (Util.Engine.trident) {
Util.Debug("Forcing uncached load of WebSocketMain.swf"); Util.Debug("Forcing uncached load of WebSocketMain.swf");
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random(); window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
} }
extra += start + "web-socket-js/swfobject.js" + end; Util.load_scripts(["web-socket-js/swfobject.js",
extra += start + "web-socket-js/web_socket.js" + end; "web-socket-js/web_socket.js"]);
document.write(extra); })();
}());
} }
function Websock() { function Websock() {
"use strict"; "use strict";
var api = {}, // Public API this._websocket = null; // WebSocket object
websocket = null, // WebSocket object this._rQ = []; // Receive queue
rQ = [], // Receive queue this._rQi = 0; // Receive queue index
rQi = 0, // Receive queue index this._rQmax = 10000; // Max receive queue size before compacting
rQmax = 10000, // Max receive queue size before compacting this._sQ = []; // Send queue
sQ = [], // Send queue
this._mode = 'base64'; // Current WebSocket mode: 'binary', 'base64'
eventHandlers = { this.maxBufferedAmount = 200;
'message' : function() {},
'open' : function() {}, this._eventHandlers = {
'close' : function() {}, 'message': function () {},
'error' : function() {} 'open': function () {},
}, 'close': function () {},
'error': function () {}
test_mode = false; };
//
// Queue public functions
//
function get_sQ() {
return sQ;
}
function get_rQ() {
return rQ;
}
function get_rQi() {
return rQi;
}
function set_rQi(val) {
rQi = val;
}
function rQlen() {
return rQ.length - rQi;
}
function rQpeek8() {
return (rQ[rQi] );
}
function rQshift8() {
return (rQ[rQi++] );
}
function rQunshift8(num) {
if (rQi === 0) {
rQ.unshift(num);
} else {
rQi -= 1;
rQ[rQi] = num;
}
}
function rQshift16() {
return (rQ[rQi++] << 8) +
(rQ[rQi++] );
}
function rQshift32() {
return (rQ[rQi++] << 24) +
(rQ[rQi++] << 16) +
(rQ[rQi++] << 8) +
(rQ[rQi++] );
} }
function rQshiftStr(len) {
if (typeof(len) === 'undefined') { len = rQlen(); }
var arr = rQ.slice(rQi, rQi + len);
rQi += len;
return arr.map(function (num) {
return String.fromCharCode(num); } ).join('');
} (function () {
function rQshiftBytes(len) { "use strict";
if (typeof(len) === 'undefined') { len = rQlen(); } Websock.prototype = {
rQi += len; // Getters and Setters
return rQ.slice(rQi-len, rQi); get_sQ: function () {
} return this._sQ;
},
get_rQ: function () {
return this._rQ;
},
get_rQi: function () {
return this._rQi;
},
set_rQi: function (val) {
this._rQi = val;
},
// Receive Queue
rQlen: function () {
return this._rQ.length - this._rQi;
},
rQpeek8: function () {
return this._rQ[this._rQi];
},
rQshift8: function () {
return this._rQ[this._rQi++];
},
rQskip8: function () {
this._rQi++;
},
rQskipBytes: function (num) {
this._rQi += num;
},
rQunshift8: function (num) {
if (this._rQi === 0) {
this._rQ.unshift(num);
} else {
this._rQi--;
this._rQ[this._rQi] = num;
}
},
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshift32: function () {
return (this._rQ[this._rQi++] << 24) +
(this._rQ[this._rQi++] << 16) +
(this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshiftStr: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
var arr = this._rQ.slice(this._rQi, this._rQi + len);
this._rQi += len;
return String.fromCharCode.apply(null, arr);
},
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
this._rQi += len;
return this._rQ.slice(this._rQi - len, this._rQi);
},
rQslice: function (start, end) {
if (end) {
return this._rQ.slice(this._rQi + start, this._rQi + end);
} else {
return this._rQ.slice(this._rQi + 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._rQ.length - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
}
this._rQi -= goback;
}
return true; // true means need more data
}
return false;
},
function rQslice(start, end) { // Send Queue
if (end) {
return rQ.slice(rQi + start, rQi + end);
} else {
return rQ.slice(rQi + start);
}
}
// Check to see if we must wait for 'num' bytes (default to FBU.bytes) flush: function () {
// to be available in the receive queue. Return true if we need to if (this._websocket.bufferedAmount !== 0) {
// wait (and possibly print a debug message), otherwise false. Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
function rQwait(msg, num, goback) {
var rQlen = rQ.length - rQi; // Skip rQlen() function call
if (rQlen < num) {
if (goback) {
if (rQi < goback) {
throw("rQwait cannot backup " + goback + " bytes");
} }
rQi -= goback;
}
//Util.Debug(" waiting for " + (num-rQlen) +
// " " + msg + " byte(s)");
return true; // true means need more data
}
return false;
}
// if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
// Private utility routines if (this._sQ.length > 0) {
// this._websocket.send(this._encode_message());
this._sQ = [];
}
return true;
} else {
Util.Info("Delaying send, bufferedAmount: " +
this._websocket.bufferedAmount);
return false;
}
},
send: function (arr) {
this._sQ = this._sQ.concat(arr);
return this.flush();
},
send_string: function (str) {
this.send(str.split('').map(function (chr) {
return chr.charCodeAt(0);
}));
},
// Event Handlers
on: function (evt, handler) {
this._eventHandlers[evt] = handler;
},
init: function (protocols, ws_schema) {
this._rQ = [];
this._rQi = 0;
this._sQ = [];
this._websocket = null;
// Check for full typed array support
var bt = false;
if (('Uint8Array' in window) &&
('set' in Uint8Array.prototype)) {
bt = true;
}
function encode_message() { // Check for full binary type support in WebSockets
/* base64 encode */ // Inspired by:
return Base64.encode(sQ); // https://github.com/Modernizr/Modernizr/issues/370
} // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
var wsbt = false;
try {
if (bt && ('binaryType' in WebSocket.prototype ||
!!(new WebSocket(ws_schema + '://.').binaryType))) {
Util.Info("Detected binaryType support in WebSockets");
wsbt = true;
}
} catch (exc) {
// Just ignore failed test localhost connection
}
function decode_message(data) { // Default protocols if not specified
//Util.Debug(">> decode_message: " + data); if (typeof(protocols) === "undefined") {
/* base64 decode */ if (wsbt) {
rQ = rQ.concat(Base64.decode(data, 0)); protocols = ['binary', 'base64'];
//Util.Debug(">> decode_message, rQ: " + rQ); } else {
} protocols = 'base64';
}
}
if (!wsbt) {
if (protocols === 'binary') {
throw new Error('WebSocket binary sub-protocol requested but not supported');
}
if (typeof(protocols) === 'object') {
var new_protocols = [];
for (var i = 0; i < protocols.length; i++) {
if (protocols[i] === 'binary') {
Util.Error('Skipping unsupported WebSocket binary sub-protocol');
} else {
new_protocols.push(protocols[i]);
}
}
if (new_protocols.length > 0) {
protocols = new_protocols;
} else {
throw new Error("Only WebSocket binary sub-protocol was requested and is not supported.");
}
}
}
// return protocols;
// Public Send functions },
//
function flush() {
if (websocket.bufferedAmount !== 0) {
Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
}
if (websocket.bufferedAmount < api.maxBufferedAmount) {
//Util.Debug("arr: " + arr);
//Util.Debug("sQ: " + sQ);
if (sQ.length > 0) {
websocket.send(encode_message(sQ));
sQ = [];
}
return true;
} else {
Util.Info("Delaying send, bufferedAmount: " +
websocket.bufferedAmount);
return false;
}
}
// overridable for testing open: function (uri, protocols) {
function send(arr) { var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
//Util.Debug(">> send_array: " + arr); protocols = this.init(protocols, ws_schema);
sQ = sQ.concat(arr);
return flush();
}
function send_string(str) { this._websocket = new WebSocket(uri, protocols);
//Util.Debug(">> send_string: " + str);
api.send(str.split('').map(
function (chr) { return chr.charCodeAt(0); } ) );
}
// if (protocols.indexOf('binary') >= 0) {
// Other public functions this._websocket.binaryType = 'arraybuffer';
function recv_message(e) {
//Util.Debug(">> recv_message: " + e.data.length);
try {
decode_message(e.data);
if (rQlen() > 0) {
eventHandlers.message();
// Compact the receive queue
if (rQ.length > rQmax) {
//Util.Debug("Compacting receive queue");
rQ = rQ.slice(rQi);
rQi = 0;
} }
} else {
Util.Debug("Ignoring empty message");
}
} catch (exc) {
if (typeof exc.stack !== 'undefined') {
Util.Warn("recv_message, caught exception: " + exc.stack);
} else if (typeof exc.description !== 'undefined') {
Util.Warn("recv_message, caught exception: " + exc.description);
} else {
Util.Warn("recv_message, caught exception:" + exc);
}
if (typeof exc.name !== 'undefined') {
eventHandlers.error(exc.name + ": " + exc.message);
} else {
eventHandlers.error(exc);
}
}
//Util.Debug("<< recv_message");
}
// Set event handlers
function on(evt, handler) {
eventHandlers[evt] = handler;
}
function init() {
rQ = [];
rQi = 0;
sQ = [];
websocket = null;
}
function open(uri) { this._websocket.onmessage = this._recv_message.bind(this);
init(); this._websocket.onopen = (function () {
Util.Debug('>> WebSock.onopen');
if (test_mode) { if (this._websocket.protocol) {
websocket = {}; this._mode = this._websocket.protocol;
} else { Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
websocket = new WebSocket(uri, 'base64'); } else {
// TODO: future native binary support this._mode = 'base64';
//websocket = new WebSocket(uri, ['binary', 'base64']); Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol);
} }
this._eventHandlers.open();
websocket.onmessage = recv_message; Util.Debug("<< WebSock.onopen");
websocket.onopen = function() { }).bind(this);
Util.Debug(">> WebSock.onopen"); this._websocket.onclose = (function (e) {
if (websocket.protocol) { Util.Debug(">> WebSock.onclose");
Util.Info("Server chose sub-protocol: " + websocket.protocol); this._eventHandlers.close(e);
} else { Util.Debug("<< WebSock.onclose");
Util.Error("Server select no sub-protocol!: " + websocket.protocol); }).bind(this);
this._websocket.onerror = (function (e) {
Util.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Util.Debug("<< WebSock.onerror: " + e);
}).bind(this);
},
close: function () {
if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection");
this._websocket.close();
}
this._websocket.onmessage = function (e) { return; };
}
},
// private methods
_encode_message: function () {
if (this._mode === 'binary') {
// Put in a binary arraybuffer
return (new Uint8Array(this._sQ)).buffer;
} else {
// base64 encode
return Base64.encode(this._sQ);
}
},
_decode_message: function (data) {
if (this._mode === 'binary') {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
for (var i = 0; i < u8.length; i++) {
this._rQ.push(u8[i]);
}
} else {
// base64 decode and concat to end
this._rQ = this._rQ.concat(Base64.decode(data, 0));
}
},
_recv_message: function (e) {
try {
this._decode_message(e.data);
if (this.rQlen() > 0) {
this._eventHandlers.message();
// Compact the receive queue
if (this._rQ.length > this._rQmax) {
this._rQ = this._rQ.slice(this._rQi);
this._rQi = 0;
}
} else {
Util.Debug("Ignoring empty message");
}
} catch (exc) {
var exception_str = "";
if (exc.name) {
exception_str += "\n name: " + exc.name + "\n";
exception_str += " message: " + exc.message + "\n";
}
if (typeof exc.description !== 'undefined') {
exception_str += " description: " + exc.description + "\n";
}
if (typeof exc.stack !== 'undefined') {
exception_str += exc.stack;
}
if (exception_str.length > 0) {
Util.Error("recv_message, caught exception: " + exception_str);
} else {
Util.Error("recv_message, caught exception: " + exc);
}
if (typeof exc.name !== 'undefined') {
this._eventHandlers.error(exc.name + ": " + exc.message);
} else {
this._eventHandlers.error(exc);
}
}
} }
eventHandlers.open();
Util.Debug("<< WebSock.onopen");
};
websocket.onclose = function(e) {
Util.Debug(">> WebSock.onclose");
eventHandlers.close(e);
Util.Debug("<< WebSock.onclose");
}; };
websocket.onerror = function(e) { })();
Util.Debug(">> WebSock.onerror: " + e);
eventHandlers.error(e);
Util.Debug("<< WebSock.onerror");
};
}
function close() {
if (websocket) {
if ((websocket.readyState === WebSocket.OPEN) ||
(websocket.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection");
websocket.close();
}
websocket.onmessage = function (e) { return; };
}
}
// Override internal functions for testing
// Takes a send function, returns reference to recv function
function testMode(override_send) {
test_mode = true;
api.send = override_send;
api.close = function () {};
return recv_message;
}
function constructor() {
// Configuration settings
api.maxBufferedAmount = 200;
// Direct access to send and receive queues
api.get_sQ = get_sQ;
api.get_rQ = get_rQ;
api.get_rQi = get_rQi;
api.set_rQi = set_rQi;
// Routines to read from the receive queue
api.rQlen = rQlen;
api.rQpeek8 = rQpeek8;
api.rQshift8 = rQshift8;
api.rQunshift8 = rQunshift8;
api.rQshift16 = rQshift16;
api.rQshift32 = rQshift32;
api.rQshiftStr = rQshiftStr;
api.rQshiftBytes = rQshiftBytes;
api.rQslice = rQslice;
api.rQwait = rQwait;
api.flush = flush;
api.send = send;
api.send_string = send_string;
api.on = on;
api.init = init;
api.open = open;
api.close = close;
api.testMode = testMode;
return api;
}
return constructor();
}
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt) * Copyright (C) 2013 NTT corp.
* Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
"use strict"; /*jslint bitwise: false, white: false, browser: true, devel: true */
/*jslint bitwise: false, white: false */
/*global Util, window, document */ /*global Util, window, document */
// Globals defined here // Globals defined here
...@@ -30,43 +30,47 @@ if (!window.$D) { ...@@ -30,43 +30,47 @@ if (!window.$D) {
} }
/* /*
* ------------------------------------------------------ * ------------------------------------------------------
* Namespaced in WebUtil * Namespaced in WebUtil
* ------------------------------------------------------ * ------------------------------------------------------
*/ */
// init log level reading the logging HTTP param // init log level reading the logging HTTP param
WebUtil.init_logging = function() { WebUtil.init_logging = function (level) {
Util._log_level = (document.location.href.match( "use strict";
/logging=([A-Za-z0-9\._\-]*)/) || if (typeof level !== "undefined") {
['', Util._log_level])[1]; Util._log_level = level;
} else {
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
Util._log_level = (param || ['', Util._log_level])[1];
}
Util.init_logging(); Util.init_logging();
}; };
WebUtil.init_logging();
WebUtil.dirObj = function (obj, depth, parent) { WebUtil.dirObj = function (obj, depth, parent) {
var i, msg = "", val = ""; "use strict";
if (! depth) { depth=2; } if (! depth) { depth = 2; }
if (! parent) { parent= ""; } if (! parent) { parent = ""; }
// Print the properties of the passed-in object // Print the properties of the passed-in object
for (i in obj) { var msg = "";
if ((depth > 1) && (typeof obj[i] === "object")) { for (var i in obj) {
if ((depth > 1) && (typeof obj[i] === "object")) {
// Recurse attributes that are objects // Recurse attributes that are objects
msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i); msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
} else { } else {
//val = new String(obj[i]).replace("\n", " "); //val = new String(obj[i]).replace("\n", " ");
var val = "";
if (typeof(obj[i]) === "undefined") { if (typeof(obj[i]) === "undefined") {
val = "undefined"; val = "undefined";
} else { } else {
val = obj[i].toString().replace("\n", " "); val = obj[i].toString().replace("\n", " ");
} }
if (val.length > 30) { if (val.length > 30) {
val = val.substr(0,30) + "..."; val = val.substr(0, 30) + "...";
} }
msg += parent + "." + i + ": " + val + "\n"; msg += parent + "." + i + ": " + val + "\n";
} }
} }
...@@ -74,10 +78,16 @@ WebUtil.dirObj = function (obj, depth, parent) { ...@@ -74,10 +78,16 @@ WebUtil.dirObj = function (obj, depth, parent) {
}; };
// Read a query string variable // Read a query string variable
WebUtil.getQueryVar = function(name, defVal) { WebUtil.getQueryVar = function (name, defVal) {
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'); "use strict";
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; } if (typeof defVal === 'undefined') { defVal = null; }
return (document.location.href.match(re) || ['',defVal])[1]; if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
}; };
...@@ -86,39 +96,118 @@ WebUtil.getQueryVar = function(name, defVal) { ...@@ -86,39 +96,118 @@ WebUtil.getQueryVar = function(name, defVal) {
*/ */
// No days means only for this browser session // No days means only for this browser session
WebUtil.createCookie = function(name,value,days) { WebUtil.createCookie = function (name, value, days) {
"use strict";
var date, expires; var date, expires;
if (days) { if (days) {
date = new Date(); date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000)); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires="+date.toGMTString(); expires = "; expires=" + date.toGMTString();
} } else {
else {
expires = ""; expires = "";
} }
document.cookie = name+"="+value+expires+"; path=/";
var secure;
if (document.location.protocol === "https:") {
secure = "; secure";
} else {
secure = "";
}
document.cookie = name + "=" + value + expires + "; path=/" + secure;
}; };
WebUtil.readCookie = function(name, defaultValue) { WebUtil.readCookie = function (name, defaultValue) {
var i, c, nameEQ = name + "=", ca = document.cookie.split(';'); "use strict";
for(i=0; i < ca.length; i += 1) { var nameEQ = name + "=",
c = ca[i]; ca = document.cookie.split(';');
while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); } for (var i = 0; i < ca.length; i += 1) {
var c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
} }
return (typeof defaultValue !== 'undefined') ? defaultValue : null; return (typeof defaultValue !== 'undefined') ? defaultValue : null;
}; };
WebUtil.eraseCookie = function(name) { WebUtil.eraseCookie = function (name) {
WebUtil.createCookie(name,"",-1); "use strict";
WebUtil.createCookie(name, "", -1);
};
/*
* Setting handling.
*/
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
"use strict";
var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) {
WebUtil.settings = cfg;
console.log(WebUtil.settings);
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
}
};
// No days means only for this browser session
WebUtil.writeSetting = function (name, value) {
"use strict";
if (window.chrome && window.chrome.storage) {
//console.log("writeSetting:", name, value);
if (WebUtil.settings[name] !== value) {
WebUtil.settings[name] = value;
window.chrome.storage.sync.set(WebUtil.settings);
}
} else {
localStorage.setItem(name, value);
}
};
WebUtil.readSetting = function (name, defaultValue) {
"use strict";
var value;
if (window.chrome && window.chrome.storage) {
value = WebUtil.settings[name];
} else {
value = localStorage.getItem(name);
}
if (typeof value === "undefined") {
value = null;
}
if (value === null && typeof defaultValue !== undefined) {
return defaultValue;
} else {
return value;
}
};
WebUtil.eraseSetting = function (name) {
"use strict";
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name);
delete WebUtil.settings[name];
} else {
localStorage.removeItem(name);
}
}; };
/* /*
* Alternate stylesheet selection * Alternate stylesheet selection
*/ */
WebUtil.getStylesheets = function() { var i, links, sheets = []; WebUtil.getStylesheets = function () {
links = document.getElementsByTagName("link"); "use strict";
for (i = 0; i < links.length; i += 1) { var links = document.getElementsByTagName("link");
var sheets = [];
for (var i = 0; i < links.length; i += 1) {
if (links[i].title && if (links[i].title &&
links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) { links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
sheets.push(links[i]); sheets.push(links[i]);
...@@ -129,14 +218,16 @@ WebUtil.getStylesheets = function() { var i, links, sheets = []; ...@@ -129,14 +218,16 @@ WebUtil.getStylesheets = function() { var i, links, sheets = [];
// No sheet means try and use value from cookie, null sheet used to // No sheet means try and use value from cookie, null sheet used to
// clear all alternates. // clear all alternates.
WebUtil.selectStylesheet = function(sheet) { WebUtil.selectStylesheet = function (sheet) {
var i, link, sheets = WebUtil.getStylesheets(); "use strict";
if (typeof sheet === 'undefined') { if (typeof sheet === 'undefined') {
sheet = 'default'; sheet = 'default';
} }
for (i=0; i < sheets.length; i += 1) {
link = sheets[i]; var sheets = WebUtil.getStylesheets();
if (link.title === sheet) { for (var i = 0; i < sheets.length; i += 1) {
var link = sheets[i];
if (link.title === sheet) {
Util.Debug("Using stylesheet " + sheet); Util.Debug("Using stylesheet " + sheet);
link.disabled = false; link.disabled = false;
} else { } else {
......
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.1//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile11.dtd"> <!DOCTYPE html>
<html> <html>
<head> <head>
<!-- <!--
noVNC example: simple example using default UI noVNC example: simple example using default UI
Copyright (C) 2012 Joel Martin Copyright (C) 2012 Joel Martin
noVNC is licensed under the LGPL-3 (see LICENSE.txt) Copyright (C) 2013 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). This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
Connect parameters are provided in query string:
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
--> -->
<title>noVNC</title> <title>noVNC</title>
...@@ -19,7 +23,7 @@ ...@@ -19,7 +23,7 @@
<!-- Apple iOS Safari settings --> <!-- Apple iOS Safari settings -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta names="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- App Start Icon --> <!-- App Start Icon -->
<link rel="apple-touch-startup-image" href="images/screen_320x460.png" /> <link rel="apple-touch-startup-image" href="images/screen_320x460.png" />
<!-- For iOS devices set the icon to use if user bookmarks app on their homescreen --> <!-- For iOS devices set the icon to use if user bookmarks app on their homescreen -->
...@@ -39,73 +43,80 @@ ...@@ -39,73 +43,80 @@
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
--> -->
<script src="include/vnc.js"></script>
<script src="include/ui.js"></script>
</head> </head>
<body> <body>
<div id="noVNC-control-bar"> <div id="noVNC-control-bar">
<!--noVNC Mobile Device only Buttons--> <!--noVNC Mobile Device only Buttons-->
<div class="noVNC-buttons-left"> <div class="noVNC-buttons-left">
<input type="image" src="images/drag.png" <input type="image" alt="viewport drag" src="images/drag.png"
id="noVNC_view_drag_button" class="noVNC_status_button" id="noVNC_view_drag_button" class="noVNC_status_button"
title="Move/Drag Viewport" title="Move/Drag Viewport">
onclick="UI.setViewDrag();">
<div id="noVNC_mobile_buttons"> <div id="noVNC_mobile_buttons">
<input type="image" src="images/mouse_none.png" <input type="image" alt="No mousebutton" src="images/mouse_none.png"
id="noVNC_mouse_button0" class="noVNC_status_button" id="noVNC_mouse_button0" class="noVNC_status_button">
onclick="UI.setMouseButton(1);"> <input type="image" alt="Left mousebutton" src="images/mouse_left.png"
<input type="image" src="images/mouse_left.png" id="noVNC_mouse_button1" class="noVNC_status_button">
id="noVNC_mouse_button1" class="noVNC_status_button" <input type="image" alt="Middle mousebutton" src="images/mouse_middle.png"
onclick="UI.setMouseButton(2);"> id="noVNC_mouse_button2" class="noVNC_status_button">
<input type="image" src="images/mouse_middle.png" <input type="image" alt="Right mousebutton" src="images/mouse_right.png"
id="noVNC_mouse_button2" class="noVNC_status_button" id="noVNC_mouse_button4" class="noVNC_status_button">
onclick="UI.setMouseButton(4);"> <input type="image" alt="Keyboard" src="images/keyboard.png"
<input type="image" src="images/mouse_right.png"
id="noVNC_mouse_button4" class="noVNC_status_button"
onclick="UI.setMouseButton(0);">
<input type="image" src="images/keyboard.png"
id="showKeyboard" class="noVNC_status_button" id="showKeyboard" class="noVNC_status_button"
value="Keyboard" title="Show Keyboard" value="Keyboard" title="Show Keyboard"/>
onclick="UI.showKeyboard()"/> <!-- Note that Google Chrome on Android doesn't respect any of these,
<input type="email" html attributes which attempt to disable text suggestions on the
autocapitalize="off" autocorrect="off" on-screen keyboard. Let's hope Chrome implements the ime-mode
id="keyboardinput" class="noVNC_status_button" style for example -->
onKeyDown="onKeyDown(event);" onblur="UI.keyInputBlur();"/> <textarea id="keyboardinput" autocapitalize="off"
autocorrect="off" autocomplete="off" spellcheck="false"
mozactionhint="Enter" onsubmit="return false;"
style="ime-mode: disabled;"></textarea>
<div id="noVNC_extra_keys">
<input type="image" alt="Extra keys" src="images/showextrakeys.png"
id="showExtraKeysButton" class="noVNC_status_button">
<input type="image" alt="Ctrl" src="images/ctrl.png"
id="toggleCtrlButton" class="noVNC_status_button">
<input type="image" alt="Alt" src="images/alt.png"
id="toggleAltButton" class="noVNC_status_button">
<input type="image" alt="Tab" src="images/tab.png"
id="sendTabButton" class="noVNC_status_button">
<input type="image" alt="Esc" src="images/esc.png"
id="sendEscButton" class="noVNC_status_button">
</div>
</div> </div>
</div> </div>
<div id="noVNC_status">Loading</div>
<!--noVNC Buttons--> <!--noVNC Buttons-->
<div class="noVNC-buttons-right"> <div class="noVNC-buttons-right">
<input type="image" src="images/ctrlaltdel.png" <input type="image" alt="Ctrl+Alt+Del" src="images/ctrlaltdel.png"
id="sendCtrlAltDelButton" class="noVNC_status_button" id="sendCtrlAltDelButton" class="noVNC_status_button"
title="Send Ctrl-Alt-Del" title="Send Ctrl-Alt-Del" />
onclick="UI.sendCtrlAltDel();" /> <input type="image" alt="Shutdown/Reboot" src="images/power.png"
<input type="image" src="images/clipboard.png" id="xvpButton" class="noVNC_status_button"
title="Shutdown/Reboot..." />
<input type="image" alt="Clipboard" src="images/clipboard.png"
id="clipboardButton" class="noVNC_status_button" id="clipboardButton" class="noVNC_status_button"
title="Clipboard" title="Clipboard" />
onclick="UI.toggleClipboardPanel();" /> <input type="image" alt="Settings" src="images/settings.png"
<input type="image" src="images/settings.png"
id="settingsButton" class="noVNC_status_button" id="settingsButton" class="noVNC_status_button"
title="Settings" title="Settings" />
onclick="UI.toggleSettingsPanel();" /> <input type="image" alt="Connect" src="images/connect.png"
<input type="image" src="images/connect.png"
id="connectButton" class="noVNC_status_button" id="connectButton" class="noVNC_status_button"
title="Connect" title="Connect" />
onclick="UI.toggleConnectPanel()" /> <input type="image" alt="Disconnect" src="images/disconnect.png"
<input type="image" src="images/disconnect.png"
id="disconnectButton" class="noVNC_status_button" id="disconnectButton" class="noVNC_status_button"
title="Disconnect" title="Disconnect" />
onclick="UI.disconnect()" />
</div> </div>
<!-- Description Panel --> <!-- Description Panel -->
<!-- Shown by default when hosted at for kanaka.github.com --> <!-- Shown by default when hosted at for kanaka.github.com -->
<div id="noVNC_description" style="display:none;" class=""> <div id="noVNC_description" class="">
noVNC is a browser based VNC client implemented using HTML5 Canvas noVNC is a browser based VNC client implemented using HTML5 Canvas
and WebSockets. You will either need a VNC server with WebSockets and WebSockets. You will either need a VNC server with WebSockets
support (such as <a href="http://libvnc.gtihub.io">libvncserver</a>) support (such as <a href="http://libvncserver.sourceforge.net/">libvncserver</a>)
or you will need to use or you will need to use
<a href="https://github.com/kanaka/websockify">websockify</a> <a href="https://github.com/kanaka/websockify">websockify</a>
to bridge between your browser and VNC server. See the noVNC to bridge between your browser and VNC server. See the noVNC
...@@ -113,25 +124,34 @@ ...@@ -113,25 +124,34 @@
and <a href="http://kanaka.github.com/noVNC">website</a> and <a href="http://kanaka.github.com/noVNC">website</a>
for more information. for more information.
<br /> <br />
<input type="button" value="Close" <input id="descriptionButton" type="button" value="Close">
onclick="UI.toggleConnectPanel();"> </div>
<!-- Popup Status Panel -->
<div id="noVNC_popup_status_panel" class="">
</div> </div>
<!-- Clipboard Panel --> <!-- Clipboard Panel -->
<div id="noVNC_clipboard" class="triangle-right top"> <div id="noVNC_clipboard" class="triangle-right top">
<textarea id="noVNC_clipboard_text" rows=5 <textarea id="noVNC_clipboard_text" rows=5>
onfocus="UI.displayBlur();" onblur="UI.displayFocus();"
onchange="UI.clipSend();">
</textarea> </textarea>
<br /> <br />
<input id="noVNC_clipboard_clear_button" type="button" <input id="noVNC_clipboard_clear_button" type="button"
value="Clear" onclick="UI.clipClear();"> value="Clear">
</div>
<!-- XVP Shutdown/Reboot Panel -->
<div id="noVNC_xvp" class="triangle-right top">
<span id="noVNC_xvp_menu">
<input type="button" id="xvpShutdownButton" value="Shutdown" />
<input type="button" id="xvpRebootButton" value="Reboot" />
<input type="button" id="xvpResetButton" value="Reset" />
</span>
</div> </div>
<!-- Settings Panel --> <!-- Settings Panel -->
<div id="noVNC_settings" class="triangle-right top"> <div id="noVNC_settings" class="triangle-right top">
<span id="noVNC_settings_menu" onmouseover="UI.displayBlur();" <span id="noVNC_settings_menu">
onmouseout="UI.displayFocus();">
<ul> <ul>
<li><input id="noVNC_encrypt" type="checkbox"> Encrypt</li> <li><input id="noVNC_encrypt" type="checkbox"> Encrypt</li>
<li><input id="noVNC_true_color" type="checkbox" checked> True Color</li> <li><input id="noVNC_true_color" type="checkbox" checked> True Color</li>
...@@ -139,7 +159,6 @@ ...@@ -139,7 +159,6 @@
<li><input id="noVNC_clip" type="checkbox"> Clip to Window</li> <li><input id="noVNC_clip" type="checkbox"> Clip to Window</li>
<li><input id="noVNC_shared" type="checkbox"> Shared Mode</li> <li><input id="noVNC_shared" type="checkbox"> Shared Mode</li>
<li><input id="noVNC_view_only" type="checkbox"> View Only</li> <li><input id="noVNC_view_only" type="checkbox"> View Only</li>
<li><input id="noVNC_connectTimeout" type="input"> Connect Timeout (s)</li>
<li><input id="noVNC_path" type="input" value="websockify"> Path</li> <li><input id="noVNC_path" type="input" value="websockify"> Path</li>
<li><input id="noVNC_repeaterID" type="input" value=""> Repeater ID</li> <li><input id="noVNC_repeaterID" type="input" value=""> Repeater ID</li>
<hr> <hr>
...@@ -156,8 +175,7 @@ ...@@ -156,8 +175,7 @@
</select></label> </select></label>
</li> </li>
<hr> <hr>
<li><input type="button" id="noVNC_apply" value="Apply" <li><input type="button" id="noVNC_apply" value="Apply"></li>
onclick="UI.settingsApply()"></li>
</ul> </ul>
</span> </span>
</div> </div>
...@@ -168,7 +186,7 @@ ...@@ -168,7 +186,7 @@
<li><label><strong>Host: </strong><input id="noVNC_host" /></label></li> <li><label><strong>Host: </strong><input id="noVNC_host" /></label></li>
<li><label><strong>Port: </strong><input id="noVNC_port" /></label></li> <li><label><strong>Port: </strong><input id="noVNC_port" /></label></li>
<li><label><strong>Password: </strong><input id="noVNC_password" type="password" /></label></li> <li><label><strong>Password: </strong><input id="noVNC_password" type="password" /></label></li>
<li><input id="noVNC_connect_button" type="button" value="Connect" onclick="UI.connect();"></li> <li><input id="noVNC_connect_button" type="button" value="Connect"></li>
</ul> </ul>
</div> </div>
...@@ -178,10 +196,6 @@ ...@@ -178,10 +196,6 @@
<div id="noVNC_screen"> <div id="noVNC_screen">
<div id="noVNC_screen_pad"></div> <div id="noVNC_screen_pad"></div>
<div id="noVNC_status_bar" class="noVNC_status_bar">
<div id="noVNC_status">Loading</div>
</div>
<h1 id="noVNC_logo"><span>no</span><br />VNC</h1> <h1 id="noVNC_logo"><span>no</span><br />VNC</h1>
<!-- HTML5 Canvas --> <!-- HTML5 Canvas -->
...@@ -192,9 +206,8 @@ ...@@ -192,9 +206,8 @@
</div> </div>
</div> </div>
<script src="include/util.js"></script>
<script src="include/ui.js"></script>
<script>
window.onload = UI.load;
</script>
</body> </body>
</html> </html>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<!-- <head>
<!--
noVNC example: simple example using default UI noVNC example: simple example using default UI
Copyright (C) 2012 Joel Martin Copyright (C) 2012 Joel Martin
noVNC is licensed under the LGPL-3 (see LICENSE.txt) Copyright (C) 2013 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). This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
Connect parameters are provided in query string: Connect parameters are provided in query string:
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1 http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
--> -->
<head> <title>noVNC</title>
<title>noVNC</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1"> <meta charset="utf-8">
<link rel="stylesheet" href="include/base.css" title="plain">
<!-- <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
<script type='text/javascript' Remove this if you use the .htaccess -->
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-->
<script src="include/vnc.js"></script> <!-- Apple iOS Safari settings -->
</head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes" />
<body style="margin: 0px;"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<div id="noVNC_screen"> <!-- App Start Icon -->
<link rel="apple-touch-startup-image" href="images/screen_320x460.png" />
<!-- For iOS devices set the icon to use if user bookmarks app on their homescreen -->
<link rel="apple-touch-icon" href="images/screen_57x57.png">
<!--
<link rel="apple-touch-icon-precomposed" href="images/screen_57x57.png" />
-->
<!-- Stylesheets -->
<link rel="stylesheet" href="include/base.css" title="plain">
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="include/util.js"></script>
</head>
<body style="margin: 0px;">
<div id="noVNC_screen">
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;"> <div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
<table border=0 width="100%"><tr> <table border=0 width="100%"><tr>
<td><div id="noVNC_status">Loading</div></td> <td><div id="noVNC_status" style="position: relative; height: auto;">
Loading
</div></td>
<td width="1%"><div id="noVNC_buttons"> <td width="1%"><div id="noVNC_buttons">
<input type=button value="Send CtrlAltDel" <input type=button value="Send CtrlAltDel"
id="sendCtrlAltDelButton"> id="sendCtrlAltDelButton">
<span id="noVNC_xvp_buttons">
<input type=button value="Shutdown"
id="xvpShutdownButton">
<input type=button value="Reboot"
id="xvpRebootButton">
<input type=button value="Reset"
id="xvpResetButton">
</span>
</div></td> </div></td>
</tr></table> </tr></table>
</div> </div>
...@@ -41,6 +74,11 @@ ...@@ -41,6 +74,11 @@
/*global window, $, Util, RFB, */ /*global window, $, Util, RFB, */
"use strict"; "use strict";
// Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js"]);
var rfb; var rfb;
function passwordRequired(rfb) { function passwordRequired(rfb) {
...@@ -61,6 +99,18 @@ ...@@ -61,6 +99,18 @@
rfb.sendCtrlAltDel(); rfb.sendCtrlAltDel();
return false; return false;
} }
function xvpShutdown() {
rfb.xvpShutdown();
return false;
}
function xvpReboot() {
rfb.xvpReboot();
return false;
}
function xvpReset() {
rfb.xvpReset();
return false;
}
function updateState(rfb, state, oldstate, msg) { function updateState(rfb, state, oldstate, msg) {
var s, sb, cad, level; var s, sb, cad, level;
s = $D('noVNC_status'); s = $D('noVNC_status');
...@@ -75,8 +125,12 @@ ...@@ -75,8 +125,12 @@
default: level = "warn"; break; default: level = "warn"; break;
} }
if (state === "normal") { cad.disabled = false; } if (state === "normal") {
else { cad.disabled = true; } cad.disabled = false;
} else {
cad.disabled = true;
xvpInit(0);
}
if (typeof(msg) !== 'undefined') { if (typeof(msg) !== 'undefined') {
sb.setAttribute("class", "noVNC_status_" + level); sb.setAttribute("class", "noVNC_status_" + level);
...@@ -84,17 +138,42 @@ ...@@ -84,17 +138,42 @@
} }
} }
window.onload = function () { function xvpInit(ver) {
var xvpbuttons;
xvpbuttons = $D('noVNC_xvp_buttons');
if (ver >= 1) {
xvpbuttons.style.display = 'inline';
} else {
xvpbuttons.style.display = 'none';
}
}
window.onscriptsload = function () {
var host, port, password, path, token; var host, port, password, path, token;
$D('sendCtrlAltDelButton').style.display = "inline"; $D('sendCtrlAltDelButton').style.display = "inline";
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel; $D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
$D('xvpShutdownButton').onclick = xvpShutdown;
$D('xvpRebootButton').onclick = xvpReboot;
$D('xvpResetButton').onclick = xvpReset;
WebUtil.init_logging(WebUtil.getQueryVar('logging', 'warn'));
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC')); document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
// By default, use the host and port of server that served this file // By default, use the host and port of server that served this file
host = WebUtil.getQueryVar('host', window.location.hostname); host = WebUtil.getQueryVar('host', window.location.hostname);
port = WebUtil.getQueryVar('port', window.location.port); port = WebUtil.getQueryVar('port', window.location.port);
// if port == 80 (or 443) then it won't be present and should be
// set manually
if (!port) {
if (window.location.protocol.substring(0,5) == 'https') {
port = 443;
}
else if (window.location.protocol.substring(0,4) == 'http') {
port = 80;
}
}
// If a token variable is passed in, set the parameter in a cookie. // If a token variable is passed in, set the parameter in a cookie.
// This is used by nova-novncproxy. // This is used by nova-novncproxy.
token = WebUtil.getQueryVar('token', null); token = WebUtil.getQueryVar('token', null);
...@@ -119,7 +198,8 @@ ...@@ -119,7 +198,8 @@
'local_cursor': WebUtil.getQueryVar('cursor', true), 'local_cursor': WebUtil.getQueryVar('cursor', true),
'shared': WebUtil.getQueryVar('shared', true), 'shared': WebUtil.getQueryVar('shared', true),
'view_only': WebUtil.getQueryVar('view_only', false), 'view_only': WebUtil.getQueryVar('view_only', false),
'updateState': updateState, 'onUpdateState': updateState,
'onXvpInit': xvpInit,
'onPasswordRequired': passwordRequired}); 'onPasswordRequired': passwordRequired});
rfb.connect(host, port, password, path); rfb.connect(host, port, password, path);
}; };
...@@ -127,4 +207,3 @@ ...@@ -127,4 +207,3 @@
</body> </body>
</html> </html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment