1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-12 09:17:41 +03:00

Update noVNC master commit 75d69b9f621606c3b2db48e4778ff41307f65c6d

This commit is contained in:
Daniel Molina 2013-11-25 18:28:10 +01:00
parent 2cad5fc87c
commit 732528eef6
16 changed files with 1073 additions and 383 deletions

View File

@ -1,7 +1,7 @@
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
The noVNC core library is licensed under the LGPLv3 (GNU Lesser
General Public License). The noVNC core library is composed of the
The noVNC core library files are licensed under the MPL 2.0 (Mozilla
Public License 2.0). The noVNC core library is composed of the
Javascript code necessary for full noVNC operation. This includes (but
is not limited to):
@ -10,6 +10,7 @@ is not limited to):
include/display.js
include/input.js
include/jsunzip.js
include/keysym.js
include/logo.js
include/rfb.js
include/ui.js
@ -36,22 +37,16 @@ The HTML, CSS, font and image files are licensed as follows:
images/ : Creative Commons Attribution-ShareAlike
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.
Please refer to the individual source files and/or to the noVNC commit
history: https://github.com/kanaka/noVNC/commits/master
The are several files and projects that have been incorporated into
the noVNC core library. Here is a list of those files and the original
licenses (all LGPL-3 compatible):
licenses (all MPL 2.0 compatible):
include/base64.js : MPL 2.0
include/base64.js : MPL 1.1, GPL-2 or LGPL-2.1
include/des.js : Various BSD style licenses
include/jsunzip.js : zlib/libpng license
@ -59,20 +54,29 @@ licenses (all LGPL-3 compatible):
include/web-socket-js/ : New BSD license (3-clause). Source code at
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:
docs/LICENSE.MPL-2.0
docs/LICENSE.LGPL-3 and
docs/LICENSE.GPL-3
docs/LICENSE.OFL-1.1
docs/LICENSE.BSD-3-Clause (New BSD)
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
docs/LICENSE.zlib
docs/LICENSE.MPL-2.0
docs/LICENSE.Apache-2.0
Or alternatively the license texts may be found here:
http://www.mozilla.org/MPL/2.0/
http://www.gnu.org/licenses/lgpl.html and
http://www.gnu.org/licenses/gpl.html
http://scripts.sil.org/OFL
http://www.mozilla.org/MPL/1.1/
http://www.mozilla.org/MPL/2.0/
http://en.wikipedia.org/wiki/BSD_licenses
http://www.gzip.org/zlib/zlib_license.html
http://www.apache.org/licenses/LICENSE-2.0.html

View File

@ -1,7 +1,8 @@
/*
* noVNC base CSS
* 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).
*/
@ -51,7 +52,6 @@ html {
float:right;
}
#noVNC_view_drag_button {
display: none;
}
@ -62,34 +62,36 @@ html {
display: none;
}
#noVNC_extra_keys {
display: inline;
list-style-type: none;
padding: 0px;
margin: 0px;
position: relative;
}
.noVNC-buttons-left {
float: left;
padding-left:10px;
padding-top:4px;
z-index: 1;
position: relative;
}
.noVNC-buttons-right {
float:right;
right: 0px;
padding-right:10px;
padding-top:4px;
}
#noVNC_status_bar {
margin-top: 0px;
padding: 0px;
}
#noVNC_status_bar div {
font-size: 12px;
padding-top: 4px;
width:100%;
z-index: 2;
position: absolute;
}
#noVNC_status {
height:20px;
font-size: 12px;
padding-top: 4px;
height:32px;
text-align: center;
font-weight: bold;
color: #fff;
}
#noVNC_settings_menu {
margin: 3px;
text-align: left;
@ -104,22 +106,12 @@ html {
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
* scaling will occur. Canvas resizes to remote VNC settings */
#noVNC_screen_pad {
margin: 0px;
padding: 0px;
height: 44px;
height: 36px;
}
#noVNC_screen {
text-align: center;
@ -154,14 +146,14 @@ html {
/*Bubble contents divs*/
#noVNC_settings {
display:none;
margin-top:77px;
margin-top:73px;
right:20px;
position:fixed;
}
#noVNC_controls {
display:none;
margin-top:77px;
margin-top:73px;
right:12px;
position:fixed;
}
@ -173,7 +165,7 @@ html {
display:none;
position:fixed;
margin-top:77px;
margin-top:73px;
right:20px;
left:20px;
padding:15px;
@ -186,9 +178,30 @@ html {
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_clipboard {
display:none;
margin-top:77px;
margin-top:73px;
right:30px;
position:fixed;
}
@ -207,17 +220,11 @@ html {
z-index: -1;
}
.noVNC_status_warn {
background-color:yellow;
}
/*
* Advanced Styling
*/
/* Control bar */
#noVNC-control-bar {
position:fixed;
.noVNC_status_normal {
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
@ -225,9 +232,32 @@ html {
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
/* Control bar */
#noVNC-control-bar {
position:fixed;
display:block;
height:44px;
height:36px;
left:0;
top:0;
width:100%;
@ -368,22 +398,85 @@ html {
font-size: 180px;
}
@media screen and (min-width: 481px) and (max-width: 640px) {
.noVNC_status_button {
font-size: 10px;
.noVNC-buttons-left {
padding-left: 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 {
width: 410px;
}
#noVNC_logo {
font-size: 150px;
}
}
@media screen and (min-width: 321px) and (max-width: 480px) {
.noVNC_status_button {
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 {
width: 250px;
}

View File

@ -1,45 +1,8 @@
/*
* Modified from:
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* ***** BEGIN LICENSE BLOCK *****
* 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 ***** */
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
/*jslint white: false, bitwise: false, plusplus: false */
/*global console */
@ -47,36 +10,39 @@
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
encode: function (data) {
"use strict";
var result = '',
chrTable = Base64.toBase64Table.split(''),
pad = Base64.base64Pad,
length = data.length,
i;
var result = '';
var toBase64Table = Base64.toBase64Table;
var length = data.length
var lengthpad = (length%3);
var i = 0, j = 0;
// Convert every three bytes to 4 ascii characters.
/* BEGIN LOOP */
for (i = 0; i < (length - 2); i += 3) {
result += chrTable[data[i] >> 2];
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += chrTable[data[i+2] & 0x3f];
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += toBase64Table[data[i+2] & 0x3f];
}
/* END LOOP */
// 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;
}
if (lengthpad === 2) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j+1] >> 4)];
result += toBase64Table[(data[j+1] & 0x0f) << 2];
result += toBase64Table[64];
} else if (lengthpad === 1) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[64];
result += toBase64Table[64];
}
return result;
@ -97,12 +63,12 @@ toBinaryTable : [
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;
var toBinaryTable = Base64.toBinaryTable;
var base64Pad = Base64.base64Pad;
var result, result_length, idx, i, c, padding;
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; }
@ -111,9 +77,10 @@ decode: function (data, offset) {
result = new Array(result_length);
// Convert one by one.
/* BEGIN LOOP */
for (idx = 0, i = offset; i < data.length; i++) {
c = binTable[data.charCodeAt(i) & 0x7f];
padding = (data.charAt(i) === pad);
c = toBinaryTable[data.charCodeAt(i) & 0x7f];
padding = (data.charAt(i) === base64Pad);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
@ -134,6 +101,7 @@ decode: function (data, offset) {
leftdata &= (1 << leftbits) - 1;
}
}
/* END LOOP */
// If there are any bits left, the base64 string was corrupted
if (leftbits) {

View File

@ -1,7 +1,8 @@
/*
* noVNC base CSS
* noVNC black CSS
* 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).
*/
@ -9,7 +10,7 @@
background-color:#000;
}
#noVNC-control-bar {
.noVNC_status_normal {
background: #4c4c4c; /* Old browsers */
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+ */
@ -18,6 +19,24 @@
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 */
}
.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 {
border:2px solid #fff;

View File

@ -1,11 +1,12 @@
/*
* noVNC base CSS
* noVNC blue CSS
* 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).
*/
#noVNC-control-bar {
.noVNC_status_normal {
background-color:#04073d;
background-image: -webkit-gradient(
linear,
@ -20,6 +21,36 @@
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 {
border:2px solid #fff;

View File

@ -1,7 +1,7 @@
/*
* noVNC: HTML5 VNC client
* 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.
*/
@ -502,8 +502,8 @@ that.finishTile = function() {
// 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;
rgbImageData = function(x, y, vx, vy, width, height, arr, offset) {
var img, i, j, data;
/*
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
(x - v.x + width < 0) || (y - v.y + height < 0)) {
@ -519,11 +519,11 @@ rgbImageData = function(x, y, width, height, arr, offset) {
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - v.x, y - v.y);
c_ctx.putImageData(img, x - vx, y - vy);
};
bgrxImageData = function(x, y, width, height, arr, offset) {
var img, i, j, data, v = viewport;
bgrxImageData = function(x, y, vx, vy, width, height, arr, offset) {
var img, i, j, data;
/*
if ((x - v.x >= v.w) || (y - v.y >= v.h) ||
(x - v.x + width < 0) || (y - v.y + height < 0)) {
@ -539,10 +539,10 @@ bgrxImageData = function(x, y, width, height, arr, offset) {
data[i + 2] = arr[j ];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - v.x, y - v.y);
c_ctx.putImageData(img, x - vx, y - vy);
};
cmapImageData = function(x, y, width, height, arr, offset) {
cmapImageData = function(x, y, vx, vy, width, height, arr, offset) {
var img, i, j, data, bgr, cmap;
img = c_ctx.createImageData(width, height);
data = img.data;
@ -554,23 +554,23 @@ cmapImageData = function(x, y, width, height, arr, offset) {
data[i + 2] = bgr[0];
data[i + 3] = 255; // Set Alpha
}
c_ctx.putImageData(img, x - viewport.x, y - viewport.y);
c_ctx.putImageData(img, x - vx, y - vy);
};
that.blitImage = function(x, y, width, height, arr, offset) {
if (conf.true_color) {
bgrxImageData(x, y, width, height, arr, offset);
bgrxImageData(x, y, viewport.x, viewport.y, width, height, arr, offset);
} else {
cmapImageData(x, y, width, height, arr, offset);
cmapImageData(x, y, viewport.x, viewport.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);
rgbImageData(x, y, viewport.x, viewport.y, width, height, arr, offset);
} else {
// prolly wrong...
cmapImageData(x, y, width, height, arr, offset);
cmapImageData(x, y, viewport.x, viewport.y, width, height, arr, offset);
}
};
@ -657,11 +657,18 @@ return constructor(); // Return the public API interface
/* Set CSS cursor property using data URI encoded cursor file */
function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) {
function changeCursor(target, pixels, mask, hotx, hoty, w0, h0, cmap) {
"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);
//Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w0: " + w0 + ", h0: " + h0);
var w = w0;
var h = h0;
if (h < w)
h = w; // increase h to make it square
else
w = h; // increace w to make it square
// Push multi-byte little-endian values
cur.push16le = function (num) {
this.push((num ) & 0xFF,
@ -715,22 +722,28 @@ function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) {
// 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
if (x >= w0 || y >= h0) {
cur.push(0); // blue
cur.push(0); // green
cur.push(0); // red
cur.push(0); // alpha
} else {
idx = ((w * y) + x) * 4;
cur.push(pixels[idx + 2]); // blue
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx ]); // red
cur.push(alpha); // alpha
idx = y * Math.ceil(w0 / 8) + Math.floor(x/8);
alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
if (cmap) {
idx = (w0 * 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 {
idx = ((w0 * y) + x) * 4;
cur.push(pixels[idx + 2]); // blue
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx ]); // red
cur.push(alpha); // alpha
}
}
}
}

View File

@ -1,7 +1,8 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-2 or any later version (see LICENSE.txt)
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/*jslint browser: true, white: false, bitwise: false */
@ -83,6 +84,10 @@ function getKeysymSpecial(evt) {
case 122 : keysym = 0xFFC8; break; // F11
case 123 : keysym = 0xFFC9; break; // F12
case 225 : keysym = 0xFE03; break; // AltGr
case 91 : keysym = 0xFFEC; break; // Super_R (Win Key)
case 93 : keysym = 0xFF67; break; // Menu (Win Menu)
default : break;
}
}
@ -106,6 +111,10 @@ function getKeysymSpecial(evt) {
if (Util.Engine.gecko || Util.Engine.presto) {
keysym = 45; }
break;
case 173 : // - (Mozilla)
if (Util.Engine.gecko) {
keysym = 45; }
break;
case 189 : keysym = 45; break; // - (IE)
case 190 : keysym = 46; break; // . (Mozilla, IE)
case 191 : keysym = 47; break; // / (Mozilla, IE)
@ -478,7 +487,11 @@ function Mouse(defaults) {
"use strict";
var that = {}, // Public API methods
conf = {}; // Configuration attributes
conf = {}, // Configuration attributes
mouseCaptured = false;
var doubleClickTimer = null,
lastTouchPos = null;
// Configuration attributes
Util.conf_defaults(conf, that, defaults, [
@ -491,11 +504,31 @@ Util.conf_defaults(conf, that, defaults, [
['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)']
]);
function captureMouse() {
// capturing the mouse ensures we get the mouseup event
if (conf.target.setCapture) {
conf.target.setCapture();
}
// some browsers give us mouseup events regardless,
// so if we never captured the mouse, we can disregard the event
mouseCaptured = true;
}
function releaseMouse() {
if (conf.target.releaseCapture) {
conf.target.releaseCapture();
}
mouseCaptured = false;
}
//
// Private functions
//
function resetDoubleClickTimer() {
doubleClickTimer = null;
}
function onMouseButton(e, down) {
var evt, pos, bmask;
if (! conf.focused) {
@ -503,8 +536,34 @@ function onMouseButton(e, down) {
}
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, conf.target, conf.scale);
if (e.touches || e.changedTouches) {
// Touch device
// When two touches occur within 500 ms of each other and are
// closer than 20 pixels together a double click is triggered.
if (down == 1) {
if (doubleClickTimer == null) {
lastTouchPos = pos;
} else {
clearTimeout(doubleClickTimer);
// When the distance between the two touches is small enough
// force the position of the latter touch to the position of
// the first.
var xs = lastTouchPos.x - pos.x;
var ys = lastTouchPos.y - pos.y;
var d = Math.sqrt((xs * xs) + (ys * ys));
// The goal is to trigger on a certain physical width, the
// devicePixelRatio brings us a bit closer but is not optimal.
if (d < 20 * window.devicePixelRatio) {
pos = lastTouchPos;
}
}
doubleClickTimer = setTimeout(resetDoubleClickTimer, 500);
}
bmask = conf.touchButton;
// If bmask is set
} else if (evt.which) {
@ -518,7 +577,7 @@ function onMouseButton(e, down) {
}
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
if (bmask > 0 && conf.onMouseButton) {
if (conf.onMouseButton) {
Util.Debug("onMouseButton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
conf.onMouseButton(pos.x, pos.y, down, bmask);
@ -528,11 +587,17 @@ function onMouseButton(e, down) {
}
function onMouseDown(e) {
captureMouse();
onMouseButton(e, 1);
}
function onMouseUp(e) {
if (!mouseCaptured) {
return;
}
onMouseButton(e, 0);
releaseMouse();
}
function onMouseWheel(e) {
@ -580,9 +645,9 @@ function onMouseDisable(e) {
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)) {
if ((pos.realx >= 0) && (pos.realy >= 0) &&
(pos.realx < conf.target.offsetWidth) &&
(pos.realy < conf.target.offsetHeight)) {
//Util.Debug("mouse event disabled");
Util.stopEvent(e);
return false;
@ -601,10 +666,12 @@ that.grab = function() {
if ('ontouchstart' in document.documentElement) {
Util.addEvent(c, 'touchstart', onMouseDown);
Util.addEvent(window, 'touchend', onMouseUp);
Util.addEvent(c, 'touchend', onMouseUp);
Util.addEvent(c, 'touchmove', onMouseMove);
} else {
Util.addEvent(c, 'mousedown', onMouseDown);
Util.addEvent(window, 'mouseup', onMouseUp);
Util.addEvent(c, 'mouseup', onMouseUp);
Util.addEvent(c, 'mousemove', onMouseMove);
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
@ -624,10 +691,12 @@ that.ungrab = function() {
if ('ontouchstart' in document.documentElement) {
Util.removeEvent(c, 'touchstart', onMouseDown);
Util.removeEvent(window, 'touchend', onMouseUp);
Util.removeEvent(c, 'touchend', onMouseUp);
Util.removeEvent(c, 'touchmove', onMouseMove);
} else {
Util.removeEvent(c, 'mousedown', onMouseDown);
Util.removeEvent(window, 'mouseup', onMouseUp);
Util.removeEvent(c, 'mouseup', onMouseUp);
Util.removeEvent(c, 'mousemove', onMouseMove);
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',

View File

@ -352,20 +352,28 @@ this.getbit = function(d)
}
/* 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)
{
if (!num)
return base;
var val = 0;
while (d.bitcount < 24) {
d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount;
d.bitcount += 8;
}
val = d.tag & (0xffff >> (16 - num));
d.tag >>= num;
d.bitcount -= num;
return val + base;
var ret = read_bits_direct(d.source, d.bitcount, d.tag, d.sourceIndex, num);
d.bitcount = ret[0];
d.tag = ret[1];
d.sourceIndex = ret[2];
return ret[3] + base;
}
/* given a data stream and a tree, decode a symbol */

View File

@ -1,7 +1,7 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
"use strict";
@ -79,10 +79,22 @@ queue_next_packet = function () {
}
};
var bytes_processed = 0;
do_packet = function () {
//Util.Debug("Processing frame: " + frame_idx);
var frame = VNC_frame_data[frame_idx];
rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)});
var frame = VNC_frame_data[frame_idx],
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;
queue_next_packet();

View File

@ -1,7 +1,8 @@
/*
* noVNC: HTML5 VNC client
* 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.
*
@ -102,7 +103,6 @@ var that = {}, // Public API methods
fb_height = 0,
fb_name = "",
last_req_time = 0,
rre_chunk_sz = 100,
timing = {
@ -147,9 +147,6 @@ Util.conf_defaults(conf, that, defaults, [
['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'],
['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
// Callback functions
['onUpdateState', 'rw', 'func', function() { },
'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '],
@ -163,6 +160,10 @@ Util.conf_defaults(conf, that, defaults, [
'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '],
['onFBUComplete', 'rw', 'func', function() { },
'onFBUComplete(rfb, fbu): RFB FBU received and processed '],
['onFBResize', 'rw', 'func', function() { },
'onFBResize(rfb, width, height): frame buffer resized'],
['onDesktopName', 'rw', 'func', function() { },
'onDesktopName(rfb, name): desktop name received'],
// These callback names are deprecated
['updateState', 'rw', 'func', function() { },
@ -299,7 +300,8 @@ function connect() {
uri += rfb_host + ":" + rfb_port + "/" + rfb_path;
}
Util.Info("connecting to " + uri);
ws.open(uri);
// TODO: make protocols a configurable
ws.open(uri, ['binary', 'base64']);
Util.Debug("<< RFB.connect");
}
@ -397,7 +399,7 @@ updateState = function(state, statusMsg) {
}
if (msgTimer) {
clearInterval(msgTimer);
clearTimeout(msgTimer);
msgTimer = null;
}
@ -438,13 +440,13 @@ updateState = function(state, statusMsg) {
if (connTimer && (rfb_state !== 'connect')) {
Util.Debug("Clearing connect timer");
clearInterval(connTimer);
clearTimeout(connTimer);
connTimer = null;
}
if (disconnTimer && (rfb_state !== 'disconnect')) {
Util.Debug("Clearing disconnect timer");
clearInterval(disconnTimer);
clearTimeout(disconnTimer);
disconnTimer = null;
}
@ -563,44 +565,18 @@ function genDES(password, challenge) {
return (new DES(passwd)).encrypt(challenge);
}
function flushClient() {
if (mouse_arr.length > 0) {
//send(mouse_arr.concat(fbUpdateRequests()));
ws.send(mouse_arr);
setTimeout(function() {
ws.send(fbUpdateRequests());
}, 50);
mouse_arr = [];
return true;
} else {
return false;
}
}
// overridable for testing
checkEvents = function() {
var now;
if (rfb_state === 'normal' && !viewportDragging) {
if (! flushClient()) {
now = new Date().getTime();
if (now > last_req_time + conf.fbu_req_rate) {
last_req_time = now;
ws.send(fbUpdateRequests());
}
}
if (rfb_state === 'normal' && !viewportDragging && mouse_arr.length > 0) {
ws.send(mouse_arr);
mouse_arr = [];
}
setTimeout(checkEvents, conf.check_rate);
};
keyPress = function(keysym, down) {
var arr;
if (conf.view_only) { return; } // View only, skip keyboard events
arr = keyEvent(keysym, down);
arr = arr.concat(fbUpdateRequests());
ws.send(arr);
ws.send(keyEvent(keysym, down));
};
mouseButton = function(x, y, down, bmask) {
@ -619,7 +595,6 @@ mouseButton = function(x, y, down, bmask) {
return;
} else {
viewportDragging = false;
ws.send(fbUpdateRequests()); // Force immediate redraw
}
}
@ -627,7 +602,8 @@ mouseButton = function(x, y, down, bmask) {
mouse_arr = mouse_arr.concat(
pointerEvent(display.absX(x), display.absY(y)) );
flushClient();
ws.send(mouse_arr);
mouse_arr = [];
};
mouseMove = function(x, y) {
@ -650,7 +626,9 @@ mouseMove = function(x, y) {
if (conf.view_only) { return; } // View only, skip mouse events
mouse_arr = mouse_arr.concat(
pointerEvent(display.absX(x), display.absY(y)) );
pointerEvent(display.absX(x), display.absY(y)));
checkEvents();
};
@ -870,6 +848,7 @@ init_msg = function() {
/* Connection name/title */
name_length = ws.rQshift32();
fb_name = ws.rQshiftStr(name_length);
conf.onDesktopName(that, fb_name);
if (conf.true_color && fb_name === "Intel(r) AMT KVM")
{
@ -878,6 +857,7 @@ init_msg = function() {
}
display.set_true_color(conf.true_color);
conf.onFBResize(that, fb_width, fb_height);
display.resize(fb_width, fb_height);
keyboard.grab();
mouse.grab();
@ -892,13 +872,12 @@ init_msg = function() {
response = pixelFormat();
response = response.concat(clientEncodings());
response = response.concat(fbUpdateRequests());
response = response.concat(fbUpdateRequests()); // initial fbu-request
timing.fbu_rt_start = (new Date()).getTime();
timing.pixels = 0;
ws.send(response);
/* Start pushing/polling */
setTimeout(checkEvents, conf.check_rate);
checkEvents();
if (conf.encrypt) {
updateState('normal', "Connected (encrypted) to: " + fb_name);
@ -926,6 +905,10 @@ normal_msg = function() {
switch (msg_type) {
case 0: // FramebufferUpdate
ret = framebufferUpdate(); // false means need more data
if (ret) {
// only allow one outstanding fbu-request at a time
ws.send(fbUpdateRequests());
}
break;
case 1: // SetColourMapEntries
Util.Debug("SetColourMapEntries");
@ -1122,6 +1105,7 @@ encHandlers.COPYRECT = function display_copy_rect() {
var old_x, old_y;
FBU.bytes = 4;
if (ws.rQwait("COPYRECT", 4)) { return false; }
display.renderQ_push({
'type': 'copy',
@ -1141,6 +1125,7 @@ encHandlers.RRE = function display_rre() {
var color, x, y, width, height, chunk;
if (FBU.subrects === 0) {
FBU.bytes = 4+fb_Bpp;
if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
FBU.subrects = ws.rQshift32();
color = ws.rQshiftBytes(fb_Bpp); // Background
@ -1357,6 +1342,45 @@ function display_tight(isTightPNG) {
return uncompressed.data;
}
var indexedToRGB = function (data, numColors, palette, width, height) {
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
var dest = [];
var x, y, b, w, w1, dp, sp;
if (numColors === 2) {
w = Math.floor((width + 7) / 8);
w1 = Math.floor(width / 8);
for (y = 0; y < height; y++) {
for (x = 0; x < w1; x++) {
for (b = 7; b >= 0; b--) {
dp = (y*width + x*8 + 7-b) * 3;
sp = (data[y*w + x] >> b & 1) * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
for (b = 7; b >= 8 - width % 8; b--) {
dp = (y*width + x*8 + 7-b) * 3;
sp = (data[y*w + x] >> b & 1) * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
} else {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
dp = (y*width + x) * 3;
sp = data[y*width + x] * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
}
return dest;
};
var handlePalette = function() {
var numColors = rQ[rQi + 2] + 1;
var paletteSize = numColors * fb_depth;
@ -1388,45 +1412,12 @@ function display_tight(isTightPNG) {
}
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
var dest = [];
var x, y, b, w, w1, dp, sp;
if (numColors === 2) {
w = Math.floor((FBU.width + 7) / 8);
w1 = Math.floor(FBU.width / 8);
for (y = 0; y < FBU.height; y++) {
for (x = 0; x < w1; x++) {
for (b = 7; b >= 0; b--) {
dp = (y*FBU.width + x*8 + 7-b) * 3;
sp = (data[y*w + x] >> b & 1) * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
for (b = 7; b >= 8 - FBU.width % 8; b--) {
dp = (y*FBU.width + x*8 + 7-b) * 3;
sp = (data[y*w + x] >> b & 1) * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
} else {
for (y = 0; y < FBU.height; y++) {
for (x = 0; x < FBU.width; x++) {
dp = (y*FBU.width + x) * 3;
sp = data[y*FBU.width + x] * 3;
dest[dp ] = palette[sp ];
dest[dp+1] = palette[sp+1];
dest[dp+2] = palette[sp+2];
}
}
}
var rgb = indexedToRGB(data, numColors, palette, FBU.width, FBU.height);
// Add it to the render queue
display.renderQ_push({
'type': 'blitRgb',
'data': dest,
'data': rgb,
'x': FBU.x,
'y': FBU.y,
'width': FBU.width,
@ -1577,10 +1568,9 @@ encHandlers.DesktopSize = function set_desktopsize() {
Util.Debug(">> set_desktopsize");
fb_width = FBU.width;
fb_height = FBU.height;
conf.onFBResize(that, fb_width, fb_height);
display.resize(fb_width, fb_height);
timing.fbu_rt_start = (new Date()).getTime();
// Send a new non-incremental request
ws.send(fbUpdateRequests());
FBU.bytes = 0;
FBU.rects -= 1;
@ -1663,6 +1653,11 @@ clientEncodings = function() {
if ((encodings[i][0] === "Cursor") &&
(! conf.local_cursor)) {
Util.Debug("Skipping Cursor pseudo-encoding");
// TODO: remove this when we have tight+non-true-color
} else if ((encodings[i][0] === "TIGHT") &&
(! conf.true_color)) {
Util.Warn("Skipping tight, only support with true color");
} else {
//Util.Debug("Adding encoding: " + encodings[i][0]);
encList.push(encodings[i][1]);
@ -1801,7 +1796,6 @@ that.sendCtrlAltDel = function() {
arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
arr = arr.concat(fbUpdateRequests());
ws.send(arr);
};
@ -1818,7 +1812,6 @@ that.sendKey = function(code, down) {
arr = arr.concat(keyEvent(code, 1));
arr = arr.concat(keyEvent(code, 0));
}
arr = arr.concat(fbUpdateRequests());
ws.send(arr);
};
@ -1830,15 +1823,16 @@ that.clipboardPasteFrom = function(text) {
};
// Override internal functions for testing
that.testMode = function(override_send) {
that.testMode = function(override_send, data_mode) {
test_mode = true;
that.recv_message = ws.testMode(override_send);
that.recv_message = ws.testMode(override_send, data_mode);
checkEvents = function () { /* Stub Out */ };
that.connect = function(host, port, password) {
rfb_host = host;
rfb_port = port;
rfb_password = password;
init_vars();
updateState('ProtocolVersion', "Starting VNC handshake");
};
};

View File

@ -1,7 +1,8 @@
/*
* noVNC: HTML5 VNC client
* 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.
*/
@ -10,17 +11,37 @@
/*jslint white: false, browser: true */
/*global window, $D, Util, WebUtil, RFB, Display */
// Load supporting scripts
window.onscriptsload = function () { UI.load(); };
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"input.js", "display.js", "jsunzip.js", "rfb.js",
"keysym.js"]);
var UI = {
rfb_state : 'loaded',
settingsOpen : false,
connSettingsOpen : false,
popupStatusOpen : false,
clipboardOpen: false,
keyboardVisible: false,
hideKeyboardTimeout: null,
extraKeysVisible: false,
ctrlOn: false,
altOn: false,
isTouchDevice: false,
// Setup rfb object, load settings from browser storage, then call
// UI.init to setup the UI/menus
load: function (callback) {
WebUtil.initSettings(UI.start, callback);
},
// Render default UI and initialize settings menu
load: function() {
var html = '', i, sheet, sheets, llevels;
start: function(callback) {
var html = '', i, sheet, sheets, llevels, port, autoconnect;
UI.isTouchDevice = 'ontouchstart' in document.documentElement;
// Stylesheet selection dropdown
sheet = WebUtil.selectStylesheet();
@ -44,13 +65,25 @@ load: function() {
// 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
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;
}
}
/* Populate the controls if defaults are provided in the URL */
UI.initSetting('host', window.location.hostname);
UI.initSetting('port', window.location.port);
UI.initSetting('port', port);
UI.initSetting('password', '');
UI.initSetting('encrypt', (window.location.protocol === "https:"));
UI.initSetting('true_color', true);
UI.initSetting('cursor', false);
UI.initSetting('cursor', !UI.isTouchDevice);
UI.initSetting('shared', true);
UI.initSetting('view_only', false);
UI.initSetting('connectTimeout', 2);
@ -59,7 +92,17 @@ load: function() {
UI.rfb = RFB({'target': $D('noVNC_canvas'),
'onUpdateState': UI.updateState,
'onClipboard': UI.clipReceive});
'onClipboard': UI.clipReceive,
'onDesktopName': UI.updateDocumentTitle});
autoconnect = WebUtil.getQueryVar('autoconnect', false);
if (autoconnect === 'true' || autoconnect == '1') {
autoconnect = true;
UI.connect();
} else {
autoconnect = false;
}
UI.updateVisualState();
// Unfocus clipboard when over the VNC area
@ -71,7 +114,7 @@ load: function() {
// };
// Show mouse selector buttons on touch screen devices
if ('ontouchstart' in document.documentElement) {
if (UI.isTouchDevice) {
// Show mobile buttons
$D('noVNC_mobile_buttons').style.display = "inline";
UI.setMouseButton();
@ -91,6 +134,7 @@ load: function() {
//UI.setOnscroll();
//UI.setResize();
}
UI.setBarPosition();
$D('noVNC_host').focus();
@ -104,21 +148,69 @@ load: function() {
} );
// Show description by default when hosted at for kanaka.github.com
if (location.host === "kanaka.github.com") {
if (location.host === "kanaka.github.io") {
// Open the description dialog
$D('noVNC_description').style.display = "block";
} else {
// Open the connect panel on first load
UI.toggleConnectPanel();
// Show the connect panel on first load unless autoconnecting
if (autoconnect === UI.connSettingsOpen) {
UI.toggleConnectPanel();
}
}
// Add mouse event click/focus/blur event handlers to the UI
UI.addMouseHandlers();
if (typeof callback === "function") {
callback(UI.rfb);
}
},
addMouseHandlers: function() {
// Setup interface handlers that can't be inline
$D("noVNC_view_drag_button").onclick = UI.setViewDrag;
$D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
$D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
$D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
$D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
$D("showKeyboard").onclick = UI.showKeyboard;
$D("keyboardinput").oninput = UI.keyInput;
$D("keyboardinput").onblur = UI.keyInputBlur;
$D("showExtraKeysButton").onclick = UI.showExtraKeys;
$D("toggleCtrlButton").onclick = UI.toggleCtrl;
$D("toggleAltButton").onclick = UI.toggleAlt;
$D("sendTabButton").onclick = UI.sendTab;
$D("sendEscButton").onclick = UI.sendEsc;
$D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
$D("noVNC_status").onclick = UI.togglePopupStatusPanel;
$D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
$D("clipboardButton").onclick = UI.toggleClipboardPanel;
$D("settingsButton").onclick = UI.toggleSettingsPanel;
$D("connectButton").onclick = UI.toggleConnectPanel;
$D("disconnectButton").onclick = UI.disconnect;
$D("descriptionButton").onclick = UI.toggleConnectPanel;
$D("noVNC_clipboard_text").onfocus = UI.displayBlur;
$D("noVNC_clipboard_text").onblur = UI.displayFocus;
$D("noVNC_clipboard_text").onchange = UI.clipSend;
$D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
$D("noVNC_settings_menu").onmouseover = UI.displayBlur;
$D("noVNC_settings_menu").onmouseover = UI.displayFocus;
$D("noVNC_apply").onclick = UI.settingsApply;
$D("noVNC_connect_button").onclick = UI.connect;
},
// 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 = WebUtil.readSetting(name);
if (val !== null && ctrl.type === 'checkbox') {
if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
val = false;
} else {
val = true;
@ -134,7 +226,7 @@ updateSetting: function(name, value) {
var i, ctrl = $D('noVNC_' + name);
// Save the cookie for this session
if (typeof value !== 'undefined') {
WebUtil.createCookie(name, value);
WebUtil.writeSetting(name, value);
}
// Update the settings control
@ -170,7 +262,7 @@ saveSetting: function(name) {
} else {
val = ctrl.value;
}
WebUtil.createCookie(name, val);
WebUtil.writeSetting(name, val);
//Util.Debug("Setting saved '" + name + "=" + val + "'");
return val;
},
@ -182,7 +274,7 @@ initSetting: function(name, defVal) {
// Check Query string followed by cookie
val = WebUtil.getQueryVar(name);
if (val === null) {
val = WebUtil.readCookie(name, defVal);
val = WebUtil.readSetting(name, defVal);
}
UI.updateSetting(name, val);
//Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
@ -196,20 +288,39 @@ forceSetting: function(name, val) {
},
// Show the popup status panel
togglePopupStatusPanel: function() {
var psp = $D('noVNC_popup_status_panel');
if (UI.popupStatusOpen === true) {
psp.style.display = "none";
UI.popupStatusOpen = false;
} else {
psp.innerHTML = $D('noVNC_status').innerHTML;
psp.style.display = "block";
psp.style.left = window.innerWidth/2 -
parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
UI.popupStatusOpen = true;
}
},
// Show the clipboard panel
toggleClipboardPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
//Close settings if open
// Close settings if open
if (UI.settingsOpen === true) {
UI.settingsApply();
UI.closeSettingsMenu();
}
//Close connection settings if open
// Close connection settings if open
if (UI.connSettingsOpen === true) {
UI.toggleConnectPanel();
}
//Toggle Clipboard Panel
// Close popup status panel if open
if (UI.popupStatusOpen === true) {
UI.togglePopupStatusPanel();
}
// Toggle Clipboard Panel
if (UI.clipboardOpen === true) {
$D('noVNC_clipboard').style.display = "none";
$D('clipboardButton').className = "noVNC_status_button";
@ -225,21 +336,29 @@ toggleClipboardPanel: function() {
toggleConnectPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
//Close connection settings if open
// 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();
}
//Toggle Connection Panel
// 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";
@ -263,7 +382,7 @@ toggleSettingsPanel: function() {
if (UI.rfb.get_display().get_cursor_uri()) {
UI.updateSetting('cursor');
} else {
UI.updateSetting('cursor', false);
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
}
UI.updateSetting('clip');
@ -283,13 +402,18 @@ toggleSettingsPanel: function() {
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
// Close connection settings if open
if (UI.connSettingsOpen === true) {
UI.toggleConnectPanel();
}
// Close popup status panel if open
if (UI.popupStatusOpen === true) {
UI.togglePopupStatusPanel();
}
$D('noVNC_settings').style.display = "block";
$D('settingsButton').className = "noVNC_status_button_selected";
UI.settingsOpen = true;
@ -373,8 +497,6 @@ setMouseButton: function(num) {
updateState: function(rfb, state, oldstate, msg) {
var s, sb, c, d, cad, vd, klass;
UI.rfb_state = state;
s = $D('noVNC_status');
sb = $D('noVNC_status_bar');
switch (state) {
case 'failed':
case 'fatal':
@ -404,9 +526,8 @@ updateState: function(rfb, state, oldstate, msg) {
}
if (typeof(msg) !== 'undefined') {
s.setAttribute("class", klass);
sb.setAttribute("class", klass);
s.innerHTML = msg;
$D('noVNC-control-bar').setAttribute("class", klass);
$D('noVNC_status').innerHTML = msg;
}
UI.updateVisualState();
@ -423,7 +544,7 @@ updateVisualState: function() {
UI.rfb.get_display().get_cursor_uri()) {
$D('noVNC_cursor').disabled = connected;
} else {
UI.updateSetting('cursor', false);
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
}
$D('noVNC_shared').disabled = connected;
@ -437,13 +558,16 @@ updateVisualState: function() {
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";
}
// State change disables viewport dragging.
// It is enabled (toggled) by direct click on the button
UI.setViewDrag(false);
@ -466,6 +590,12 @@ updateVisualState: function() {
},
// 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;
@ -584,7 +714,8 @@ setViewDrag: function(drag) {
vmb.style.display = "none";
}
if (typeof(drag) === "undefined") {
if (typeof(drag) === "undefined" ||
typeof(drag) === "object") {
// If not specified, then toggle
drag = !UI.rfb.get_viewportDrag();
}
@ -599,23 +730,123 @@ setViewDrag: function(drag) {
// On touch devices, show the OS keyboard
showKeyboard: function() {
var kbi, skb, l;
kbi = $D('keyboardinput');
skb = $D('showKeyboard');
l = kbi.value.length;
if(UI.keyboardVisible === false) {
$D('keyboardinput').focus();
kbi.focus();
kbi.setSelectionRange(l, l); // Move the caret to the end
UI.keyboardVisible = true;
$D('showKeyboard').className = "noVNC_status_button_selected";
skb.className = "noVNC_status_button_selected";
} else if(UI.keyboardVisible === true) {
$D('keyboardinput').blur();
$D('showKeyboard').className = "noVNC_status_button";
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";
}
},
// When keypress events are left uncought, catch the input events from
// the keyboardinput element instead and send the corresponding key events.
keyInput: function(event) {
var elem, input, len;
elem = $D('keyboardinput');
input = event.target.value;
len = (elem.selectionStart > input.length) ? elem.selectionStart : input.length;
if (len < 1) { // something removed?
UI.rfb.sendKey(0xff08); // send BACKSPACE
} else if (len > 1) { // new input?
for (var i = len-1; i > 0; i -= 1) {
// HTML does not consider trailing whitespaces as a part of the string
// and they are therefore undefined.
if (input[len-i] !== undefined) {
UI.rfb.sendKey(input.charCodeAt(len-i)); // send charCode
} else {
UI.rfb.sendKey(0x0020); // send SPACE
}
}
}
// In order to be able to delete text which has been written in
// another session there has to always be text in the
// keyboardinput element with which backspace can interact.
// We also need to reset the input field text to avoid overflow.
elem.value = "x";
},
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);
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() {

View File

@ -1,7 +1,7 @@
/*
* noVNC: HTML5 VNC client
* 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.
*/
@ -207,6 +207,65 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
* Cross-browser routines
*/
// Dynamically load scripts without using document.write()
// Reference: http://unixpapa.com/js/dyna.html
//
// Handles the case where load_scripts is invoked from a script that
// itself is loaded via load_scripts. Once all scripts are loaded the
// window.onscriptsloaded handler is called (if set).
Util.get_include_uri = function() {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
}
Util._loading_scripts = [];
Util._pending_scripts = [];
Util.load_scripts = function(files) {
var head = document.getElementsByTagName('head')[0], script,
ls = Util._loading_scripts, ps = Util._pending_scripts;
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 = 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();
}
}
}
};
// 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);
}
}
// Get DOM element position on page
Util.getPosition = function (obj) {
var x = 0, y = 0;
@ -239,7 +298,11 @@ Util.getEventPosition = function (e, obj, scale) {
if (typeof scale === "undefined") {
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};
};

View File

@ -1,49 +1,69 @@
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
// License: New BSD License
// Reference: http://dev.w3.org/html5/websockets/
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
// Reference: http://tools.ietf.org/html/rfc6455
(function() {
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) return;
var console = window.console;
if (!console || !console.log || !console.error) {
console = {log: function(){ }, error: function(){ }};
if (window.WEB_SOCKET_FORCE_FLASH) {
// Keeps going.
} else if (window.WebSocket) {
return;
} else if (window.MozWebSocket) {
// Firefox.
window.WebSocket = MozWebSocket;
return;
}
if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
console.error("Flash Player >= 10.0.0 is required.");
var logger;
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;
}
if (location.protocol == "file:") {
console.error(
logger.error(
"WARNING: web-socket-js doesn't work in file:///... URL " +
"unless you set Flash Security Settings properly. " +
"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} protocol
* @param {array or string} protocols
* @param {string} proxyHost
* @param {int} proxyPort
* @param {string} headers
*/
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this;
self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
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.
// Otherwise, when onopen fires immediately, onopen is called before it is set.
setTimeout(function() {
self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create(
self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
});
}, 0);
};
@ -78,6 +98,12 @@
* Close this web socket gracefully.
*/
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) {
return;
}
@ -131,7 +157,7 @@
events[i](event);
}
var handler = this["on" + event.type];
if (handler) handler(event);
if (handler) handler.apply(this, [event]);
};
/**
@ -139,16 +165,22 @@
* @param {Object} flashEvent
*/
WebSocket.prototype.__handleEvent = function(flashEvent) {
if ("readyState" in flashEvent) {
this.readyState = flashEvent.readyState;
}
if ("protocol" in flashEvent) {
this.protocol = flashEvent.protocol;
}
var jsEvent;
if (flashEvent.type == "open" || flashEvent.type == "error") {
jsEvent = this.__createSimpleEvent(flashEvent.type);
} else if (flashEvent.type == "close") {
// TODO implement jsEvent.wasClean
jsEvent = this.__createSimpleEvent("close");
jsEvent.wasClean = flashEvent.wasClean ? true : false;
jsEvent.code = flashEvent.code;
jsEvent.reason = flashEvent.reason;
} else if (flashEvent.type == "message") {
var data = decodeURIComponent(flashEvent.message);
jsEvent = this.__createMessageEvent("message", data);
@ -157,6 +189,7 @@
}
this.dispatchEvent(jsEvent);
};
WebSocket.prototype.__createSimpleEvent = function(type) {
@ -188,6 +221,9 @@
WebSocket.CLOSING = 2;
WebSocket.CLOSED = 3;
// Field to check implementation of WebSocket.
WebSocket.__isFlashImplementation = true;
WebSocket.__initialized = false;
WebSocket.__flash = null;
WebSocket.__instances = {};
WebSocket.__tasks = [];
@ -207,16 +243,31 @@
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
*/
WebSocket.__initialize = function() {
if (WebSocket.__flash) return;
if (WebSocket.__initialized) return;
WebSocket.__initialized = true;
if (WebSocket.__swfLocation) {
// For backword compatibility.
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
}
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;
}
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");
container.id = "webSocketContainer";
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
@ -250,9 +301,11 @@
null,
function(e) {
if (!e.success) {
console.error("[WebSocket] swfobject.embedSWF failed");
logger.error("[WebSocket] swfobject.embedSWF failed");
}
});
}
);
};
/**
@ -287,7 +340,7 @@
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
}
} catch (e) {
console.error(e);
logger.error(e);
}
}, 0);
return true;
@ -295,12 +348,12 @@
// Called by Flash.
WebSocket.__log = function(message) {
console.log(decodeURIComponent(message));
logger.log(decodeURIComponent(message));
};
// Called by Flash.
WebSocket.__error = function(message) {
console.error(decodeURIComponent(message));
logger.error(decodeURIComponent(message));
};
WebSocket.__addTask = function(task) {
@ -327,15 +380,12 @@
};
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
if (window.addEventListener) {
window.addEventListener("load", function(){
WebSocket.__initialize();
}, false);
} else {
window.attachEvent("onload", function(){
WebSocket.__initialize();
});
}
// NOTE:
// This fires immediately if web_socket.js is dynamically loaded after
// the document is loaded.
swfobject.addDomLoadEvent(function() {
WebSocket.__initialize();
});
}
})();

View File

@ -1,7 +1,7 @@
/*
* Websock: high-performance binary WebSockets
* 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
* enables communication with raw TCP sockets (i.e. the binary stream)
@ -35,23 +35,14 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
Websock_native = false;
(function () {
function 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() +
window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
"web-socket-js/WebSocketMain.swf";
if (Util.Engine.trident) {
Util.Debug("Forcing uncached load of WebSocketMain.swf");
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
}
extra += start + "web-socket-js/swfobject.js" + end;
extra += start + "web-socket-js/web_socket.js" + end;
document.write(extra);
Util.load_scripts(["web-socket-js/swfobject.js",
"web-socket-js/web_socket.js"]);
}());
}
@ -61,6 +52,7 @@ function Websock() {
var api = {}, // Public API
websocket = null, // WebSocket object
mode = 'base64', // Current WebSocket mode: 'binary', 'base64'
rQ = [], // Receive queue
rQi = 0, // Receive queue index
rQmax = 10000, // Max receive queue size before compacting
@ -127,9 +119,7 @@ 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('');
return String.fromCharCode.apply(null, arr);
}
function rQshiftBytes(len) {
if (typeof(len) === 'undefined') { len = rQlen(); }
@ -169,14 +159,27 @@ function rQwait(msg, num, goback) {
//
function encode_message() {
/* base64 encode */
return Base64.encode(sQ);
if (mode === 'binary') {
// Put in a binary arraybuffer
return (new Uint8Array(sQ)).buffer;
} else {
// base64 encode
return Base64.encode(sQ);
}
}
function decode_message(data) {
//Util.Debug(">> decode_message: " + data);
/* base64 decode */
rQ = rQ.concat(Base64.decode(data, 0));
if (mode === 'binary') {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
for (var i = 0; i < u8.length; i++) {
rQ.push(u8[i]);
}
} else {
// base64 decode and concat to the end
rQ = rQ.concat(Base64.decode(data, 0));
}
//Util.Debug(">> decode_message, rQ: " + rQ);
}
@ -259,30 +262,88 @@ function on(evt, handler) {
eventHandlers[evt] = handler;
}
function init() {
function init(protocols) {
rQ = [];
rQi = 0;
sQ = [];
websocket = null;
var bt = false,
wsbt = false,
try_binary = false;
// Check for full typed array support
if (('Uint8Array' in window) &&
('set' in Uint8Array.prototype)) {
bt = true;
}
// Check for full binary type support in WebSockets
// TODO: this sucks, the property should exist on the prototype
// but it does not.
try {
if (bt && ('binaryType' in (new WebSocket("ws://localhost:17523")))) {
Util.Info("Detected binaryType support in WebSockets");
wsbt = true;
}
} catch (exc) {
// Just ignore failed test localhost connections
}
// Default protocols if not specified
if (typeof(protocols) === "undefined") {
if (wsbt) {
protocols = ['binary', 'base64'];
} else {
protocols = 'base64';
}
}
// If no binary support, make sure it was not requested
if (!wsbt) {
if (protocols === 'binary') {
throw("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("Only WebSocket binary sub-protocol was requested and not supported.");
}
}
}
return protocols;
}
function open(uri) {
init();
function open(uri, protocols) {
protocols = init(protocols);
if (test_mode) {
websocket = {};
} else {
websocket = new WebSocket(uri, 'base64');
// TODO: future native binary support
//websocket = new WebSocket(uri, ['binary', 'base64']);
websocket = new WebSocket(uri, protocols);
if (protocols.indexOf('binary') >= 0) {
websocket.binaryType = 'arraybuffer';
}
}
websocket.onmessage = recv_message;
websocket.onopen = function() {
Util.Debug(">> WebSock.onopen");
if (websocket.protocol) {
mode = websocket.protocol;
Util.Info("Server chose sub-protocol: " + websocket.protocol);
} else {
mode = 'base64';
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
}
eventHandlers.open();
@ -313,8 +374,9 @@ function close() {
// Override internal functions for testing
// Takes a send function, returns reference to recv function
function testMode(override_send) {
function testMode(override_send, data_mode) {
test_mode = true;
mode = data_mode;
api.send = override_send;
api.close = function () {};
return recv_message;

View File

@ -1,7 +1,8 @@
/*
* noVNC: HTML5 VNC client
* 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.
*/
@ -37,14 +38,16 @@ if (!window.$D) {
*/
// init log level reading the logging HTTP param
WebUtil.init_logging = function() {
Util._log_level = (document.location.href.match(
/logging=([A-Za-z0-9\._\-]*)/) ||
['', Util._log_level])[1];
WebUtil.init_logging = function(level) {
if (typeof level !== "undefined") {
Util._log_level = level;
} else {
Util._log_level = (document.location.href.match(
/logging=([A-Za-z0-9\._\-]*)/) ||
['', Util._log_level])[1];
}
Util.init_logging();
};
WebUtil.init_logging();
WebUtil.dirObj = function (obj, depth, parent) {
@ -75,9 +78,14 @@ WebUtil.dirObj = function (obj, depth, parent) {
// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
return (document.location.href.match(re) || ['',defVal])[1];
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
@ -87,16 +95,20 @@ WebUtil.getQueryVar = function(name, defVal) {
// No days means only for this browser session
WebUtil.createCookie = function(name,value,days) {
var date, expires;
var date, expires, secure;
if (days) {
date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
}
else {
} else {
expires = "";
}
document.cookie = name+"="+value+expires+"; path=/";
if (document.location.protocol === "https:") {
secure = "; secure";
} else {
secure = "";
}
document.cookie = name+"="+value+expires+"; path=/"+secure;
};
WebUtil.readCookie = function(name, defaultValue) {
@ -113,6 +125,67 @@ WebUtil.eraseCookie = function(name) {
WebUtil.createCookie(name,"",-1);
};
/*
* Setting handling.
*/
WebUtil.initSettings = function(callback) {
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) {
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) {
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) {
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name);
delete WebUtil.settings[name];
} else {
localStorage.removeItem(name);
}
};
/*
* Alternate stylesheet selection
*/