diff --git a/src/sunstone/public/vendor/noVNC/LICENSE.txt b/src/sunstone/public/vendor/noVNC/LICENSE.txt index 67cdca5ecd..011c70a9dd 100644 --- a/src/sunstone/public/vendor/noVNC/LICENSE.txt +++ b/src/sunstone/public/vendor/noVNC/LICENSE.txt @@ -1,7 +1,7 @@ noVNC is Copyright (C) 2011 Joel Martin -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 diff --git a/src/sunstone/public/vendor/noVNC/base.css b/src/sunstone/public/vendor/noVNC/base.css index 3a2feb33cd..f57d20e39b 100644 --- a/src/sunstone/public/vendor/noVNC/base.css +++ b/src/sunstone/public/vendor/noVNC/base.css @@ -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; } diff --git a/src/sunstone/public/vendor/noVNC/base64.js b/src/sunstone/public/vendor/noVNC/base64.js index e9b3c522d9..5a6890ad29 100644 --- a/src/sunstone/public/vendor/noVNC/base64.js +++ b/src/sunstone/public/vendor/noVNC/base64.js @@ -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 (original author) - * Samuel Sieb - * - * 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) { diff --git a/src/sunstone/public/vendor/noVNC/black.css b/src/sunstone/public/vendor/noVNC/black.css index e958ee3802..7d940c5af8 100644 --- a/src/sunstone/public/vendor/noVNC/black.css +++ b/src/sunstone/public/vendor/noVNC/black.css @@ -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; diff --git a/src/sunstone/public/vendor/noVNC/blue.css b/src/sunstone/public/vendor/noVNC/blue.css index 3dad0b41cc..b2a0adcc92 100644 --- a/src/sunstone/public/vendor/noVNC/blue.css +++ b/src/sunstone/public/vendor/noVNC/blue.css @@ -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; diff --git a/src/sunstone/public/vendor/noVNC/display.js b/src/sunstone/public/vendor/noVNC/display.js index 5ad99ba468..9f2d6b89b8 100644 --- a/src/sunstone/public/vendor/noVNC/display.js +++ b/src/sunstone/public/vendor/noVNC/display.js @@ -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 + } } } } diff --git a/src/sunstone/public/vendor/noVNC/input.js b/src/sunstone/public/vendor/noVNC/input.js index 9298dfefe7..8f0c650fc3 100644 --- a/src/sunstone/public/vendor/noVNC/input.js +++ b/src/sunstone/public/vendor/noVNC/input.js @@ -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', diff --git a/src/sunstone/public/vendor/noVNC/jsunzip.js b/src/sunstone/public/vendor/noVNC/jsunzip.js index f815218fa9..8968f866aa 100755 --- a/src/sunstone/public/vendor/noVNC/jsunzip.js +++ b/src/sunstone/public/vendor/noVNC/jsunzip.js @@ -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 */ diff --git a/src/sunstone/public/vendor/noVNC/playback.js b/src/sunstone/public/vendor/noVNC/playback.js index a21c7b6822..7756529d8c 100644 --- a/src/sunstone/public/vendor/noVNC/playback.js +++ b/src/sunstone/public/vendor/noVNC/playback.js @@ -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(); diff --git a/src/sunstone/public/vendor/noVNC/rfb.js b/src/sunstone/public/vendor/noVNC/rfb.js index 00fb7d8852..98328779c6 100644 --- a/src/sunstone/public/vendor/noVNC/rfb.js +++ b/src/sunstone/public/vendor/noVNC/rfb.js @@ -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"); }; }; diff --git a/src/sunstone/public/vendor/noVNC/ui.js b/src/sunstone/public/vendor/noVNC/ui.js index dc837e6001..ad004e9392 100644 --- a/src/sunstone/public/vendor/noVNC/ui.js +++ b/src/sunstone/public/vendor/noVNC/ui.js @@ -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() { diff --git a/src/sunstone/public/vendor/noVNC/util.js b/src/sunstone/public/vendor/noVNC/util.js index 57ccb54080..8893591c44 100644 --- a/src/sunstone/public/vendor/noVNC/util.js +++ b/src/sunstone/public/vendor/noVNC/util.js @@ -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 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}; }; diff --git a/src/sunstone/public/vendor/noVNC/web-socket-js/WebSocketMain.swf b/src/sunstone/public/vendor/noVNC/web-socket-js/WebSocketMain.swf index 244c445bad..f286c81aac 100644 Binary files a/src/sunstone/public/vendor/noVNC/web-socket-js/WebSocketMain.swf and b/src/sunstone/public/vendor/noVNC/web-socket-js/WebSocketMain.swf differ diff --git a/src/sunstone/public/vendor/noVNC/web-socket-js/web_socket.js b/src/sunstone/public/vendor/noVNC/web-socket-js/web_socket.js index a13301339b..06cc5d0270 100644 --- a/src/sunstone/public/vendor/noVNC/web-socket-js/web_socket.js +++ b/src/sunstone/public/vendor/noVNC/web-socket-js/web_socket.js @@ -1,49 +1,69 @@ // Copyright: Hiroshi Ichikawa // 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(); + }); } })(); diff --git a/src/sunstone/public/vendor/noVNC/websock.js b/src/sunstone/public/vendor/noVNC/websock.js index 20d51d6a2d..01a24c3faa 100644 --- a/src/sunstone/public/vendor/noVNC/websock.js +++ b/src/sunstone/public/vendor/noVNC/websock.js @@ -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 = "