Merge pull request #368 from DirectXMan12/refactor/cleanup
Cleanup and test all the things (plus ditching Crockford)!
This commit is contained in:
commit
0ca7cf4867
|
@ -0,0 +1,13 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '0.11'
|
||||
env:
|
||||
matrix:
|
||||
- TEST_BROWSER_NAME=PhantomJS
|
||||
- TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 7,Linux'
|
||||
- TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 7,Linux' TEST_BROWSER_VERSION='30,26'
|
||||
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 7' TEST_BROWSER_VERSION=10
|
||||
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 8.1' TEST_BROWSER_VERSION=11
|
||||
- TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.8' TEST_BROWSER_VERSION=6
|
||||
- TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.9' TEST_BROWSER_VERSION=7
|
||||
before_script: npm install -g karma-cli
|
|
@ -1,5 +1,6 @@
|
|||
## noVNC: HTML5 VNC Client
|
||||
|
||||
[](https://travis-ci.org/kanaka/noVNC)
|
||||
|
||||
### Description
|
||||
|
||||
|
|
|
@ -4,112 +4,110 @@
|
|||
|
||||
// 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 */
|
||||
/*jslint white: false */
|
||||
/*global console */
|
||||
|
||||
var Base64 = {
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '';
|
||||
var toBase64Table = Base64.toBase64Table;
|
||||
var length = data.length;
|
||||
var lengthpad = (length % 3);
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
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 += 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 (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;
|
||||
},
|
||||
|
||||
/* Convert Base64 data to a string */
|
||||
toBinaryTable : [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var 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; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5);
|
||||
result = new Array(result_length);
|
||||
|
||||
// Convert one by one.
|
||||
/* BEGIN LOOP */
|
||||
for (idx = 0, i = offset; i < data.length; i++) {
|
||||
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);
|
||||
continue;
|
||||
for (var i = 0; i < (length - 2); i += 3) {
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
||||
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
||||
result += toBase64Table[data[i + 2] & 0x3f];
|
||||
}
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding) {
|
||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
var j = 0;
|
||||
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;
|
||||
},
|
||||
|
||||
/* Convert Base64 data to a string */
|
||||
/* jshint -W013 */
|
||||
toBinaryTable : [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
/* jshint +W013 */
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var toBinaryTable = Base64.toBinaryTable;
|
||||
var base64Pad = Base64.base64Pad;
|
||||
var result, result_length;
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
var data_length = data.indexOf('=') - offset;
|
||||
|
||||
if (data_length < 0) { data_length = data.length - offset; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||
result = new Array(result_length);
|
||||
|
||||
// Convert one by one.
|
||||
for (var idx = 0, i = offset; i < data.length; i++) {
|
||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||
var padding = (data.charAt(i) === base64Pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c === -1) {
|
||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding) {
|
||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||
}
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits) {
|
||||
err = new Error('Corrupted base64 string');
|
||||
err.name = 'Base64-Error';
|
||||
throw err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/* END LOOP */
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits) {
|
||||
throw {name: 'Base64-Error',
|
||||
message: 'Corrupted base64 string'};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}; /* End of Base64 namespace */
|
||||
|
|
349
include/des.js
349
include/des.js
|
@ -75,199 +75,202 @@
|
|||
* fine Java utilities: http://www.acme.com/java/
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint white: false, bitwise: false, plusplus: false */
|
||||
/* jslint white: false */
|
||||
|
||||
function DES(passwd) {
|
||||
"use strict";
|
||||
|
||||
// Tables, permutations, S-boxes, etc.
|
||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
||||
keys = [];
|
||||
// Tables, permutations, S-boxes, etc.
|
||||
// jshint -W013
|
||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
||||
keys = [];
|
||||
|
||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||
// jshint -W015
|
||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||
// jshint +W013,+W015
|
||||
|
||||
// Set the key.
|
||||
function setKeys(keyBlock) {
|
||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||
raw0, raw1, rawi, KnLi;
|
||||
// Set the key.
|
||||
function setKeys(keyBlock) {
|
||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||
raw0, raw1, rawi, KnLi;
|
||||
|
||||
for (j = 0, l = 56; j < 56; ++j, l-=8) {
|
||||
l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1
|
||||
m = l & 0x7;
|
||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||
}
|
||||
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
|
||||
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
||||
m = l & 0x7;
|
||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0;
|
||||
for (o=28; o<59; o+=28) {
|
||||
for (j = o-28; j < o; ++j) {
|
||||
l = j + totrot[i];
|
||||
if (l < o) {
|
||||
pcr[j] = pc1m[l];
|
||||
} else {
|
||||
pcr[j] = pc1m[l - 28];
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0;
|
||||
for (o = 28; o < 59; o += 28) {
|
||||
for (j = o - 28; j < o; ++j) {
|
||||
l = j + totrot[i];
|
||||
if (l < o) {
|
||||
pcr[j] = pc1m[l];
|
||||
} else {
|
||||
pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 24; ++j) {
|
||||
if (pcr[PC2[j]] !== 0) {
|
||||
kn[m] |= 1 << (23 - j);
|
||||
}
|
||||
if (pcr[PC2[j + 24]] !== 0) {
|
||||
kn[n] |= 1 << (23 - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 24; ++j) {
|
||||
if (pcr[PC2[j]] !== 0) {
|
||||
kn[m] |= 1<<(23-j);
|
||||
}
|
||||
if (pcr[PC2[j + 24]] !== 0) {
|
||||
kn[n] |= 1<<(23-j);
|
||||
}
|
||||
|
||||
// cookey
|
||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||
raw0 = kn[rawi++];
|
||||
raw1 = kn[rawi++];
|
||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||
++KnLi;
|
||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
||||
++KnLi;
|
||||
}
|
||||
}
|
||||
|
||||
// cookey
|
||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||
raw0 = kn[rawi++];
|
||||
raw1 = kn[rawi++];
|
||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||
++KnLi;
|
||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
||||
++KnLi;
|
||||
}
|
||||
}
|
||||
// Encrypt 8 bytes of text
|
||||
function enc8(text) {
|
||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
||||
l, r, x; // left, right, accumulator
|
||||
|
||||
// Encrypt 8 bytes of text
|
||||
function enc8(text) {
|
||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
||||
l, r, x; // left, right, accumulator
|
||||
// Squash 8 bytes to 2 ints
|
||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
|
||||
// Squash 8 bytes to 2 ints
|
||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||
r ^= x;
|
||||
l ^= (x << 4);
|
||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||
r ^= x;
|
||||
l ^= (x << 16);
|
||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||
l ^= x;
|
||||
r ^= (x << 2);
|
||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||
l ^= x;
|
||||
r ^= (x << 8);
|
||||
r = (r << 1) | ((r >>> 31) & 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 1) | ((l >>> 31) & 1);
|
||||
|
||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||
r ^= x;
|
||||
l ^= (x << 4);
|
||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||
r ^= x;
|
||||
l ^= (x << 16);
|
||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||
l ^= x;
|
||||
r ^= (x << 2);
|
||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||
l ^= x;
|
||||
r ^= (x << 8);
|
||||
r = (r << 1) | ((r >>> 31) & 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 1) | ((l >>> 31) & 1);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
x = (r << 28) | (r >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = r ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x3f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
l ^= fval;
|
||||
x = (l << 28) | (l >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = l ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x0000003f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
r ^= fval;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
x = (r << 28) | (r >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = r ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x3f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
l ^= fval;
|
||||
x = (l << 28) | (l >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = l ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x0000003f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
r ^= fval;
|
||||
r = (r << 31) | (r >>> 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 31) | (l >>> 1);
|
||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||
r ^= x;
|
||||
l ^= (x << 8);
|
||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||
r ^= x;
|
||||
l ^= (x << 2);
|
||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||
l ^= x;
|
||||
r ^= (x << 16);
|
||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||
l ^= x;
|
||||
r ^= (x << 4);
|
||||
|
||||
// Spread ints to bytes
|
||||
x = [r, l];
|
||||
for (i = 0; i < 8; i++) {
|
||||
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
r = (r << 31) | (r >>> 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 31) | (l >>> 1);
|
||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||
r ^= x;
|
||||
l ^= (x << 8);
|
||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||
r ^= x;
|
||||
l ^= (x << 2);
|
||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||
l ^= x;
|
||||
r ^= (x << 16);
|
||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||
l ^= x;
|
||||
r ^= (x << 4);
|
||||
|
||||
// Spread ints to bytes
|
||||
x = [r, l];
|
||||
for (i = 0; i < 8; i++) {
|
||||
b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256;
|
||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||
// Encrypt 16 bytes of text using passwd as key
|
||||
function encrypt(t) {
|
||||
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Encrypt 16 bytes of text using passwd as key
|
||||
function encrypt(t) {
|
||||
return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16)));
|
||||
}
|
||||
|
||||
setKeys(passwd); // Setup keys
|
||||
return {'encrypt': encrypt}; // Public interface
|
||||
setKeys(passwd); // Setup keys
|
||||
return {'encrypt': encrypt}; // Public interface
|
||||
|
||||
} // function DES
|
||||
|
|
1422
include/display.js
1422
include/display.js
File diff suppressed because it is too large
Load Diff
713
include/input.js
713
include/input.js
|
@ -5,397 +5,384 @@
|
|||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
/*jslint browser: true, white: false, bitwise: false */
|
||||
/*jslint browser: true, white: false */
|
||||
/*global window, Util */
|
||||
|
||||
var Keyboard, Mouse;
|
||||
|
||||
//
|
||||
// Keyboard event handler
|
||||
//
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function Keyboard(defaults) {
|
||||
"use strict";
|
||||
//
|
||||
// Keyboard event handler
|
||||
//
|
||||
|
||||
var that = {}, // Public API methods
|
||||
conf = {}, // Configuration attributes
|
||||
Keyboard = function (defaults) {
|
||||
this._keyDownList = []; // List of depressed keys
|
||||
// (even if they are happy)
|
||||
|
||||
keyDownList = []; // List of depressed keys
|
||||
// (even if they are happy)
|
||||
Util.set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true
|
||||
});
|
||||
|
||||
// Configuration attributes
|
||||
Util.conf_defaults(conf, that, defaults, [
|
||||
['target', 'wo', 'dom', document, 'DOM element that captures keyboard input'],
|
||||
['focused', 'rw', 'bool', true, 'Capture and send key events'],
|
||||
// create the keyboard handler
|
||||
this._handler = new KeyEventDecoder(kbdUtil.ModifierSync(),
|
||||
VerifyCharModifier( /* jshint newcap: false */
|
||||
TrackKeyState(
|
||||
EscapeModifiers(this._handleRfbEvent.bind(this))
|
||||
)
|
||||
)
|
||||
); /* jshint newcap: true */
|
||||
|
||||
['onKeyPress', 'rw', 'func', null, 'Handler for key press/release']
|
||||
]);
|
||||
// keep these here so we can refer to them later
|
||||
this._eventHandlers = {
|
||||
'keyup': this._handleKeyUp.bind(this),
|
||||
'keydown': this._handleKeyDown.bind(this),
|
||||
'keypress': this._handleKeyPress.bind(this),
|
||||
'blur': this._allKeysUp.bind(this)
|
||||
};
|
||||
};
|
||||
|
||||
Keyboard.prototype = {
|
||||
// private methods
|
||||
|
||||
//
|
||||
// Private functions
|
||||
//
|
||||
|
||||
/////// setup
|
||||
|
||||
function onRfbEvent(evt) {
|
||||
if (conf.onKeyPress) {
|
||||
Util.Debug("onKeyPress " + (evt.type == 'keydown' ? "down" : "up")
|
||||
+ ", keysym: " + evt.keysym.keysym + "(" + evt.keysym.keyname + ")");
|
||||
conf.onKeyPress(evt.keysym.keysym, evt.type == 'keydown');
|
||||
}
|
||||
}
|
||||
|
||||
// create the keyboard handler
|
||||
var k = KeyEventDecoder(kbdUtil.ModifierSync(),
|
||||
VerifyCharModifier(
|
||||
TrackKeyState(
|
||||
EscapeModifiers(onRfbEvent)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
function onKeyDown(e) {
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
if (k.keydown(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function onKeyPress(e) {
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
if (k.keypress(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyUp(e) {
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
if (k.keyup(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onOther(e) {
|
||||
k.syncModifiers(e);
|
||||
}
|
||||
|
||||
function allKeysUp() {
|
||||
Util.Debug(">> Keyboard.allKeysUp");
|
||||
|
||||
k.releaseAll();
|
||||
Util.Debug("<< Keyboard.allKeysUp");
|
||||
}
|
||||
|
||||
//
|
||||
// Public API interface functions
|
||||
//
|
||||
|
||||
that.grab = function() {
|
||||
//Util.Debug(">> Keyboard.grab");
|
||||
var c = conf.target;
|
||||
|
||||
Util.addEvent(c, 'keydown', onKeyDown);
|
||||
Util.addEvent(c, 'keyup', onKeyUp);
|
||||
Util.addEvent(c, 'keypress', onKeyPress);
|
||||
|
||||
// Release (key up) if window loses focus
|
||||
Util.addEvent(window, 'blur', allKeysUp);
|
||||
|
||||
//Util.Debug("<< Keyboard.grab");
|
||||
};
|
||||
|
||||
that.ungrab = function() {
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
var c = conf.target;
|
||||
|
||||
Util.removeEvent(c, 'keydown', onKeyDown);
|
||||
Util.removeEvent(c, 'keyup', onKeyUp);
|
||||
Util.removeEvent(c, 'keypress', onKeyPress);
|
||||
Util.removeEvent(window, 'blur', allKeysUp);
|
||||
|
||||
// Release (key up) all keys that are in a down state
|
||||
allKeysUp();
|
||||
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
};
|
||||
|
||||
that.sync = function(e) {
|
||||
k.syncModifiers(e);
|
||||
}
|
||||
|
||||
return that; // Return the public API interface
|
||||
|
||||
} // End of Keyboard()
|
||||
|
||||
|
||||
//
|
||||
// Mouse event handler
|
||||
//
|
||||
|
||||
function Mouse(defaults) {
|
||||
"use strict";
|
||||
|
||||
var that = {}, // Public API methods
|
||||
conf = {}, // Configuration attributes
|
||||
mouseCaptured = false;
|
||||
|
||||
var doubleClickTimer = null,
|
||||
lastTouchPos = null;
|
||||
|
||||
// Configuration attributes
|
||||
Util.conf_defaults(conf, that, defaults, [
|
||||
['target', 'ro', 'dom', document, 'DOM element that captures mouse input'],
|
||||
['notify', 'ro', 'func', null, 'Function to call to notify whenever a mouse event is received'],
|
||||
['focused', 'rw', 'bool', true, 'Capture and send mouse clicks/movement'],
|
||||
['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'],
|
||||
|
||||
['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'],
|
||||
['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'],
|
||||
['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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (conf.notify) {
|
||||
conf.notify(e);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
_handleRfbEvent: function (e) {
|
||||
if (this._onKeyPress) {
|
||||
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
|
||||
this._onKeyPress(e.keysym.keysym, e.type == 'keydown');
|
||||
}
|
||||
doubleClickTimer = setTimeout(resetDoubleClickTimer, 500);
|
||||
},
|
||||
|
||||
_handleKeyDown: function (e) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
if (this._handler.keydown(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeyPress: function (e) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
if (this._handler.keypress(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeyUp: function (e) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
if (this._handler.keyup(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_allKeysUp: function () {
|
||||
Util.Debug(">> Keyboard.allKeysUp");
|
||||
this._handler.releaseAll();
|
||||
Util.Debug("<< Keyboard.allKeysUp");
|
||||
},
|
||||
|
||||
// Public methods
|
||||
|
||||
grab: function () {
|
||||
//Util.Debug(">> Keyboard.grab");
|
||||
var c = this._target;
|
||||
|
||||
Util.addEvent(c, 'keydown', this._eventHandlers.keydown);
|
||||
Util.addEvent(c, 'keyup', this._eventHandlers.keyup);
|
||||
Util.addEvent(c, 'keypress', this._eventHandlers.keypress);
|
||||
|
||||
// Release (key up) if window loses focus
|
||||
Util.addEvent(window, 'blur', this._eventHandlers.blur);
|
||||
|
||||
//Util.Debug("<< Keyboard.grab");
|
||||
},
|
||||
|
||||
ungrab: function () {
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
var c = this._target;
|
||||
|
||||
Util.removeEvent(c, 'keydown', this._eventHandlers.keydown);
|
||||
Util.removeEvent(c, 'keyup', this._eventHandlers.keyup);
|
||||
Util.removeEvent(c, 'keypress', this._eventHandlers.keypress);
|
||||
Util.removeEvent(window, 'blur', this._eventHandlers.blur);
|
||||
|
||||
// Release (key up) all keys that are in a down state
|
||||
this._allKeysUp();
|
||||
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
},
|
||||
|
||||
sync: function (e) {
|
||||
this._handler.syncModifiers(e);
|
||||
}
|
||||
bmask = conf.touchButton;
|
||||
// If bmask is set
|
||||
} else if (evt.which) {
|
||||
/* everything except IE */
|
||||
bmask = 1 << evt.button;
|
||||
} else {
|
||||
/* IE including 9 */
|
||||
bmask = (evt.button & 0x1) + // Left
|
||||
(evt.button & 0x2) * 2 + // Right
|
||||
(evt.button & 0x4) / 2; // Middle
|
||||
}
|
||||
//Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down +
|
||||
// " bmask: " + bmask + "(evt.button: " + evt.button + ")");
|
||||
if (conf.onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
conf.onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function onMouseDown(e) {
|
||||
captureMouse();
|
||||
onMouseButton(e, 1);
|
||||
}
|
||||
Util.make_properties(Keyboard, [
|
||||
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
['focused', 'rw', 'bool'], // Capture and send key events
|
||||
|
||||
function onMouseUp(e) {
|
||||
if (!mouseCaptured) {
|
||||
return;
|
||||
}
|
||||
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
||||
]);
|
||||
|
||||
onMouseButton(e, 0);
|
||||
releaseMouse();
|
||||
}
|
||||
//
|
||||
// Mouse event handler
|
||||
//
|
||||
|
||||
function onMouseWheel(e) {
|
||||
var evt, pos, bmask, wheelData;
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
if (conf.notify) {
|
||||
conf.notify(e);
|
||||
}
|
||||
Mouse = function (defaults) {
|
||||
this._mouseCaptured = false;
|
||||
|
||||
evt = (e ? e : window.event);
|
||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
||||
wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
|
||||
if (wheelData > 0) {
|
||||
bmask = 1 << 3;
|
||||
} else {
|
||||
bmask = 1 << 4;
|
||||
}
|
||||
//Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
|
||||
if (conf.onMouseButton) {
|
||||
conf.onMouseButton(pos.x, pos.y, 1, bmask);
|
||||
conf.onMouseButton(pos.x, pos.y, 0, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
}
|
||||
this._doubleClickTimer = null;
|
||||
this._lastTouchPos = null;
|
||||
|
||||
function onMouseMove(e) {
|
||||
var evt, pos;
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
if (conf.notify) {
|
||||
conf.notify(e);
|
||||
}
|
||||
// Configuration attributes
|
||||
Util.set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true,
|
||||
'scale': 1.0,
|
||||
'touchButton': 1
|
||||
});
|
||||
|
||||
evt = (e ? e : window.event);
|
||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
||||
//Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
|
||||
if (conf.onMouseMove) {
|
||||
conf.onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
}
|
||||
this._eventHandlers = {
|
||||
'mousedown': this._handleMouseDown.bind(this),
|
||||
'mouseup': this._handleMouseUp.bind(this),
|
||||
'mousemove': this._handleMouseMove.bind(this),
|
||||
'mousewheel': this._handleMouseWheel.bind(this),
|
||||
'mousedisable': this._handleMouseDisable.bind(this)
|
||||
};
|
||||
};
|
||||
|
||||
function onMouseDisable(e) {
|
||||
var evt, pos;
|
||||
if (! conf.focused) {
|
||||
return true;
|
||||
}
|
||||
evt = (e ? e : window.event);
|
||||
pos = Util.getEventPosition(e, conf.target, conf.scale);
|
||||
/* Stop propagation if inside canvas area */
|
||||
if ((pos.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;
|
||||
}
|
||||
//Util.Debug("mouse event not disabled");
|
||||
return true;
|
||||
}
|
||||
Mouse.prototype = {
|
||||
// private methods
|
||||
_captureMouse: function () {
|
||||
// capturing the mouse ensures we get the mouseup event
|
||||
if (this._target.setCapture) {
|
||||
this._target.setCapture();
|
||||
}
|
||||
|
||||
//
|
||||
// Public API interface functions
|
||||
//
|
||||
// some browsers give us mouseup events regardless,
|
||||
// so if we never captured the mouse, we can disregard the event
|
||||
this._mouseCaptured = true;
|
||||
},
|
||||
|
||||
that.grab = function() {
|
||||
//Util.Debug(">> Mouse.grab");
|
||||
var c = conf.target;
|
||||
_releaseMouse: function () {
|
||||
if (this._target.releaseCapture) {
|
||||
this._target.releaseCapture();
|
||||
}
|
||||
this._mouseCaptured = false;
|
||||
},
|
||||
|
||||
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',
|
||||
onMouseWheel);
|
||||
}
|
||||
_resetDoubleClickTimer: function () {
|
||||
this._doubleClickTimer = null;
|
||||
},
|
||||
|
||||
/* Work around right and middle click browser behaviors */
|
||||
Util.addEvent(document, 'click', onMouseDisable);
|
||||
Util.addEvent(document.body, 'contextmenu', onMouseDisable);
|
||||
_handleMouseButton: function (e, down) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
//Util.Debug("<< Mouse.grab");
|
||||
};
|
||||
if (this._notify) {
|
||||
this._notify(e);
|
||||
}
|
||||
|
||||
that.ungrab = function() {
|
||||
//Util.Debug(">> Mouse.ungrab");
|
||||
var c = conf.target;
|
||||
var evt = (e ? e : window.event);
|
||||
var pos = Util.getEventPosition(e, this._target, this._scale);
|
||||
|
||||
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',
|
||||
onMouseWheel);
|
||||
}
|
||||
var bmask;
|
||||
if (e.touches || e.changedTouches) {
|
||||
// Touch device
|
||||
|
||||
/* Work around right and middle click browser behaviors */
|
||||
Util.removeEvent(document, 'click', onMouseDisable);
|
||||
Util.removeEvent(document.body, 'contextmenu', onMouseDisable);
|
||||
// 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 (this._doubleClickTimer === null) {
|
||||
this._lastTouchPos = pos;
|
||||
} else {
|
||||
clearTimeout(this._doubleClickTimer);
|
||||
|
||||
//Util.Debug(">> Mouse.ungrab");
|
||||
};
|
||||
// When the distance between the two touches is small enough
|
||||
// force the position of the latter touch to the position of
|
||||
// the first.
|
||||
|
||||
return that; // Return the public API interface
|
||||
var xs = this._lastTouchPos.x - pos.x;
|
||||
var ys = this._lastTouchPos.y - pos.y;
|
||||
var d = Math.sqrt((xs * xs) + (ys * ys));
|
||||
|
||||
} // End of Mouse()
|
||||
// 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 = this._lastTouchPos;
|
||||
}
|
||||
}
|
||||
this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
|
||||
}
|
||||
bmask = this._touchButton;
|
||||
// If bmask is set
|
||||
} else if (evt.which) {
|
||||
/* everything except IE */
|
||||
bmask = 1 << evt.button;
|
||||
} else {
|
||||
/* IE including 9 */
|
||||
bmask = (evt.button & 0x1) + // Left
|
||||
(evt.button & 0x2) * 2 + // Right
|
||||
(evt.button & 0x4) / 2; // Middle
|
||||
}
|
||||
|
||||
if (this._onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
this._onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
},
|
||||
|
||||
_handleMouseDown: function (e) {
|
||||
this._captureMouse();
|
||||
this._handleMouseButton(e, 1);
|
||||
},
|
||||
|
||||
_handleMouseUp: function (e) {
|
||||
if (!this._mouseCaptured) { return; }
|
||||
|
||||
this._handleMouseButton(e, 0);
|
||||
this._releaseMouse();
|
||||
},
|
||||
|
||||
_handleMouseWheel: function (e) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
if (this._notify) {
|
||||
this._notify(e);
|
||||
}
|
||||
|
||||
var evt = (e ? e : window.event);
|
||||
var pos = Util.getEventPosition(e, this._target, this._scale);
|
||||
var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
|
||||
var bmask;
|
||||
if (wheelData > 0) {
|
||||
bmask = 1 << 3;
|
||||
} else {
|
||||
bmask = 1 << 4;
|
||||
}
|
||||
|
||||
if (this._onMouseButton) {
|
||||
this._onMouseButton(pos.x, pos.y, 1, bmask);
|
||||
this._onMouseButton(pos.x, pos.y, 0, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
},
|
||||
|
||||
_handleMouseMove: function (e) {
|
||||
if (! this._focused) { return true; }
|
||||
|
||||
if (this._notify) {
|
||||
this._notify(e);
|
||||
}
|
||||
|
||||
var evt = (e ? e : window.event);
|
||||
var pos = Util.getEventPosition(e, this._target, this._scale);
|
||||
if (this._onMouseMove) {
|
||||
this._onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
},
|
||||
|
||||
_handleMouseDisable: function (e) {
|
||||
if (!this._focused) { return true; }
|
||||
|
||||
var evt = (e ? e : window.event);
|
||||
var pos = Util.getEventPosition(e, this._target, this._scale);
|
||||
|
||||
/* Stop propagation if inside canvas area */
|
||||
if ((pos.realx >= 0) && (pos.realy >= 0) &&
|
||||
(pos.realx < this._target.offsetWidth) &&
|
||||
(pos.realy < this._target.offsetHeight)) {
|
||||
//Util.Debug("mouse event disabled");
|
||||
Util.stopEvent(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
// Public methods
|
||||
grab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
Util.addEvent(c, 'touchstart', this._eventHandlers.mousedown);
|
||||
Util.addEvent(window, 'touchend', this._eventHandlers.mouseup);
|
||||
Util.addEvent(c, 'touchend', this._eventHandlers.mouseup);
|
||||
Util.addEvent(c, 'touchmove', this._eventHandlers.mousemove);
|
||||
} else {
|
||||
Util.addEvent(c, 'mousedown', this._eventHandlers.mousedown);
|
||||
Util.addEvent(window, 'mouseup', this._eventHandlers.mouseup);
|
||||
Util.addEvent(c, 'mouseup', this._eventHandlers.mouseup);
|
||||
Util.addEvent(c, 'mousemove', this._eventHandlers.mousemove);
|
||||
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
||||
this._eventHandlers.mousewheel);
|
||||
}
|
||||
|
||||
/* Work around right and middle click browser behaviors */
|
||||
Util.addEvent(document, 'click', this._eventHandlers.mousedisable);
|
||||
Util.addEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable);
|
||||
},
|
||||
|
||||
ungrab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
Util.removeEvent(c, 'touchstart', this._eventHandlers.mousedown);
|
||||
Util.removeEvent(window, 'touchend', this._eventHandlers.mouseup);
|
||||
Util.removeEvent(c, 'touchend', this._eventHandlers.mouseup);
|
||||
Util.removeEvent(c, 'touchmove', this._eventHandlers.mousemove);
|
||||
} else {
|
||||
Util.removeEvent(c, 'mousedown', this._eventHandlers.mousedown);
|
||||
Util.removeEvent(window, 'mouseup', this._eventHandlers.mouseup);
|
||||
Util.removeEvent(c, 'mouseup', this._eventHandlers.mouseup);
|
||||
Util.removeEvent(c, 'mousemove', this._eventHandlers.mousemove);
|
||||
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
|
||||
this._eventHandlers.mousewheel);
|
||||
}
|
||||
|
||||
/* Work around right and middle click browser behaviors */
|
||||
Util.removeEvent(document, 'click', this._eventHandlers.mousedisable);
|
||||
Util.removeEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Util.make_properties(Mouse, [
|
||||
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
||||
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
|
||||
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
|
||||
['scale', 'rw', 'float'], // Viewport scale factor 0.0 - 1.0
|
||||
|
||||
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
|
||||
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
||||
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
]);
|
||||
})();
|
||||
|
|
|
@ -15,7 +15,7 @@ var kbdUtil = (function() {
|
|||
|
||||
var sub = substitutions[cp];
|
||||
return sub ? sub : cp;
|
||||
};
|
||||
}
|
||||
|
||||
function isMac() {
|
||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||
|
@ -387,17 +387,22 @@ function VerifyCharModifier(next) {
|
|||
if (timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var delayProcess = function () {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
process();
|
||||
};
|
||||
|
||||
while (queue.length !== 0) {
|
||||
var cur = queue[0];
|
||||
queue = queue.splice(1);
|
||||
switch (cur.type) {
|
||||
case 'stall':
|
||||
// insert a delay before processing available events.
|
||||
timer = setTimeout(function() {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
process();
|
||||
}, 5);
|
||||
/* jshint loopfunc: true */
|
||||
timer = setTimeout(delayProcess, 5);
|
||||
/* jshint loopfunc: false */
|
||||
return;
|
||||
case 'keydown':
|
||||
// is the next element a keypress? Then we should merge the two
|
||||
|
@ -489,23 +494,25 @@ function TrackKeyState(next) {
|
|||
|
||||
var item = state.splice(idx, 1)[0];
|
||||
// for each keysym tracked by this key entry, clone the current event and override the keysym
|
||||
var clone = (function(){
|
||||
function Clone(){}
|
||||
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
||||
}());
|
||||
for (var key in item.keysyms) {
|
||||
var clone = (function(){
|
||||
function Clone(){}
|
||||
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
||||
}());
|
||||
var out = clone(evt);
|
||||
out.keysym = item.keysyms[key];
|
||||
next(out);
|
||||
}
|
||||
break;
|
||||
case 'releaseall':
|
||||
/* jshint shadow: true */
|
||||
for (var i = 0; i < state.length; ++i) {
|
||||
for (var key in state[i].keysyms) {
|
||||
var keysym = state[i].keysyms[key];
|
||||
next({keyId: 0, keysym: keysym, type: 'keyup'});
|
||||
}
|
||||
}
|
||||
/* jshint shadow: false */
|
||||
state = [];
|
||||
}
|
||||
};
|
||||
|
@ -527,8 +534,10 @@ function EscapeModifiers(next) {
|
|||
// send the character event
|
||||
next(evt);
|
||||
// redo modifiers
|
||||
/* jshint shadow: true */
|
||||
for (var i = 0; i < evt.escape.length; ++i) {
|
||||
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
||||
}
|
||||
/* jshint shadow: false */
|
||||
};
|
||||
}
|
||||
|
|
3718
include/rfb.js
3718
include/rfb.js
File diff suppressed because it is too large
Load Diff
1864
include/ui.js
1864
include/ui.js
File diff suppressed because it is too large
Load Diff
582
include/util.js
582
include/util.js
|
@ -6,9 +6,8 @@
|
|||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint bitwise: false, white: false */
|
||||
/*global window, console, document, navigator, ActiveXObject */
|
||||
/* jshint white: false, nonstandard: true */
|
||||
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
|
||||
|
||||
// Globals defined here
|
||||
var Util = {};
|
||||
|
@ -19,88 +18,161 @@ var Util = {};
|
|||
*/
|
||||
|
||||
Array.prototype.push8 = function (num) {
|
||||
"use strict";
|
||||
this.push(num & 0xFF);
|
||||
};
|
||||
|
||||
Array.prototype.push16 = function (num) {
|
||||
"use strict";
|
||||
this.push((num >> 8) & 0xFF,
|
||||
(num ) & 0xFF );
|
||||
num & 0xFF);
|
||||
};
|
||||
Array.prototype.push32 = function (num) {
|
||||
"use strict";
|
||||
this.push((num >> 24) & 0xFF,
|
||||
(num >> 16) & 0xFF,
|
||||
(num >> 8) & 0xFF,
|
||||
(num ) & 0xFF );
|
||||
num & 0xFF);
|
||||
};
|
||||
|
||||
// IE does not support map (even in IE9)
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
if (!Array.prototype.map)
|
||||
{
|
||||
Array.prototype.map = function(fun /*, thisp*/)
|
||||
{
|
||||
var len = this.length;
|
||||
if (typeof fun != "function")
|
||||
throw new TypeError();
|
||||
if (!Array.prototype.map) {
|
||||
Array.prototype.map = function (fun /*, thisp*/) {
|
||||
"use strict";
|
||||
var len = this.length;
|
||||
if (typeof fun != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var res = new Array(len);
|
||||
var thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (i in this)
|
||||
res[i] = fun.call(thisp, this[i], i, this);
|
||||
}
|
||||
var res = new Array(len);
|
||||
var thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (i in this) {
|
||||
res[i] = fun.call(thisp, this[i], i, this);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
// IE <9 does not support indexOf
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
if (!Array.prototype.indexOf)
|
||||
{
|
||||
Array.prototype.indexOf = function(elt /*, from*/)
|
||||
{
|
||||
var len = this.length >>> 0;
|
||||
if (!Array.prototype.indexOf) {
|
||||
Array.prototype.indexOf = function (elt /*, from*/) {
|
||||
"use strict";
|
||||
var len = this.length >>> 0;
|
||||
|
||||
var from = Number(arguments[1]) || 0;
|
||||
from = (from < 0)
|
||||
? Math.ceil(from)
|
||||
: Math.floor(from);
|
||||
if (from < 0)
|
||||
from += len;
|
||||
var from = Number(arguments[1]) || 0;
|
||||
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
|
||||
if (from < 0) {
|
||||
from += len;
|
||||
}
|
||||
|
||||
for (; from < len; from++)
|
||||
{
|
||||
if (from in this &&
|
||||
this[from] === elt)
|
||||
return from;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
for (; from < len; from++) {
|
||||
if (from in this &&
|
||||
this[from] === elt) {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
||||
if (!Object.keys) {
|
||||
Object.keys = (function () {
|
||||
'use strict';
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty,
|
||||
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
||||
dontEnums = [
|
||||
'toString',
|
||||
'toLocaleString',
|
||||
'valueOf',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'constructor'
|
||||
],
|
||||
dontEnumsLength = dontEnums.length;
|
||||
|
||||
//
|
||||
return function (obj) {
|
||||
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
|
||||
throw new TypeError('Object.keys called on non-object');
|
||||
}
|
||||
|
||||
var result = [], prop, i;
|
||||
|
||||
for (prop in obj) {
|
||||
if (hasOwnProperty.call(obj, prop)) {
|
||||
result.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDontEnumBug) {
|
||||
for (i = 0; i < dontEnumsLength; i++) {
|
||||
if (hasOwnProperty.call(obj, dontEnums[i])) {
|
||||
result.push(dontEnums[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// PhantomJS 1.x doesn't support bind,
|
||||
// so leave this in until PhantomJS 2.0 is released
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - " +
|
||||
"what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function () {},
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// requestAnimationFrame shim with setTimeout fallback
|
||||
//
|
||||
|
||||
window.requestAnimFrame = (function(){
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(callback){
|
||||
window.requestAnimFrame = (function () {
|
||||
"use strict";
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})();
|
||||
|
||||
/*
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in Util
|
||||
* ------------------------------------------------------
|
||||
|
@ -112,6 +184,7 @@ window.requestAnimFrame = (function(){
|
|||
|
||||
Util._log_level = 'warn';
|
||||
Util.init_logging = function (level) {
|
||||
"use strict";
|
||||
if (typeof level === 'undefined') {
|
||||
level = Util._log_level;
|
||||
} else {
|
||||
|
@ -122,26 +195,34 @@ Util.init_logging = function (level) {
|
|||
window.console = {
|
||||
'log' : window.opera.postError,
|
||||
'warn' : window.opera.postError,
|
||||
'error': window.opera.postError };
|
||||
'error': window.opera.postError
|
||||
};
|
||||
} else {
|
||||
window.console = {
|
||||
'log' : function(m) {},
|
||||
'warn' : function(m) {},
|
||||
'error': function(m) {}};
|
||||
'log' : function (m) {},
|
||||
'warn' : function (m) {},
|
||||
'error': function (m) {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
|
||||
/* jshint -W086 */
|
||||
switch (level) {
|
||||
case 'debug': Util.Debug = function (msg) { console.log(msg); };
|
||||
case 'info': Util.Info = function (msg) { console.log(msg); };
|
||||
case 'warn': Util.Warn = function (msg) { console.warn(msg); };
|
||||
case 'error': Util.Error = function (msg) { console.error(msg); };
|
||||
case 'debug':
|
||||
Util.Debug = function (msg) { console.log(msg); };
|
||||
case 'info':
|
||||
Util.Info = function (msg) { console.log(msg); };
|
||||
case 'warn':
|
||||
Util.Warn = function (msg) { console.warn(msg); };
|
||||
case 'error':
|
||||
Util.Error = function (msg) { console.error(msg); };
|
||||
case 'none':
|
||||
break;
|
||||
default:
|
||||
throw("invalid logging type '" + level + "'");
|
||||
throw new Error("invalid logging type '" + level + "'");
|
||||
}
|
||||
/* jshint +W086 */
|
||||
};
|
||||
Util.get_logging = function () {
|
||||
return Util._log_level;
|
||||
|
@ -149,93 +230,133 @@ Util.get_logging = function () {
|
|||
// Initialize logging level
|
||||
Util.init_logging();
|
||||
|
||||
Util.make_property = function (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
// Set configuration default for Crockford style function namespaces
|
||||
Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
|
||||
var getter, setter;
|
||||
|
||||
// Default getter function
|
||||
getter = function (idx) {
|
||||
if ((type in {'arr':1, 'array':1}) &&
|
||||
(typeof idx !== 'undefined')) {
|
||||
return cfg[v][idx];
|
||||
} else {
|
||||
return cfg[v];
|
||||
}
|
||||
};
|
||||
|
||||
// Default setter function
|
||||
setter = function (val, idx) {
|
||||
if (type in {'boolean':1, 'bool':1}) {
|
||||
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
|
||||
val = false;
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
val = true;
|
||||
return this['_' + name];
|
||||
}
|
||||
} else if (type in {'integer':1, 'int':1}) {
|
||||
val = parseInt(val, 10);
|
||||
} else if (type === 'str') {
|
||||
val = String(val);
|
||||
} else if (type === 'func') {
|
||||
if (!val) {
|
||||
val = function () {};
|
||||
}
|
||||
}
|
||||
if (typeof idx !== 'undefined') {
|
||||
cfg[v][idx] = val;
|
||||
} else {
|
||||
cfg[v] = val;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the description
|
||||
api[v + '_description'] = desc;
|
||||
|
||||
// Set the getter function
|
||||
if (typeof api['get_' + v] === 'undefined') {
|
||||
api['get_' + v] = getter;
|
||||
}
|
||||
|
||||
// Set the setter function with extra sanity checks
|
||||
if (typeof api['set_' + v] === 'undefined') {
|
||||
api['set_' + v] = function (val, idx) {
|
||||
if (mode in {'RO':1, 'ro':1}) {
|
||||
throw(v + " is read-only");
|
||||
} else if ((mode in {'WO':1, 'wo':1}) &&
|
||||
(typeof cfg[v] !== 'undefined')) {
|
||||
throw(v + " can only be set once");
|
||||
}
|
||||
setter(val, idx);
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
// Set the default value
|
||||
if (typeof defaults[v] !== 'undefined') {
|
||||
defval = defaults[v];
|
||||
} else if ((type in {'arr':1, 'array':1}) &&
|
||||
(! (defval instanceof Array))) {
|
||||
defval = [];
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
// Coerce existing setting to the right type
|
||||
//Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
|
||||
setter(defval);
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
// Set group of configuration defaults
|
||||
Util.conf_defaults = function(cfg, api, defaults, arr) {
|
||||
Util.make_properties = function (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
};
|
||||
|
||||
Util.set_defaults = function (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
|
||||
arr[i][2], arr[i][3], arr[i][4]);
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
|
||||
if (conf[keys[i]]) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
Util.decodeUTF8 = function(utf8string) {
|
||||
Util.decodeUTF8 = function (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -250,42 +371,46 @@ Util.decodeUTF8 = function(utf8string) {
|
|||
// 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() {
|
||||
Util.get_include_uri = function () {
|
||||
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
|
||||
}
|
||||
};
|
||||
Util._loading_scripts = [];
|
||||
Util._pending_scripts = [];
|
||||
Util.load_scripts = function(files) {
|
||||
Util.load_scripts = function (files) {
|
||||
"use strict";
|
||||
var head = document.getElementsByTagName('head')[0], script,
|
||||
ls = Util._loading_scripts, ps = Util._pending_scripts;
|
||||
for (var f=0; f<files.length; f++) {
|
||||
|
||||
var loadFunc = function (e) {
|
||||
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
||||
ls[0].readyState === 'complete')) {
|
||||
// For IE, append the script to trigger execution
|
||||
var s = ls.shift();
|
||||
//console.log("loaded script: " + s.src);
|
||||
head.appendChild(s);
|
||||
}
|
||||
if (!this.readyState ||
|
||||
(Util.Engine.presto && this.readyState === 'loaded') ||
|
||||
this.readyState === 'complete') {
|
||||
if (ps.indexOf(this) >= 0) {
|
||||
this.onload = this.onreadystatechange = null;
|
||||
//console.log("completed script: " + this.src);
|
||||
ps.splice(ps.indexOf(this), 1);
|
||||
|
||||
// Call window.onscriptsload after last script loads
|
||||
if (ps.length === 0 && window.onscriptsload) {
|
||||
window.onscriptsload();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = Util.get_include_uri() + files[f];
|
||||
//console.log("loading script: " + script.src);
|
||||
script.onload = script.onreadystatechange = 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
script.onload = script.onreadystatechange = loadFunc;
|
||||
// In-order script execution tricks
|
||||
if (Util.Engine.trident) {
|
||||
// For IE wait until readyState is 'loaded' before
|
||||
|
@ -300,20 +425,22 @@ Util.load_scripts = function(files) {
|
|||
}
|
||||
ps.push(script);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Get DOM element position on page
|
||||
// This solution is based based on http://www.greywyvern.com/?post=331
|
||||
// Thanks to Brian Huisman AKA GreyWyvern!
|
||||
Util.getPosition = (function() {
|
||||
Util.getPosition = (function () {
|
||||
"use strict";
|
||||
function getStyle(obj, styleProp) {
|
||||
var y;
|
||||
if (obj.currentStyle) {
|
||||
var y = obj.currentStyle[styleProp];
|
||||
y = obj.currentStyle[styleProp];
|
||||
} else if (window.getComputedStyle)
|
||||
var y = window.getComputedStyle(obj, null)[styleProp];
|
||||
y = window.getComputedStyle(obj, null)[styleProp];
|
||||
return y;
|
||||
};
|
||||
}
|
||||
|
||||
function scrollDist() {
|
||||
var myScrollTop = 0, myScrollLeft = 0;
|
||||
|
@ -342,7 +469,7 @@ Util.getPosition = (function() {
|
|||
}
|
||||
|
||||
return [myScrollLeft, myScrollTop];
|
||||
};
|
||||
}
|
||||
|
||||
return function (obj) {
|
||||
var curleft = 0, curtop = 0, scr = obj, fixed = false;
|
||||
|
@ -362,7 +489,7 @@ Util.getPosition = (function() {
|
|||
do {
|
||||
curleft += obj.offsetLeft;
|
||||
curtop += obj.offsetTop;
|
||||
} while (obj = obj.offsetParent);
|
||||
} while ((obj = obj.offsetParent));
|
||||
|
||||
return {'x': curleft, 'y': curtop};
|
||||
};
|
||||
|
@ -371,6 +498,7 @@ Util.getPosition = (function() {
|
|||
|
||||
// Get mouse event position in DOM element
|
||||
Util.getEventPosition = function (e, obj, scale) {
|
||||
"use strict";
|
||||
var evt, docX, docY, pos;
|
||||
//if (!e) evt = window.event;
|
||||
evt = (e ? e : window.event);
|
||||
|
@ -390,38 +518,41 @@ Util.getEventPosition = function (e, obj, 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);
|
||||
var x = Math.max(Math.min(realx, obj.width - 1), 0);
|
||||
var y = Math.max(Math.min(realy, obj.height - 1), 0);
|
||||
return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale};
|
||||
};
|
||||
|
||||
|
||||
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
|
||||
Util.addEvent = function (obj, evType, fn){
|
||||
if (obj.attachEvent){
|
||||
var r = obj.attachEvent("on"+evType, fn);
|
||||
Util.addEvent = function (obj, evType, fn) {
|
||||
"use strict";
|
||||
if (obj.attachEvent) {
|
||||
var r = obj.attachEvent("on" + evType, fn);
|
||||
return r;
|
||||
} else if (obj.addEventListener){
|
||||
obj.addEventListener(evType, fn, false);
|
||||
} else if (obj.addEventListener) {
|
||||
obj.addEventListener(evType, fn, false);
|
||||
return true;
|
||||
} else {
|
||||
throw("Handler could not be attached");
|
||||
throw new Error("Handler could not be attached");
|
||||
}
|
||||
};
|
||||
|
||||
Util.removeEvent = function(obj, evType, fn){
|
||||
if (obj.detachEvent){
|
||||
var r = obj.detachEvent("on"+evType, fn);
|
||||
Util.removeEvent = function (obj, evType, fn) {
|
||||
"use strict";
|
||||
if (obj.detachEvent) {
|
||||
var r = obj.detachEvent("on" + evType, fn);
|
||||
return r;
|
||||
} else if (obj.removeEventListener){
|
||||
} else if (obj.removeEventListener) {
|
||||
obj.removeEventListener(evType, fn, false);
|
||||
return true;
|
||||
} else {
|
||||
throw("Handler could not be removed");
|
||||
throw new Error("Handler could not be removed");
|
||||
}
|
||||
};
|
||||
|
||||
Util.stopEvent = function(e) {
|
||||
Util.stopEvent = function (e) {
|
||||
"use strict";
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
else { e.cancelBubble = true; }
|
||||
|
||||
|
@ -433,41 +564,88 @@ Util.stopEvent = function(e) {
|
|||
// Set browser engine versions. Based on mootools.
|
||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||
|
||||
Util.Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': (function() { return (!window.opera) ? false : true; }()),
|
||||
(function () {
|
||||
"use strict";
|
||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||
var detectPresto = function () {
|
||||
return !!window.opera;
|
||||
};
|
||||
|
||||
'trident': (function() {
|
||||
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
||||
'webkit': (function() {
|
||||
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
//'webkit': (function() {
|
||||
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
|
||||
'gecko': (function() {
|
||||
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
|
||||
};
|
||||
if (Util.Engine.webkit) {
|
||||
// Extract actual webkit version if available
|
||||
Util.Engine.webkit = (function(v) {
|
||||
var re = new RegExp('WebKit/([0-9\.]*) ');
|
||||
v = (navigator.userAgent.match(re) || ['', v])[1];
|
||||
return parseFloat(v, 10);
|
||||
})(Util.Engine.webkit);
|
||||
}
|
||||
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
|
||||
var detectTrident = function () {
|
||||
if (!window.ActiveXObject) {
|
||||
return false;
|
||||
} else {
|
||||
if (window.XMLHttpRequest) {
|
||||
return (document.querySelectorAll) ? 6 : 5;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Util.Flash = (function(){
|
||||
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
var detectInitialWebkit = function () {
|
||||
try {
|
||||
if (navigator.taintEnabled) {
|
||||
return false;
|
||||
} else {
|
||||
if (Util.Features.xpath) {
|
||||
return (Util.Features.query) ? 525 : 420;
|
||||
} else {
|
||||
return 419;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var detectActualWebkit = function (initial_ver) {
|
||||
var re = /WebKit\/([0-9\.]*) /;
|
||||
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
|
||||
return parseFloat(str_ver, 10);
|
||||
};
|
||||
|
||||
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
|
||||
var detectGecko = function () {
|
||||
/* jshint -W041 */
|
||||
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
|
||||
return false;
|
||||
} else {
|
||||
return (document.getElementsByClassName) ? 19 : 18;
|
||||
}
|
||||
/* jshint +W041 */
|
||||
};
|
||||
|
||||
Util.Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': detectPresto(),
|
||||
'trident': detectTrident(),
|
||||
'webkit': detectInitialWebkit(),
|
||||
'gecko': detectGecko(),
|
||||
};
|
||||
|
||||
if (Util.Engine.webkit) {
|
||||
// Extract actual webkit version if available
|
||||
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
|
||||
}
|
||||
})();
|
||||
|
||||
Util.Flash = (function () {
|
||||
"use strict";
|
||||
var v, version;
|
||||
try {
|
||||
v = navigator.plugins['Shockwave Flash'].description;
|
||||
} catch(err1) {
|
||||
} catch (err1) {
|
||||
try {
|
||||
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||
} catch(err2) {
|
||||
} catch (err2) {
|
||||
v = '0 r0';
|
||||
}
|
||||
}
|
||||
version = v.match(/\d+/g);
|
||||
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* read binary data off of the receive queue.
|
||||
*/
|
||||
|
||||
/*jslint browser: true, bitwise: false, plusplus: false */
|
||||
/*jslint browser: true, bitwise: true */
|
||||
/*global Util, Base64 */
|
||||
|
||||
|
||||
|
@ -43,382 +43,342 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
|||
}
|
||||
Util.load_scripts(["web-socket-js/swfobject.js",
|
||||
"web-socket-js/web_socket.js"]);
|
||||
}());
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
function Websock() {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
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
|
||||
sQ = [], // Send queue
|
||||
this._websocket = null; // WebSocket object
|
||||
this._rQ = []; // Receive queue
|
||||
this._rQi = 0; // Receive queue index
|
||||
this._rQmax = 10000; // Max receive queue size before compacting
|
||||
this._sQ = []; // Send queue
|
||||
|
||||
eventHandlers = {
|
||||
'message' : function() {},
|
||||
'open' : function() {},
|
||||
'close' : function() {},
|
||||
'error' : function() {}
|
||||
},
|
||||
this._mode = 'base64'; // Current WebSocket mode: 'binary', 'base64'
|
||||
this.maxBufferedAmount = 200;
|
||||
|
||||
test_mode = false;
|
||||
|
||||
|
||||
//
|
||||
// Queue public functions
|
||||
//
|
||||
|
||||
function get_sQ() {
|
||||
return sQ;
|
||||
this._eventHandlers = {
|
||||
'message': function () {},
|
||||
'open': function () {},
|
||||
'close': function () {},
|
||||
'error': function () {}
|
||||
};
|
||||
}
|
||||
|
||||
function get_rQ() {
|
||||
return rQ;
|
||||
}
|
||||
function get_rQi() {
|
||||
return rQi;
|
||||
}
|
||||
function set_rQi(val) {
|
||||
rQi = val;
|
||||
}
|
||||
(function () {
|
||||
"use strict";
|
||||
Websock.prototype = {
|
||||
// Getters and Setters
|
||||
get_sQ: function () {
|
||||
return this._sQ;
|
||||
},
|
||||
|
||||
function rQlen() {
|
||||
return rQ.length - rQi;
|
||||
}
|
||||
get_rQ: function () {
|
||||
return this._rQ;
|
||||
},
|
||||
|
||||
function rQpeek8() {
|
||||
return (rQ[rQi] );
|
||||
}
|
||||
function rQshift8() {
|
||||
return (rQ[rQi++] );
|
||||
}
|
||||
function rQunshift8(num) {
|
||||
if (rQi === 0) {
|
||||
rQ.unshift(num);
|
||||
} else {
|
||||
rQi -= 1;
|
||||
rQ[rQi] = num;
|
||||
}
|
||||
get_rQi: function () {
|
||||
return this._rQi;
|
||||
},
|
||||
|
||||
}
|
||||
function rQshift16() {
|
||||
return (rQ[rQi++] << 8) +
|
||||
(rQ[rQi++] );
|
||||
}
|
||||
function rQshift32() {
|
||||
return (rQ[rQi++] << 24) +
|
||||
(rQ[rQi++] << 16) +
|
||||
(rQ[rQi++] << 8) +
|
||||
(rQ[rQi++] );
|
||||
}
|
||||
function rQshiftStr(len) {
|
||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||
var arr = rQ.slice(rQi, rQi + len);
|
||||
rQi += len;
|
||||
return String.fromCharCode.apply(null, arr);
|
||||
}
|
||||
function rQshiftBytes(len) {
|
||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||
rQi += len;
|
||||
return rQ.slice(rQi-len, rQi);
|
||||
}
|
||||
set_rQi: function (val) {
|
||||
this._rQi = val;
|
||||
},
|
||||
|
||||
function rQslice(start, end) {
|
||||
if (end) {
|
||||
return rQ.slice(rQi + start, rQi + end);
|
||||
} else {
|
||||
return rQ.slice(rQi + start);
|
||||
}
|
||||
}
|
||||
// Receive Queue
|
||||
rQlen: function () {
|
||||
return this._rQ.length - this._rQi;
|
||||
},
|
||||
|
||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||
// to be available in the receive queue. Return true if we need to
|
||||
// wait (and possibly print a debug message), otherwise false.
|
||||
function rQwait(msg, num, goback) {
|
||||
var rQlen = rQ.length - rQi; // Skip rQlen() function call
|
||||
if (rQlen < num) {
|
||||
if (goback) {
|
||||
if (rQi < goback) {
|
||||
throw("rQwait cannot backup " + goback + " bytes");
|
||||
rQpeek8: function () {
|
||||
return this._rQ[this._rQi];
|
||||
},
|
||||
|
||||
rQshift8: function () {
|
||||
return this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
rQskip8: function () {
|
||||
this._rQi++;
|
||||
},
|
||||
|
||||
rQskipBytes: function (num) {
|
||||
this._rQi += num;
|
||||
},
|
||||
|
||||
rQunshift8: function (num) {
|
||||
if (this._rQi === 0) {
|
||||
this._rQ.unshift(num);
|
||||
} else {
|
||||
this._rQi--;
|
||||
this._rQ[this._rQi] = num;
|
||||
}
|
||||
rQi -= goback;
|
||||
}
|
||||
//Util.Debug(" waiting for " + (num-rQlen) +
|
||||
// " " + msg + " byte(s)");
|
||||
return true; // true means need more data
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Private utility routines
|
||||
//
|
||||
rQshift16: function () {
|
||||
return (this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
function encode_message() {
|
||||
if (mode === 'binary') {
|
||||
// Put in a binary arraybuffer
|
||||
return (new Uint8Array(sQ)).buffer;
|
||||
} else {
|
||||
// base64 encode
|
||||
return Base64.encode(sQ);
|
||||
}
|
||||
}
|
||||
rQshift32: function () {
|
||||
return (this._rQ[this._rQi++] << 24) +
|
||||
(this._rQ[this._rQi++] << 16) +
|
||||
(this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
function decode_message(data) {
|
||||
//Util.Debug(">> decode_message: " + data);
|
||||
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);
|
||||
}
|
||||
rQshiftStr: function (len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
var arr = this._rQ.slice(this._rQi, this._rQi + len);
|
||||
this._rQi += len;
|
||||
return String.fromCharCode.apply(null, arr);
|
||||
},
|
||||
|
||||
rQshiftBytes: function (len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
this._rQi += len;
|
||||
return this._rQ.slice(this._rQi - len, this._rQi);
|
||||
},
|
||||
|
||||
//
|
||||
// Public Send functions
|
||||
//
|
||||
|
||||
function flush() {
|
||||
if (websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
|
||||
}
|
||||
if (websocket.bufferedAmount < api.maxBufferedAmount) {
|
||||
//Util.Debug("arr: " + arr);
|
||||
//Util.Debug("sQ: " + sQ);
|
||||
if (sQ.length > 0) {
|
||||
websocket.send(encode_message(sQ));
|
||||
sQ = [];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Util.Info("Delaying send, bufferedAmount: " +
|
||||
websocket.bufferedAmount);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// overridable for testing
|
||||
function send(arr) {
|
||||
//Util.Debug(">> send_array: " + arr);
|
||||
sQ = sQ.concat(arr);
|
||||
return flush();
|
||||
}
|
||||
|
||||
function send_string(str) {
|
||||
//Util.Debug(">> send_string: " + str);
|
||||
api.send(str.split('').map(
|
||||
function (chr) { return chr.charCodeAt(0); } ) );
|
||||
}
|
||||
|
||||
//
|
||||
// Other public functions
|
||||
|
||||
function recv_message(e) {
|
||||
//Util.Debug(">> recv_message: " + e.data.length);
|
||||
|
||||
try {
|
||||
decode_message(e.data);
|
||||
if (rQlen() > 0) {
|
||||
eventHandlers.message();
|
||||
// Compact the receive queue
|
||||
if (rQ.length > rQmax) {
|
||||
//Util.Debug("Compacting receive queue");
|
||||
rQ = rQ.slice(rQi);
|
||||
rQi = 0;
|
||||
rQslice: function (start, end) {
|
||||
if (end) {
|
||||
return this._rQ.slice(this._rQi + start, this._rQi + end);
|
||||
} else {
|
||||
return this._rQ.slice(this._rQi + start);
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
if (typeof exc.stack !== 'undefined') {
|
||||
Util.Warn("recv_message, caught exception: " + exc.stack);
|
||||
} else if (typeof exc.description !== 'undefined') {
|
||||
Util.Warn("recv_message, caught exception: " + exc.description);
|
||||
} else {
|
||||
Util.Warn("recv_message, caught exception:" + exc);
|
||||
}
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
eventHandlers.error(exc.name + ": " + exc.message);
|
||||
} else {
|
||||
eventHandlers.error(exc);
|
||||
}
|
||||
}
|
||||
//Util.Debug("<< recv_message");
|
||||
}
|
||||
},
|
||||
|
||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||
// to be available in the receive queue. Return true if we need to
|
||||
// wait (and possibly print a debug message), otherwise false.
|
||||
rQwait: function (msg, num, goback) {
|
||||
var rQlen = this._rQ.length - this._rQi; // Skip rQlen() function call
|
||||
if (rQlen < num) {
|
||||
if (goback) {
|
||||
if (this._rQi < goback) {
|
||||
throw new Error("rQwait cannot backup " + goback + " bytes");
|
||||
}
|
||||
this._rQi -= goback;
|
||||
}
|
||||
return true; // true means need more data
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Set event handlers
|
||||
function on(evt, handler) {
|
||||
eventHandlers[evt] = handler;
|
||||
}
|
||||
// Send Queue
|
||||
|
||||
function init(protocols, ws_schema) {
|
||||
rQ = [];
|
||||
rQi = 0;
|
||||
sQ = [];
|
||||
websocket = null;
|
||||
flush: function () {
|
||||
if (this._websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
}
|
||||
|
||||
var bt = false,
|
||||
wsbt = false,
|
||||
try_binary = false;
|
||||
if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
|
||||
if (this._sQ.length > 0) {
|
||||
this._websocket.send(this._encode_message());
|
||||
this._sQ = [];
|
||||
}
|
||||
|
||||
// Check for full typed array support
|
||||
if (('Uint8Array' in window) &&
|
||||
('set' in Uint8Array.prototype)) {
|
||||
bt = true;
|
||||
}
|
||||
// Check for full binary type support in WebSocket
|
||||
// Inspired by:
|
||||
// https://github.com/Modernizr/Modernizr/issues/370
|
||||
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
|
||||
try {
|
||||
if (bt && ('binaryType' in WebSocket.prototype ||
|
||||
!!(new WebSocket(ws_schema + '://.').binaryType))) {
|
||||
Util.Info("Detected binaryType support in WebSockets");
|
||||
wsbt = true;
|
||||
}
|
||||
} catch (exc) {
|
||||
// Just ignore failed test localhost connections
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Util.Info("Delaying send, bufferedAmount: " +
|
||||
this._websocket.bufferedAmount);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Default protocols if not specified
|
||||
if (typeof(protocols) === "undefined") {
|
||||
if (wsbt) {
|
||||
protocols = ['binary', 'base64'];
|
||||
} else {
|
||||
protocols = 'base64';
|
||||
}
|
||||
}
|
||||
send: function (arr) {
|
||||
this._sQ = this._sQ.concat(arr);
|
||||
return this.flush();
|
||||
},
|
||||
|
||||
// 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");
|
||||
send_string: function (str) {
|
||||
this.send(str.split('').map(function (chr) {
|
||||
return chr.charCodeAt(0);
|
||||
}));
|
||||
},
|
||||
|
||||
// Event Handlers
|
||||
on: function (evt, handler) {
|
||||
this._eventHandlers[evt] = handler;
|
||||
},
|
||||
|
||||
init: function (protocols, ws_schema) {
|
||||
this._rQ = [];
|
||||
this._rQi = 0;
|
||||
this._sQ = [];
|
||||
this._websocket = null;
|
||||
|
||||
// Check for full typed array support
|
||||
var bt = false;
|
||||
if (('Uint8Array' in window) &&
|
||||
('set' in Uint8Array.prototype)) {
|
||||
bt = true;
|
||||
}
|
||||
|
||||
// Check for full binary type support in WebSockets
|
||||
// Inspired by:
|
||||
// https://github.com/Modernizr/Modernizr/issues/370
|
||||
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
|
||||
var wsbt = false;
|
||||
try {
|
||||
if (bt && ('binaryType' in WebSocket.prototype ||
|
||||
!!(new WebSocket(ws_schema + '://.').binaryType))) {
|
||||
Util.Info("Detected binaryType support in WebSockets");
|
||||
wsbt = true;
|
||||
}
|
||||
} catch (exc) {
|
||||
// Just ignore failed test localhost connection
|
||||
}
|
||||
|
||||
// Default protocols if not specified
|
||||
if (typeof(protocols) === "undefined") {
|
||||
if (wsbt) {
|
||||
protocols = ['binary', 'base64'];
|
||||
} else {
|
||||
new_protocols.push(protocols[i]);
|
||||
protocols = 'base64';
|
||||
}
|
||||
}
|
||||
if (new_protocols.length > 0) {
|
||||
protocols = new_protocols;
|
||||
|
||||
if (!wsbt) {
|
||||
if (protocols === 'binary') {
|
||||
throw new Error('WebSocket binary sub-protocol requested but not supported');
|
||||
}
|
||||
|
||||
if (typeof(protocols) === 'object') {
|
||||
var new_protocols = [];
|
||||
|
||||
for (var i = 0; i < protocols.length; i++) {
|
||||
if (protocols[i] === 'binary') {
|
||||
Util.Error('Skipping unsupported WebSocket binary sub-protocol');
|
||||
} else {
|
||||
new_protocols.push(protocols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_protocols.length > 0) {
|
||||
protocols = new_protocols;
|
||||
} else {
|
||||
throw new Error("Only WebSocket binary sub-protocol was requested and is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return protocols;
|
||||
},
|
||||
|
||||
open: function (uri, protocols) {
|
||||
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
||||
protocols = this.init(protocols, ws_schema);
|
||||
|
||||
this._websocket = new WebSocket(uri, protocols);
|
||||
|
||||
if (protocols.indexOf('binary') >= 0) {
|
||||
this._websocket.binaryType = 'arraybuffer';
|
||||
}
|
||||
|
||||
this._websocket.onmessage = this._recv_message.bind(this);
|
||||
this._websocket.onopen = (function () {
|
||||
Util.Debug('>> WebSock.onopen');
|
||||
if (this._websocket.protocol) {
|
||||
this._mode = this._websocket.protocol;
|
||||
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
} else {
|
||||
this._mode = 'base64';
|
||||
Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol);
|
||||
}
|
||||
this._eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
}).bind(this);
|
||||
this._websocket.onclose = (function (e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
this._eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
}).bind(this);
|
||||
this._websocket.onerror = (function (e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
this._eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror: " + e);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
if (this._websocket) {
|
||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
this._websocket.close();
|
||||
}
|
||||
|
||||
this._websocket.onmessage = function (e) { return; };
|
||||
}
|
||||
},
|
||||
|
||||
// private methods
|
||||
_encode_message: function () {
|
||||
if (this._mode === 'binary') {
|
||||
// Put in a binary arraybuffer
|
||||
return (new Uint8Array(this._sQ)).buffer;
|
||||
} else {
|
||||
throw("Only WebSocket binary sub-protocol was requested and not supported.");
|
||||
// base64 encode
|
||||
return Base64.encode(this._sQ);
|
||||
}
|
||||
},
|
||||
|
||||
_decode_message: function (data) {
|
||||
if (this._mode === 'binary') {
|
||||
// push arraybuffer values onto the end
|
||||
var u8 = new Uint8Array(data);
|
||||
for (var i = 0; i < u8.length; i++) {
|
||||
this._rQ.push(u8[i]);
|
||||
}
|
||||
} else {
|
||||
// base64 decode and concat to end
|
||||
this._rQ = this._rQ.concat(Base64.decode(data, 0));
|
||||
}
|
||||
},
|
||||
|
||||
_recv_message: function (e) {
|
||||
try {
|
||||
this._decode_message(e.data);
|
||||
if (this.rQlen() > 0) {
|
||||
this._eventHandlers.message();
|
||||
// Compact the receive queue
|
||||
if (this._rQ.length > this._rQmax) {
|
||||
this._rQ = this._rQ.slice(this._rQi);
|
||||
this._rQi = 0;
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
var exception_str = "";
|
||||
if (exc.name) {
|
||||
exception_str += "\n name: " + exc.name + "\n";
|
||||
exception_str += " message: " + exc.message + "\n";
|
||||
}
|
||||
|
||||
if (typeof exc.description !== 'undefined') {
|
||||
exception_str += " description: " + exc.description + "\n";
|
||||
}
|
||||
|
||||
if (typeof exc.stack !== 'undefined') {
|
||||
exception_str += exc.stack;
|
||||
}
|
||||
|
||||
if (exception_str.length > 0) {
|
||||
Util.Error("recv_message, caught exception: " + exception_str);
|
||||
} else {
|
||||
Util.Error("recv_message, caught exception: " + exc);
|
||||
}
|
||||
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
this._eventHandlers.error(exc.name + ": " + exc.message);
|
||||
} else {
|
||||
this._eventHandlers.error(exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return protocols;
|
||||
}
|
||||
|
||||
function open(uri, protocols) {
|
||||
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
||||
protocols = init(protocols, ws_schema);
|
||||
|
||||
if (test_mode) {
|
||||
websocket = {};
|
||||
} else {
|
||||
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();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
};
|
||||
websocket.onclose = function(e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
};
|
||||
websocket.onerror = function(e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror");
|
||||
};
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (websocket) {
|
||||
if ((websocket.readyState === WebSocket.OPEN) ||
|
||||
(websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
websocket.close();
|
||||
}
|
||||
websocket.onmessage = function (e) { return; };
|
||||
}
|
||||
}
|
||||
|
||||
// Override internal functions for testing
|
||||
// Takes a send function, returns reference to recv function
|
||||
function testMode(override_send, data_mode) {
|
||||
test_mode = true;
|
||||
mode = data_mode;
|
||||
api.send = override_send;
|
||||
api.close = function () {};
|
||||
return recv_message;
|
||||
}
|
||||
|
||||
function constructor() {
|
||||
// Configuration settings
|
||||
api.maxBufferedAmount = 200;
|
||||
|
||||
// Direct access to send and receive queues
|
||||
api.get_sQ = get_sQ;
|
||||
api.get_rQ = get_rQ;
|
||||
api.get_rQi = get_rQi;
|
||||
api.set_rQi = set_rQi;
|
||||
|
||||
// Routines to read from the receive queue
|
||||
api.rQlen = rQlen;
|
||||
api.rQpeek8 = rQpeek8;
|
||||
api.rQshift8 = rQshift8;
|
||||
api.rQunshift8 = rQunshift8;
|
||||
api.rQshift16 = rQshift16;
|
||||
api.rQshift32 = rQshift32;
|
||||
api.rQshiftStr = rQshiftStr;
|
||||
api.rQshiftBytes = rQshiftBytes;
|
||||
api.rQslice = rQslice;
|
||||
api.rQwait = rQwait;
|
||||
|
||||
api.flush = flush;
|
||||
api.send = send;
|
||||
api.send_string = send_string;
|
||||
|
||||
api.on = on;
|
||||
api.init = init;
|
||||
api.open = open;
|
||||
api.close = close;
|
||||
api.testMode = testMode;
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
return constructor();
|
||||
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint bitwise: false, white: false */
|
||||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||
/*global Util, window, document */
|
||||
|
||||
// Globals defined here
|
||||
|
@ -31,45 +30,47 @@ if (!window.$D) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in WebUtil
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
|
||||
// init log level reading the logging HTTP param
|
||||
WebUtil.init_logging = function(level) {
|
||||
WebUtil.init_logging = function (level) {
|
||||
"use strict";
|
||||
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];
|
||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||
Util._log_level = (param || ['', Util._log_level])[1];
|
||||
}
|
||||
Util.init_logging();
|
||||
};
|
||||
|
||||
|
||||
WebUtil.dirObj = function (obj, depth, parent) {
|
||||
var i, msg = "", val = "";
|
||||
if (! depth) { depth=2; }
|
||||
if (! parent) { parent= ""; }
|
||||
"use strict";
|
||||
if (! depth) { depth = 2; }
|
||||
if (! parent) { parent = ""; }
|
||||
|
||||
// Print the properties of the passed-in object
|
||||
for (i in obj) {
|
||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||
// Print the properties of the passed-in object
|
||||
var msg = "";
|
||||
for (var i in obj) {
|
||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||
// Recurse attributes that are objects
|
||||
msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
|
||||
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
|
||||
} else {
|
||||
//val = new String(obj[i]).replace("\n", " ");
|
||||
var val = "";
|
||||
if (typeof(obj[i]) === "undefined") {
|
||||
val = "undefined";
|
||||
} else {
|
||||
val = obj[i].toString().replace("\n", " ");
|
||||
}
|
||||
if (val.length > 30) {
|
||||
val = val.substr(0,30) + "...";
|
||||
}
|
||||
val = val.substr(0, 30) + "...";
|
||||
}
|
||||
msg += parent + "." + i + ": " + val + "\n";
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +78,8 @@ WebUtil.dirObj = function (obj, depth, parent) {
|
|||
};
|
||||
|
||||
// Read a query string variable
|
||||
WebUtil.getQueryVar = function(name, defVal) {
|
||||
WebUtil.getQueryVar = function (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||
match = document.location.href.match(re);
|
||||
if (typeof defVal === 'undefined') { defVal = null; }
|
||||
|
@ -94,42 +96,50 @@ WebUtil.getQueryVar = function(name, defVal) {
|
|||
*/
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.createCookie = function(name,value,days) {
|
||||
var date, expires, secure;
|
||||
WebUtil.createCookie = function (name, value, days) {
|
||||
"use strict";
|
||||
var date, expires;
|
||||
if (days) {
|
||||
date = new Date();
|
||||
date.setTime(date.getTime()+(days*24*60*60*1000));
|
||||
expires = "; expires="+date.toGMTString();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
|
||||
var secure;
|
||||
if (document.location.protocol === "https:") {
|
||||
secure = "; secure";
|
||||
} else {
|
||||
secure = "";
|
||||
}
|
||||
document.cookie = name+"="+value+expires+"; path=/"+secure;
|
||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||
};
|
||||
|
||||
WebUtil.readCookie = function(name, defaultValue) {
|
||||
var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
|
||||
for(i=0; i < ca.length; i += 1) {
|
||||
c = ca[i];
|
||||
while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
|
||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
|
||||
WebUtil.readCookie = function (name, defaultValue) {
|
||||
"use strict";
|
||||
var nameEQ = name + "=",
|
||||
ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i += 1) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
||||
}
|
||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||
};
|
||||
|
||||
WebUtil.eraseCookie = function(name) {
|
||||
WebUtil.createCookie(name,"",-1);
|
||||
WebUtil.eraseCookie = function (name) {
|
||||
"use strict";
|
||||
WebUtil.createCookie(name, "", -1);
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting handling.
|
||||
*/
|
||||
|
||||
WebUtil.initSettings = function(callback) {
|
||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||
"use strict";
|
||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.get(function (cfg) {
|
||||
|
@ -148,7 +158,8 @@ WebUtil.initSettings = function(callback) {
|
|||
};
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.writeSetting = function(name, value) {
|
||||
WebUtil.writeSetting = function (name, value) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
//console.log("writeSetting:", name, value);
|
||||
if (WebUtil.settings[name] !== value) {
|
||||
|
@ -160,7 +171,8 @@ WebUtil.writeSetting = function(name, value) {
|
|||
}
|
||||
};
|
||||
|
||||
WebUtil.readSetting = function(name, defaultValue) {
|
||||
WebUtil.readSetting = function (name, defaultValue) {
|
||||
"use strict";
|
||||
var value;
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
value = WebUtil.settings[name];
|
||||
|
@ -177,7 +189,8 @@ WebUtil.readSetting = function(name, defaultValue) {
|
|||
}
|
||||
};
|
||||
|
||||
WebUtil.eraseSetting = function(name) {
|
||||
WebUtil.eraseSetting = function (name) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.remove(name);
|
||||
delete WebUtil.settings[name];
|
||||
|
@ -189,9 +202,12 @@ WebUtil.eraseSetting = function(name) {
|
|||
/*
|
||||
* Alternate stylesheet selection
|
||||
*/
|
||||
WebUtil.getStylesheets = function() { var i, links, sheets = [];
|
||||
links = document.getElementsByTagName("link");
|
||||
for (i = 0; i < links.length; i += 1) {
|
||||
WebUtil.getStylesheets = function () {
|
||||
"use strict";
|
||||
var links = document.getElementsByTagName("link");
|
||||
var sheets = [];
|
||||
|
||||
for (var i = 0; i < links.length; i += 1) {
|
||||
if (links[i].title &&
|
||||
links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
|
||||
sheets.push(links[i]);
|
||||
|
@ -202,14 +218,16 @@ WebUtil.getStylesheets = function() { var i, links, sheets = [];
|
|||
|
||||
// No sheet means try and use value from cookie, null sheet used to
|
||||
// clear all alternates.
|
||||
WebUtil.selectStylesheet = function(sheet) {
|
||||
var i, link, sheets = WebUtil.getStylesheets();
|
||||
WebUtil.selectStylesheet = function (sheet) {
|
||||
"use strict";
|
||||
if (typeof sheet === 'undefined') {
|
||||
sheet = 'default';
|
||||
}
|
||||
for (i=0; i < sheets.length; i += 1) {
|
||||
link = sheets[i];
|
||||
if (link.title === sheet) {
|
||||
|
||||
var sheets = WebUtil.getStylesheets();
|
||||
for (var i = 0; i < sheets.length; i += 1) {
|
||||
var link = sheets[i];
|
||||
if (link.title === sheet) {
|
||||
Util.Debug("Using stylesheet " + sheet);
|
||||
link.disabled = false;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
// Karma configuration
|
||||
|
||||
module.exports = function(config) {
|
||||
/*var customLaunchers = {
|
||||
sl_chrome_win7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
|
||||
sl_firefox30_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '30',
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_firefox26_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 26,
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_windows7_ie10: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 7',
|
||||
version: '10'
|
||||
},
|
||||
|
||||
sl_windows81_ie11: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
|
||||
sl_osxmavericks_safari7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
|
||||
sl_osxmtnlion_safari6: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.8',
|
||||
version: '6'
|
||||
}
|
||||
};*/
|
||||
|
||||
var customLaunchers = {};
|
||||
var browsers = [];
|
||||
var useSauce = false;
|
||||
|
||||
if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) {
|
||||
useSauce = true;
|
||||
}
|
||||
|
||||
if (useSauce && process.env.TEST_BROWSER_NAME && process.env.TEST_BROWSER_NAME != 'PhantomJS') {
|
||||
var names = process.env.TEST_BROWSER_NAME.split(',');
|
||||
var platforms = process.env.TEST_BROWSER_OS.split(',');
|
||||
var versions = [];
|
||||
if (process.env.TEST_BROWSER_VERSION) {
|
||||
versions = process.env.TEST_BROWSER_VERSION.split(',');
|
||||
} else {
|
||||
versions = [null];
|
||||
}
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
for (var j = 0; j < platforms.length; j++) {
|
||||
for (var k = 0; k < versions.length; k++) {
|
||||
var launcher_name = 'sl_' + platforms[j].replace(/[^a-zA-Z0-9]/g, '') + '_' + names[i];
|
||||
if (versions[k]) {
|
||||
launcher_name += '_' + versions[k];
|
||||
}
|
||||
|
||||
customLaunchers[launcher_name] = {
|
||||
base: 'SauceLabs',
|
||||
browserName: names[i],
|
||||
platform: platforms[j],
|
||||
};
|
||||
|
||||
if (versions[i]) {
|
||||
customLaunchers[launcher_name].version = versions[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
browsers = Object.keys(customLaunchers);
|
||||
} else {
|
||||
useSauce = false;
|
||||
browsers = ['PhantomJS'];
|
||||
}
|
||||
|
||||
var my_conf = {
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha', 'sinon', 'chai', 'sinon-chai'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser (loaded in order)
|
||||
files: [
|
||||
'tests/fake.*.js',
|
||||
'include/util.js', // load first to avoid issues, since methods are called immediately
|
||||
//'../include/*.js',
|
||||
'include/base64.js',
|
||||
'include/keysym.js',
|
||||
'include/keysymdef.js',
|
||||
'include/keyboard.js',
|
||||
'include/input.js',
|
||||
'include/websock.js',
|
||||
'include/rfb.js',
|
||||
'include/jsunzip.js',
|
||||
'include/des.js',
|
||||
'include/display.js',
|
||||
'tests/test.*.js'
|
||||
],
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
'ui': 'bdd'
|
||||
}
|
||||
},
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
'../include/playback.js',
|
||||
'../include/ui.js'
|
||||
],
|
||||
|
||||
customLaunchers: customLaunchers,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: browsers,
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['mocha', 'saucelabs'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Increase timeout in case connection is slow/we run more browsers than possible
|
||||
// (we currently get 3 for free, and we try to run 7, so it can take a while)
|
||||
captureTimeout: 240000
|
||||
};
|
||||
|
||||
if (useSauce) {
|
||||
my_conf.sauceLabs = {
|
||||
testName: 'noVNC Tests (all)',
|
||||
startConnect: true,
|
||||
};
|
||||
}
|
||||
|
||||
config.set(my_conf);
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "noVNC",
|
||||
"version": "0.5.0",
|
||||
"description": "An HTML5 VNC client",
|
||||
"main": "karma.conf.js",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "karma start karma.conf.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kanaka/noVNC.git"
|
||||
},
|
||||
"author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)",
|
||||
"contributors": [
|
||||
"Solly Ross <sross@redhat.com> (https://github.com/directxman12)",
|
||||
"Peter Åstrand <astrand@cendio.se> (https://github.com/astrand)",
|
||||
"Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)"
|
||||
],
|
||||
"license": "MPL 2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kanaka/noVNC/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kanaka/noVNC",
|
||||
"devDependencies": {
|
||||
"ansi": "^0.3.0",
|
||||
"casperjs": "^1.1.0-beta3",
|
||||
"chai": "^1.9.1",
|
||||
"commander": "^2.2.0",
|
||||
"karma": "^0.12.16",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^0.1.4",
|
||||
"karma-mocha-reporter": "^0.2.5",
|
||||
"karma-phantomjs-launcher": "^0.1.4",
|
||||
"karma-sauce-launcher": "^0.2.8",
|
||||
"karma-sinon": "^1.0.3",
|
||||
"karma-sinon-chai": "^0.1.6",
|
||||
"mocha": "^1.20.1",
|
||||
"open": "0.0.5",
|
||||
"phantom": "^0.6.3",
|
||||
"phantomjs": "^1.9.7-9",
|
||||
"sinon": "^1.10.2",
|
||||
"sinon-chai": "^2.5.0",
|
||||
"spooky": "^0.2.4",
|
||||
"temp": "^0.8.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
var FakeWebSocket;
|
||||
|
||||
(function () {
|
||||
// PhantomJS can't create Event objects directly, so we need to use this
|
||||
function make_event(name, props) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent(name, true, true);
|
||||
if (props) {
|
||||
for (var prop in props) {
|
||||
evt[prop] = props[prop];
|
||||
}
|
||||
}
|
||||
return evt;
|
||||
}
|
||||
|
||||
FakeWebSocket = function (uri, protocols) {
|
||||
this.url = uri;
|
||||
this.binaryType = "arraybuffer";
|
||||
this.extensions = "";
|
||||
|
||||
if (!protocols || typeof protocols === 'string') {
|
||||
this.protocol = protocols;
|
||||
} else {
|
||||
this.protocol = protocols[0];
|
||||
}
|
||||
|
||||
this._send_queue = new Uint8Array(20000);
|
||||
|
||||
this.readyState = FakeWebSocket.CONNECTING;
|
||||
this.bufferedAmount = 0;
|
||||
|
||||
this.__is_fake = true;
|
||||
};
|
||||
|
||||
FakeWebSocket.prototype = {
|
||||
close: function (code, reason) {
|
||||
this.readyState = FakeWebSocket.CLOSED;
|
||||
if (this.onclose) {
|
||||
this.onclose(make_event("close", { 'code': code, 'reason': reason, 'wasClean': true }));
|
||||
}
|
||||
},
|
||||
|
||||
send: function (data) {
|
||||
if (this.protocol == 'base64') {
|
||||
data = Base64.decode(data);
|
||||
} else {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
this._send_queue.set(data, this.bufferedAmount);
|
||||
this.bufferedAmount += data.length;
|
||||
},
|
||||
|
||||
_get_sent_data: function () {
|
||||
var arr = [];
|
||||
for (var i = 0; i < this.bufferedAmount; i++) {
|
||||
arr[i] = this._send_queue[i];
|
||||
}
|
||||
|
||||
this.bufferedAmount = 0;
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
_open: function (data) {
|
||||
this.readyState = FakeWebSocket.OPEN;
|
||||
if (this.onopen) {
|
||||
this.onopen(make_event('open'));
|
||||
}
|
||||
},
|
||||
|
||||
_receive_data: function (data) {
|
||||
this.onmessage(make_event("message", { 'data': data }));
|
||||
}
|
||||
};
|
||||
|
||||
FakeWebSocket.OPEN = WebSocket.OPEN;
|
||||
FakeWebSocket.CONNECTING = WebSocket.CONNECTING;
|
||||
FakeWebSocket.CLOSING = WebSocket.CLOSING;
|
||||
FakeWebSocket.CLOSED = WebSocket.CLOSED;
|
||||
|
||||
FakeWebSocket.__is_fake = true;
|
||||
|
||||
FakeWebSocket.replace = function () {
|
||||
if (!WebSocket.__is_fake) {
|
||||
var real_version = WebSocket;
|
||||
WebSocket = FakeWebSocket;
|
||||
FakeWebSocket.__real_version = real_version;
|
||||
}
|
||||
};
|
||||
|
||||
FakeWebSocket.restore = function () {
|
||||
if (WebSocket.__is_fake) {
|
||||
WebSocket = WebSocket.__real_version;
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -2,7 +2,7 @@ var Spooky = require('spooky');
|
|||
var path = require('path');
|
||||
|
||||
var phantom_path = require('phantomjs').path;
|
||||
var casper_path = path.resolve(__dirname, 'node_modules/casperjs/bin/casperjs');
|
||||
var casper_path = path.resolve(__dirname, '../node_modules/casperjs/bin/casperjs');
|
||||
process.env.PHANTOMJS_EXECUTABLE = phantom_path;
|
||||
var casper_opts = {
|
||||
child: {
|
||||
|
|
|
@ -67,16 +67,16 @@ if (program.autoInject) {
|
|||
temp.track();
|
||||
|
||||
var template = {
|
||||
header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + path.resolve(__dirname, 'node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
|
||||
header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + path.resolve(__dirname, '../node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
|
||||
script_tag: function(p) { return "<script src='" + p + "'></script>"; },
|
||||
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
|
||||
};
|
||||
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/chai/chai.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/mocha/mocha.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/sinon/pkg/sinon.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, 'node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, '../node_modules/chai/chai.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, '../node_modules/mocha/mocha.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, '../node_modules/sinon/pkg/sinon.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, '../node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n" + template.script_tag(path.resolve(__dirname, '../node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n<script>mocha.setup('bdd');</script>";
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// requires local modules: base64
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Base64 Tools', function() {
|
||||
"use strict";
|
||||
|
||||
var BIN_ARR = new Array(256);
|
||||
for (var i = 0; i < 256; i++) {
|
||||
BIN_ARR[i] = i;
|
||||
}
|
||||
|
||||
var B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==";
|
||||
|
||||
|
||||
describe('encode', function() {
|
||||
it('should encode a binary string into Base64', function() {
|
||||
var encoded = Base64.encode(BIN_ARR);
|
||||
expect(encoded).to.equal(B64_STR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', function() {
|
||||
it('should decode a Base64 string into a normal string', function() {
|
||||
var decoded = Base64.decode(B64_STR);
|
||||
expect(decoded).to.deep.equal(BIN_ARR);
|
||||
});
|
||||
|
||||
it('should throw an error if we have extra characters at the end of the string', function() {
|
||||
expect(function () { Base64.decode(B64_STR+'abcdef'); }).to.throw(Error);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,332 @@
|
|||
// requires local modules: util, base64, display
|
||||
/* jshint expr: true */
|
||||
var expect = chai.expect;
|
||||
|
||||
chai.use(function (_chai, utils) {
|
||||
_chai.Assertion.addMethod('displayed', function (target_data) {
|
||||
var obj = this._obj;
|
||||
var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data;
|
||||
// NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
|
||||
var data = new Uint8Array(data_cl);
|
||||
this.assert(utils.eql(data, target_data),
|
||||
"expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
|
||||
"expected #{this} not to have displayed the image #{act}",
|
||||
target_data,
|
||||
data);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Display/Canvas Helper', function () {
|
||||
var checked_data = [
|
||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
|
||||
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
|
||||
];
|
||||
checked_data = new Uint8Array(checked_data);
|
||||
|
||||
var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
|
||||
basic_data = new Uint8Array(basic_data);
|
||||
|
||||
function make_image_canvas (input_data) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 4;
|
||||
canvas.height = 4;
|
||||
var ctx = canvas.getContext('2d');
|
||||
var data = ctx.createImageData(4, 4);
|
||||
for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
|
||||
ctx.putImageData(data, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
describe('viewport handling', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(5, 5);
|
||||
display.viewportChange(1, 1, 3, 3);
|
||||
display.getCleanDirtyReset();
|
||||
});
|
||||
|
||||
it('should take viewport location into consideration when drawing images', function () {
|
||||
display.resize(4, 4);
|
||||
display.viewportChange(0, 0, 2, 2);
|
||||
display.drawImage(make_image_canvas(basic_data), 1, 1);
|
||||
|
||||
var expected = new Uint8Array(16);
|
||||
var i;
|
||||
for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
|
||||
for (i = 8; i < 16; i++) { expected[i] = 0; }
|
||||
expect(display).to.have.displayed(expected);
|
||||
});
|
||||
|
||||
it('should redraw the left side when shifted left', function () {
|
||||
display.viewportChange(-1, 0, 3, 3);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 });
|
||||
});
|
||||
|
||||
it('should redraw the right side when shifted right', function () {
|
||||
display.viewportChange(1, 0, 3, 3);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 });
|
||||
});
|
||||
|
||||
it('should redraw the top part when shifted up', function () {
|
||||
display.viewportChange(0, -1, 3, 3);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 });
|
||||
});
|
||||
|
||||
it('should redraw the bottom part when shifted down', function () {
|
||||
display.viewportChange(0, 1, 3, 3);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 });
|
||||
});
|
||||
|
||||
it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () {
|
||||
display.viewportChange(0, 1, 3, 3);
|
||||
var cdr1 = display.getCleanDirtyReset();
|
||||
var cdr2 = display.getCleanDirtyReset();
|
||||
expect(cdr1).to.not.deep.equal(cdr2);
|
||||
expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 });
|
||||
expect(cdr2.dirtyBoxes).to.be.empty;
|
||||
});
|
||||
|
||||
it('should simply mark the whole display area as dirty if not using viewports', function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
|
||||
display.resize(5, 5);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('resizing', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(4, 3);
|
||||
});
|
||||
|
||||
it('should change the size of the logical canvas', function () {
|
||||
display.resize(5, 7);
|
||||
expect(display._fb_width).to.equal(5);
|
||||
expect(display._fb_height).to.equal(7);
|
||||
});
|
||||
|
||||
it('should update the viewport dimensions', function () {
|
||||
sinon.spy(display, 'viewportChange');
|
||||
display.resize(2, 2);
|
||||
expect(display.viewportChange).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawing', function () {
|
||||
|
||||
// TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
|
||||
// basic cases
|
||||
function drawing_tests (pref_js) {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
|
||||
display.resize(4, 4);
|
||||
});
|
||||
|
||||
it('should clear the screen on #clear without a logo set', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
|
||||
display._logo = null;
|
||||
display.clear();
|
||||
display.resize(4, 4);
|
||||
var empty = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
|
||||
expect(display).to.have.displayed(new Uint8Array(empty));
|
||||
});
|
||||
|
||||
it('should draw the logo on #clear with a logo set', function (done) {
|
||||
display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() };
|
||||
display._drawCtx._act_drawImg = display._drawCtx.drawImage;
|
||||
display._drawCtx.drawImage = function (img, x, y) {
|
||||
this._act_drawImg(img, x, y);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
done();
|
||||
};
|
||||
display.clear();
|
||||
expect(display._fb_width).to.equal(4);
|
||||
expect(display._fb_height).to.equal(4);
|
||||
});
|
||||
|
||||
it('should support filling a rectangle with particular color via #fillRect', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support copying an portion of the canvas via #copyImage', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
|
||||
display.copyImage(0, 0, 2, 2, 2, 2);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing tile data with a background color and sub tiles', function () {
|
||||
display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.finishTile();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing BGRX blit images with true color via #blitImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 4] = checked_data[i * 4 + 2];
|
||||
data[i * 4 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 4 + 2] = checked_data[i * 4];
|
||||
data[i * 4 + 3] = checked_data[i * 4 + 3];
|
||||
}
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 3] = checked_data[i * 4];
|
||||
data[i * 3 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 3 + 2] = checked_data[i * 4 + 2];
|
||||
}
|
||||
display.blitRgbImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing blit images from a data URL via #blitStringImage', function (done) {
|
||||
var img_url = make_image_canvas(checked_data).toDataURL();
|
||||
display._drawCtx._act_drawImg = display._drawCtx.drawImage;
|
||||
display._drawCtx.drawImage = function (img, x, y) {
|
||||
this._act_drawImg(img, x, y);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
done();
|
||||
};
|
||||
display.blitStringImage(img_url, 0, 0);
|
||||
});
|
||||
|
||||
it('should support drawing solid colors with color maps', function () {
|
||||
display._true_color = false;
|
||||
display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
|
||||
display.fillRect(0, 0, 4, 4, [1]);
|
||||
display.fillRect(0, 0, 2, 2, [0]);
|
||||
display.fillRect(2, 2, 2, 2, [0]);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing blit images with color maps', function () {
|
||||
display._true_color = false;
|
||||
display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
|
||||
var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing an image object via #drawImage', function () {
|
||||
var img = make_image_canvas(checked_data);
|
||||
display.drawImage(img, 0, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
}
|
||||
|
||||
describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
|
||||
describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
|
||||
});
|
||||
|
||||
describe('the render queue processor', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false });
|
||||
display.resize(4, 4);
|
||||
sinon.spy(display, '_scan_renderQ');
|
||||
this.old_requestAnimFrame = window.requestAnimFrame;
|
||||
window.requestAnimFrame = function (cb) {
|
||||
this.next_frame_cb = cb;
|
||||
}.bind(this);
|
||||
this.next_frame = function () { this.next_frame_cb(); };
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.requestAnimFrame = this.old_requestAnimFrame;
|
||||
});
|
||||
|
||||
it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
|
||||
display.renderQ_push({ type: 'noop' }); // does nothing
|
||||
expect(display._scan_renderQ).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
|
||||
display._renderQ.length = 2;
|
||||
display.renderQ_push({ type: 'noop' });
|
||||
expect(display._scan_renderQ).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
|
||||
var img = { complete: false };
|
||||
display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
|
||||
{ type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
|
||||
display.drawImage = sinon.spy();
|
||||
display.fillRect = sinon.spy();
|
||||
|
||||
display._scan_renderQ();
|
||||
expect(display.drawImage).to.not.have.been.called;
|
||||
expect(display.fillRect).to.not.have.been.called;
|
||||
|
||||
display._renderQ[0].img.complete = true;
|
||||
this.next_frame();
|
||||
expect(display.drawImage).to.have.been.calledOnce;
|
||||
expect(display.fillRect).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should draw a blit image on type "blit"', function () {
|
||||
display.blitImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
|
||||
expect(display.blitImage).to.have.been.calledOnce;
|
||||
expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
|
||||
});
|
||||
|
||||
it('should draw a blit RGB image on type "blitRgb"', function () {
|
||||
display.blitRgbImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
|
||||
expect(display.blitRgbImage).to.have.been.calledOnce;
|
||||
expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
|
||||
});
|
||||
|
||||
it('should copy a region on type "copy"', function () {
|
||||
display.copyImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
|
||||
expect(display.copyImage).to.have.been.calledOnce;
|
||||
expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
|
||||
});
|
||||
|
||||
it('should fill a rect with a given color on type "fill"', function () {
|
||||
display.fillRect = sinon.spy();
|
||||
display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
|
||||
expect(display.fillRect).to.have.been.calledOnce;
|
||||
expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
|
||||
});
|
||||
|
||||
it('should draw an image from an image object on type "img" (if complete)', function () {
|
||||
display.drawImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
|
||||
expect(display.drawImage).to.have.been.calledOnce;
|
||||
expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,6 @@
|
|||
var assert = chai.assert;
|
||||
// requires local modules: keysym, keysymdef, keyboard
|
||||
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Helpers', function() {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// requires local modules: input, keyboard, keysymdef
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
|
||||
/* jshint newcap: false, expr: true */
|
||||
describe('Key Event Pipeline Stages', function() {
|
||||
"use strict";
|
||||
describe('Decode Keyboard Events', function() {
|
||||
|
@ -50,7 +51,7 @@ describe('Key Event Pipeline Stages', function() {
|
|||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
|
||||
done();
|
||||
}).keydown({keyCode: 0x41})
|
||||
}).keydown({keyCode: 0x41});
|
||||
});
|
||||
it('should forward keyup events with the right type', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,105 @@
|
|||
// requires local modules: util
|
||||
/* jshint expr: true */
|
||||
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Utils', function() {
|
||||
"use strict";
|
||||
|
||||
describe('Array instance methods', function () {
|
||||
describe('push8', function () {
|
||||
it('should push a byte on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push8(128);
|
||||
expect(arr).to.deep.equal([1, 128]);
|
||||
});
|
||||
|
||||
it('should only use the least significant byte of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push8(0xABCD);
|
||||
expect(arr).to.deep.equal([1, 0xCD]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('push16', function () {
|
||||
it('should push two bytes on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push16(0xABCD);
|
||||
expect(arr).to.deep.equal([1, 0xAB, 0xCD]);
|
||||
});
|
||||
|
||||
it('should only use the two least significant bytes of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push16(0xABCDEF);
|
||||
expect(arr).to.deep.equal([1, 0xCD, 0xEF]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('push32', function () {
|
||||
it('should push four bytes on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push32(0xABCDEF12);
|
||||
expect(arr).to.deep.equal([1, 0xAB, 0xCD, 0xEF, 0x12]);
|
||||
});
|
||||
|
||||
it('should only use the four least significant bytes of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push32(0xABCDEF1234);
|
||||
expect(arr).to.deep.equal([1, 0xCD, 0xEF, 0x12, 0x34]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('logging functions', function () {
|
||||
beforeEach(function () {
|
||||
sinon.spy(console, 'log');
|
||||
sinon.spy(console, 'warn');
|
||||
sinon.spy(console, 'error');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
console.log.restore();
|
||||
console.warn.restore();
|
||||
console.error.restore();
|
||||
});
|
||||
|
||||
it('should use noop for levels lower than the min level', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Debug('hi');
|
||||
Util.Info('hello');
|
||||
expect(console.log).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should use console.log for Debug and Info', function () {
|
||||
Util.init_logging('debug');
|
||||
Util.Debug('dbg');
|
||||
Util.Info('inf');
|
||||
expect(console.log).to.have.been.calledWith('dbg');
|
||||
expect(console.log).to.have.been.calledWith('inf');
|
||||
});
|
||||
|
||||
it('should use console.warn for Warn', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Warn('wrn');
|
||||
expect(console.warn).to.have.been.called;
|
||||
expect(console.warn).to.have.been.calledWith('wrn');
|
||||
});
|
||||
|
||||
it('should use console.error for Error', function () {
|
||||
Util.init_logging('error');
|
||||
Util.Error('err');
|
||||
expect(console.error).to.have.been.called;
|
||||
expect(console.error).to.have.been.calledWith('err');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(directxman12): test the conf_default and conf_defaults methods
|
||||
// TODO(directxman12): test decodeUTF8
|
||||
// TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent)
|
||||
// TODO(directxman12): figure out a good way to test getPosition and getEventPosition
|
||||
// TODO(directxman12): figure out how to test the browser detection functions properly
|
||||
// (we can't really test them against the browsers, except for Gecko
|
||||
// via PhantomJS, the default test driver)
|
||||
// TODO(directxman12): figure out how to test Util.Flash
|
||||
});
|
|
@ -0,0 +1,480 @@
|
|||
// requires local modules: websock, base64, util
|
||||
// requires test modules: fake.websocket
|
||||
/* jshint expr: true */
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Websock', function() {
|
||||
"use strict";
|
||||
|
||||
describe('Queue methods', function () {
|
||||
var sock;
|
||||
var RQ_TEMPLATE = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
for (var i = RQ_TEMPLATE.length - 1; i >= 0; i--) {
|
||||
sock.rQunshift8(RQ_TEMPLATE[i]);
|
||||
}
|
||||
});
|
||||
describe('rQlen', function () {
|
||||
it('should return the length of the receive queue', function () {
|
||||
sock.set_rQi(0);
|
||||
|
||||
expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length);
|
||||
});
|
||||
|
||||
it("should return the proper length if we read some from the receive queue", function () {
|
||||
sock.set_rQi(1);
|
||||
|
||||
expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQpeek8', function () {
|
||||
it('should peek at the next byte without poping it off the queue', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var peek = sock.rQpeek8();
|
||||
expect(sock.rQpeek8()).to.equal(peek);
|
||||
expect(sock.rQlen()).to.equal(bef_len);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift8', function () {
|
||||
it('should pop a single byte from the receive queue', function () {
|
||||
var peek = sock.rQpeek8();
|
||||
var bef_len = sock.rQlen();
|
||||
expect(sock.rQshift8()).to.equal(peek);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQunshift8', function () {
|
||||
it('should place a byte at the front of the queue', function () {
|
||||
sock.rQunshift8(255);
|
||||
expect(sock.rQpeek8()).to.equal(255);
|
||||
expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift16', function () {
|
||||
it('should pop two bytes from the receive queue and return a single number', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
|
||||
expect(sock.rQshift16()).to.equal(expected);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift32', function () {
|
||||
it('should pop four bytes from the receive queue and return a single number', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var expected = (RQ_TEMPLATE[0] << 24) +
|
||||
(RQ_TEMPLATE[1] << 16) +
|
||||
(RQ_TEMPLATE[2] << 8) +
|
||||
RQ_TEMPLATE[3];
|
||||
expect(sock.rQshift32()).to.equal(expected);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshiftStr', function () {
|
||||
it('should shift the given number of bytes off of the receive queue and return a string', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var bef_rQi = sock.get_rQi();
|
||||
var shifted = sock.rQshiftStr(3);
|
||||
expect(shifted).to.be.a('string');
|
||||
expect(shifted).to.equal(String.fromCharCode.apply(null, RQ_TEMPLATE.slice(bef_rQi, bef_rQi + 3)));
|
||||
expect(sock.rQlen()).to.equal(bef_len - 3);
|
||||
});
|
||||
|
||||
it('should shift the entire rest of the queue off if no length is given', function () {
|
||||
sock.rQshiftStr();
|
||||
expect(sock.rQlen()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshiftBytes', function () {
|
||||
it('should shift the given number of bytes of the receive queue and return an array', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var bef_rQi = sock.get_rQi();
|
||||
var shifted = sock.rQshiftBytes(3);
|
||||
expect(shifted).to.be.an.instanceof(Array);
|
||||
expect(shifted).to.deep.equal(RQ_TEMPLATE.slice(bef_rQi, bef_rQi + 3));
|
||||
expect(sock.rQlen()).to.equal(bef_len - 3);
|
||||
});
|
||||
|
||||
it('should shift the entire rest of the queue off if no length is given', function () {
|
||||
sock.rQshiftBytes();
|
||||
expect(sock.rQlen()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQslice', function () {
|
||||
beforeEach(function () {
|
||||
sock.set_rQi(0);
|
||||
});
|
||||
|
||||
it('should not modify the receive queue', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
sock.rQslice(0, 2);
|
||||
expect(sock.rQlen()).to.equal(bef_len);
|
||||
});
|
||||
|
||||
it('should return an array containing the given slice of the receive queue', function () {
|
||||
var sl = sock.rQslice(0, 2);
|
||||
expect(sl).to.be.an.instanceof(Array);
|
||||
expect(sl).to.deep.equal(RQ_TEMPLATE.slice(0, 2));
|
||||
});
|
||||
|
||||
it('should use the rest of the receive queue if no end is given', function () {
|
||||
var sl = sock.rQslice(1);
|
||||
expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
|
||||
expect(sl).to.deep.equal(RQ_TEMPLATE.slice(1));
|
||||
});
|
||||
|
||||
it('should take the current rQi in to account', function () {
|
||||
sock.set_rQi(1);
|
||||
expect(sock.rQslice(0, 2)).to.deep.equal(RQ_TEMPLATE.slice(1, 3));
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQwait', function () {
|
||||
beforeEach(function () {
|
||||
sock.set_rQi(0);
|
||||
});
|
||||
|
||||
it('should return true if there are not enough bytes in the receive queue', function () {
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if there are enough bytes in the receive queue', function () {
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
|
||||
});
|
||||
|
||||
it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
|
||||
sock.set_rQi(5);
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
|
||||
expect(sock.get_rQi()).to.equal(1);
|
||||
});
|
||||
|
||||
it('should raise an error if we try to go back more than possible', function () {
|
||||
sock.set_rQi(5);
|
||||
expect(function () { sock.rQwait('hi', RQ_TEMPLATE.length, 6); }).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should not reduce rQi if there are enough bytes', function () {
|
||||
sock.set_rQi(5);
|
||||
sock.rQwait('hi', 1, 6);
|
||||
expect(sock.get_rQi()).to.equal(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('flush', function () {
|
||||
beforeEach(function () {
|
||||
sock._websocket = {
|
||||
send: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
it('should actually send on the websocket if the websocket does not have too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
sock._sQ = [1, 2, 3];
|
||||
var encoded = sock._encode_message();
|
||||
|
||||
sock.flush();
|
||||
expect(sock._websocket.send).to.have.been.calledOnce;
|
||||
expect(sock._websocket.send).to.have.been.calledWith(encoded);
|
||||
});
|
||||
|
||||
it('should return true if the websocket did not have too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
|
||||
expect(sock.flush()).to.be.true;
|
||||
});
|
||||
|
||||
it('should not call send if we do not have anything queued up', function () {
|
||||
sock._sQ = [];
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
|
||||
sock.flush();
|
||||
|
||||
expect(sock._websocket.send).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should not send and return false if the websocket has too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 12;
|
||||
|
||||
expect(sock.flush()).to.be.false;
|
||||
expect(sock._websocket.send).to.not.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send', function () {
|
||||
beforeEach(function () {
|
||||
sock.flush = sinon.spy();
|
||||
});
|
||||
|
||||
it('should add to the send queue', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
var sq = sock.get_sQ();
|
||||
expect(sock.get_sQ().slice(sq.length - 3)).to.deep.equal([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should call flush', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
expect(sock.flush).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send_string', function () {
|
||||
beforeEach(function () {
|
||||
sock.send = sinon.spy();
|
||||
});
|
||||
|
||||
it('should call send after converting the string to an array', function () {
|
||||
sock.send_string("\x01\x02\x03");
|
||||
expect(sock.send).to.have.been.calledWith([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('lifecycle methods', function () {
|
||||
var old_WS;
|
||||
before(function () {
|
||||
old_WS = WebSocket;
|
||||
});
|
||||
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
WebSocket = sinon.spy();
|
||||
WebSocket.OPEN = old_WS.OPEN;
|
||||
WebSocket.CONNECTING = old_WS.CONNECTING;
|
||||
WebSocket.CLOSING = old_WS.CLOSING;
|
||||
WebSocket.CLOSED = old_WS.CLOSED;
|
||||
});
|
||||
|
||||
describe('opening', function () {
|
||||
it('should pick the correct protocols if none are given' , function () {
|
||||
|
||||
});
|
||||
|
||||
it('should open the actual websocket', function () {
|
||||
sock.open('ws://localhost:8675', 'base64');
|
||||
expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'base64');
|
||||
});
|
||||
|
||||
it('should fail if we try to use binary but do not support it', function () {
|
||||
expect(function () { sock.open('ws:///', 'binary'); }).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should fail if we specified an array with only binary and we do not support it', function () {
|
||||
expect(function () { sock.open('ws:///', ['binary']); }).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should skip binary if we have multiple options for encoding and do not support binary', function () {
|
||||
sock.open('ws:///', ['binary', 'base64']);
|
||||
expect(WebSocket).to.have.been.calledWith('ws:///', ['base64']);
|
||||
});
|
||||
// it('should initialize the event handlers')?
|
||||
});
|
||||
|
||||
describe('closing', function () {
|
||||
beforeEach(function () {
|
||||
sock.open('ws://');
|
||||
sock._websocket.close = sinon.spy();
|
||||
});
|
||||
|
||||
it('should close the actual websocket if it is open', function () {
|
||||
sock._websocket.readyState = WebSocket.OPEN;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should close the actual websocket if it is connecting', function () {
|
||||
sock._websocket.readyState = WebSocket.CONNECTING;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not try to close the actual websocket if closing', function () {
|
||||
sock._websocket.readyState = WebSocket.CLOSING;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should not try to close the actual websocket if closed', function () {
|
||||
sock._websocket.readyState = WebSocket.CLOSED;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should reset onmessage to not call _recv_message', function () {
|
||||
sinon.spy(sock, '_recv_message');
|
||||
sock.close();
|
||||
sock._websocket.onmessage(null);
|
||||
try {
|
||||
expect(sock._recv_message).not.to.have.been.called;
|
||||
} finally {
|
||||
sock._recv_message.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('event handlers', function () {
|
||||
beforeEach(function () {
|
||||
sock._recv_message = sinon.spy();
|
||||
sock.on('open', sinon.spy());
|
||||
sock.on('close', sinon.spy());
|
||||
sock.on('error', sinon.spy());
|
||||
sock.open('ws://');
|
||||
});
|
||||
|
||||
it('should call _recv_message on a message', function () {
|
||||
sock._websocket.onmessage(null);
|
||||
expect(sock._recv_message).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should copy the mode over upon opening', function () {
|
||||
sock._websocket.protocol = 'cheese';
|
||||
sock._websocket.onopen();
|
||||
expect(sock._mode).to.equal('cheese');
|
||||
});
|
||||
|
||||
it('should assume base64 if no protocol was available on opening', function () {
|
||||
sock._websocket.protocol = null;
|
||||
sock._websocket.onopen();
|
||||
expect(sock._mode).to.equal('base64');
|
||||
});
|
||||
|
||||
it('should call the open event handler on opening', function () {
|
||||
sock._websocket.onopen();
|
||||
expect(sock._eventHandlers.open).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should call the close event handler on closing', function () {
|
||||
sock._websocket.onclose();
|
||||
expect(sock._eventHandlers.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should call the error event handler on error', function () {
|
||||
sock._websocket.onerror();
|
||||
expect(sock._eventHandlers.error).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
WebSocket = old_WS;
|
||||
});
|
||||
});
|
||||
|
||||
describe('WebSocket Receiving', function () {
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
});
|
||||
|
||||
it('should support decoding base64 string data to add it to the receive queue', function () {
|
||||
var msg = { data: Base64.encode([1, 2, 3]) };
|
||||
sock._mode = 'base64';
|
||||
sock._recv_message(msg);
|
||||
expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
|
||||
});
|
||||
|
||||
it('should support adding binary Uint8Array data to the receive queue', function () {
|
||||
var msg = { data: new Uint8Array([1, 2, 3]) };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
|
||||
});
|
||||
|
||||
it('should call the message event handler if present', function () {
|
||||
sock._eventHandlers.message = sinon.spy();
|
||||
var msg = { data: Base64.encode([1, 2, 3]) };
|
||||
sock._mode = 'base64';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.message).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not call the message event handler if there is nothing in the receive queue', function () {
|
||||
sock._eventHandlers.message = sinon.spy();
|
||||
var msg = { data: Base64.encode([]) };
|
||||
sock._mode = 'base64';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.message).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should compact the receive queue', function () {
|
||||
// NB(sross): while this is an internal implementation detail, it's important to
|
||||
// test, otherwise the receive queue could become very large very quickly
|
||||
sock._rQ = [0, 1, 2, 3, 4, 5];
|
||||
sock.set_rQi(6);
|
||||
sock._rQmax = 3;
|
||||
var msg = { data: Base64.encode([1, 2, 3]) };
|
||||
sock._mode = 'base64';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._rQ.length).to.equal(3);
|
||||
expect(sock.get_rQi()).to.equal(0);
|
||||
});
|
||||
|
||||
it('should call the error event handler on an exception', function () {
|
||||
sock._eventHandlers.error = sinon.spy();
|
||||
sock._eventHandlers.message = sinon.stub().throws();
|
||||
var msg = { data: Base64.encode([1, 2, 3]) };
|
||||
sock._mode = 'base64';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.error).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Data encoding', function () {
|
||||
before(function () { FakeWebSocket.replace(); });
|
||||
after(function () { FakeWebSocket.restore(); });
|
||||
|
||||
describe('as binary data', function () {
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
sock.open('ws://', 'binary');
|
||||
sock._websocket._open();
|
||||
});
|
||||
|
||||
it('should convert the send queue into an ArrayBuffer', function () {
|
||||
sock._sQ = [1, 2, 3];
|
||||
var res = sock._encode_message(); // An ArrayBuffer
|
||||
expect(new Uint8Array(res)).to.deep.equal(new Uint8Array(res));
|
||||
});
|
||||
|
||||
it('should properly pass the encoded data off to the actual WebSocket', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
expect(sock._websocket._get_sent_data()).to.deep.equal([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('as Base64 data', function () {
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
sock.open('ws://', 'base64');
|
||||
sock._websocket._open();
|
||||
});
|
||||
|
||||
it('should convert the send queue into a Base64-encoded string', function () {
|
||||
sock._sQ = [1, 2, 3];
|
||||
expect(sock._encode_message()).to.equal(Base64.encode([1, 2, 3]));
|
||||
});
|
||||
|
||||
it('should properly pass the encoded data off to the actual WebSocket', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
expect(sock._websocket._get_sent_data()).to.deep.equal([1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue