1
0
mirror of https://github.com/fcwu/docker-ubuntu-vnc-desktop synced 2026-08-05 16:12:41 +02:00

Add web vnc on port 6080

This commit is contained in:
Doro Wu
2014-02-19 16:51:28 +08:00
parent 33c92d861d
commit a39e42097e
117 changed files with 16717 additions and 4 deletions
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>Javascript Arrays Performance Test</title>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="browser.js"></script>
<script src="stats.js"></script>
<script src="arrays.js"></script>
</head>
<body>
<h3>Javascript Arrays Performance Test</h3>
Iterations: <input id='iterations' style='width:50'>&nbsp;
Array Size: <input id='arraySize' style='width:40'>*1024&nbsp;
<input id='startButton' type='button' value='Run Tests'
onclick="begin();">&nbsp;
<br><br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=50></textarea>
</br>
<canvas id="canvas" style="display: none;"></canvas>
</body>
<script>
var verbose = true;
window.onload = function() {
vmessage("in onload");
init();
}
</script>
</html>
+375
View File
@@ -0,0 +1,375 @@
/*
* Javascript binary array performance tests
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
var ctx, i, j, randlist,
new_normal, new_imageData, new_arrayBuffer,
browser = Browser.browser + " " +
Browser.version + " on " +
Browser.OS,
do_imageData = false,
do_arrayBuffer = false,
conf = {
'create_cnt' : 2000,
'read_cnt' : 5000000,
'write_cnt' : 5000000,
'iterations' : 0,
'order_l1' : [browser],
'order_l2' : ['normal',
'imageData',
'arrayBuffer'],
'order_l3' : ['create',
'sequentialRead',
'randomRead',
'sequentialWrite']
},
stats = {},
testFunc = {},
iteration, arraySize;
var newline = "\n";
if (Util.Engine.trident) {
var newline = "<br>\n";
}
function message(str) {
//console.log(str);
cell = $D('messages');
cell.innerHTML += str + newline;
cell.scrollTop = cell.scrollHeight;
}
function vmessage(str) {
if (verbose) {
message(str);
} else {
console.log(str);
}
}
new_normal = function() {
var arr = [], i;
for (i = 0; i < arraySize; i++) {
arr[i] = 0;
}
return arr;
}
/* Will be overridden with real function */
new_imageData = function() {
throw("imageData not supported");
};
new_imageData_createImageData = function() {
var imageData = ctx.createImageData(1024/4, arraySize / 1024);
return imageData.data;
};
new_imageData_getImageData = function() {
var imageData = ctx.getImageData(0, 0, 1024/4, arraySize / 1024),
arr = imageData.data;
for (i = 0; i < arraySize; i++) {
arr[i] = 0;
}
return arr;
};
new_arrayBuffer = function() {
var arr = new ArrayBuffer(arraySize);
return new Uint8Array(arr);
}
function init_randlist() {
randlist = [];
for (var i=0; i < arraySize; i++) {
randlist[i] = parseInt(Math.random() * 256, 10);
}
}
function copy_randlist(arr) {
for (var i=0; i < arraySize; i++) {
arr[i] = randlist[i];
}
}
function begin() {
var i, j;
conf.iterations = parseInt($D('iterations').value, 10);
arraySize = parseInt($D('arraySize').value, 10) * 1024;
init_randlist();
// TODO: randomize test_list
stats = {};
for (i = 0; i < conf.order_l2.length; i++) {
stats[conf.order_l2[i]] = {};
for (j = 0; j < conf.order_l3.length; j++) {
stats[conf.order_l2[i]][conf.order_l3[j]] = [];
}
}
$D('startButton').value = "Running";
$D('startButton').disabled = true;
message("running " + conf.iterations + " test iterations");
iteration = 1;
setTimeout(run_next_iteration, 250);
}
function finish() {
var totalTime, arrayType, testType, times;
message("tests finished");
for (j = 0; j < conf.order_l3.length; j++) {
testType = conf.order_l3[j];
message("Test '" + testType + "'");
for (i = 0; i < conf.order_l2.length; i++) {
arrayType = conf.order_l2[i];
message(" Array Type '" + arrayType);
times = stats[arrayType][testType];
message(" Average : " + times.mean() + "ms" +
" (Total: " + times.sum() + "ms)");
message(" Min/Max : " + times.min() + "ms/" +
times.max() + "ms");
message(" StdDev : " + times.stdDev() + "ms");
}
}
vmessage("array_chart.py JSON data:");
chart_data = {'conf' : conf, 'stats' : { } };
chart_data.stats[browser] = stats;
chart_data.stats['next_browser'] = {};
vmessage(JSON.stringify(chart_data, null, 2));
$D('startButton').disabled = false;
$D('startButton').value = "Run Tests";
}
function run_next_iteration() {
var arrayType, testType, deltaTime;
for (i = 0; i < conf.order_l2.length; i++) {
arrayType = conf.order_l2[i];
if (arrayType === 'imageData' && (!do_imageData)) {
continue;
}
if (arrayType === 'arrayBuffer' && (!do_arrayBuffer)) {
continue;
}
for (j = 0; j < conf.order_l3.length; j++) {
testType = conf.order_l3[j];
deltaTime = testFunc[arrayType + "_" + testType]();
stats[arrayType][testType].push(deltaTime);
vmessage("test " + (arrayType + "_" + testType) +
" time: " + (deltaTime) + "ms");
}
}
message("finished test iteration " + iteration);
if (iteration >= conf.iterations) {
setTimeout(finish, 1);
return;
}
iteration++;
setTimeout(run_next_iteration, 1);
}
/*
* Test functions
*/
testFunc["normal_create"] = function() {
var cnt, arrNormal, startTime, endTime;
vmessage("create normal array " + conf.create_cnt + "x, initialized to 0");
startTime = (new Date()).getTime();
for (cnt = 0; cnt < conf.create_cnt; cnt++) {
arrNormal = new_normal();
}
endTime = (new Date()).getTime();
return endTime - startTime;
};
testFunc["imageData_create"] = function() {
var cnt, arrImage, startTime, endTime;
vmessage("create imageData array " + conf.create_cnt + "x, initialized to 0");
startTime = (new Date()).getTime();
for (cnt = 0; cnt < conf.create_cnt; cnt++) {
arrImage = new_imageData();
}
endTime = (new Date()).getTime();
if (arrImage[103] !== 0) {
message("Initialization failed, arrImage[103] is: " + arrImage[103]);
throw("Initialization failed, arrImage[103] is: " + arrImage[103]);
}
return endTime - startTime;
};
testFunc["arrayBuffer_create"] = function() {
var cnt, arrBuffer, startTime, endTime;
vmessage("create arrayBuffer array " + conf.create_cnt + "x, initialized to 0");
startTime = (new Date()).getTime();
for (cnt = 0; cnt < conf.create_cnt; cnt++) {
arrBuffer = new_arrayBuffer();
}
endTime = (new Date()).getTime();
if (arrBuffer[103] !== 0) {
message("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]);
throw("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]);
}
return endTime - startTime;
};
function test_sequentialRead(arr) {
var i, j, cnt, startTime, endTime;
/* Initialize the array */
copy_randlist(arr);
startTime = (new Date()).getTime();
i = 0;
j = 0;
for (cnt = 0; cnt < conf.read_cnt; cnt++) {
j = arr[i];
i++;
if (i >= arraySize) {
i = 0;
}
}
endTime = (new Date()).getTime();
return endTime - startTime;
}
function test_randomRead(arr) {
var i, cnt, startTime, endTime;
/* Initialize the array */
copy_randlist(arr); // used as jumplist
startTime = (new Date()).getTime();
i = 0;
for (cnt = 0; cnt < conf.read_cnt; cnt++) {
i = (arr[i] + cnt) % arraySize;
}
endTime = (new Date()).getTime();
return endTime - startTime;
}
function test_sequentialWrite(arr) {
var i, cnt, startTime, endTime;
/* Initialize the array */
copy_randlist(arr);
startTime = (new Date()).getTime();
i = 0;
for (cnt = 0; cnt < conf.write_cnt; cnt++) {
arr[i] = (cnt % 256);
i++;
if (i >= arraySize) {
i = 0;
}
}
endTime = (new Date()).getTime();
return endTime - startTime;
}
/* Sequential Read Tests */
testFunc["normal_sequentialRead"] = function() {
vmessage("read normal array " + conf.read_cnt + "x");
return test_sequentialRead(new_normal());
};
testFunc["imageData_sequentialRead"] = function() {
vmessage("read imageData array " + conf.read_cnt + "x");
return test_sequentialRead(new_imageData());
};
testFunc["arrayBuffer_sequentialRead"] = function() {
vmessage("read arrayBuffer array " + conf.read_cnt + "x");
return test_sequentialRead(new_arrayBuffer());
};
/* Random Read Tests */
testFunc["normal_randomRead"] = function() {
vmessage("read normal array " + conf.read_cnt + "x");
return test_randomRead(new_normal());
};
testFunc["imageData_randomRead"] = function() {
vmessage("read imageData array " + conf.read_cnt + "x");
return test_randomRead(new_imageData());
};
testFunc["arrayBuffer_randomRead"] = function() {
vmessage("read arrayBuffer array " + conf.read_cnt + "x");
return test_randomRead(new_arrayBuffer());
};
/* Sequential Write Tests */
testFunc["normal_sequentialWrite"] = function() {
vmessage("write normal array " + conf.write_cnt + "x");
return test_sequentialWrite(new_normal());
};
testFunc["imageData_sequentialWrite"] = function() {
vmessage("write imageData array " + conf.write_cnt + "x");
return test_sequentialWrite(new_imageData());
};
testFunc["arrayBuffer_sequentialWrite"] = function() {
vmessage("write arrayBuffer array " + conf.write_cnt + "x");
return test_sequentialWrite(new_arrayBuffer());
};
init = function() {
vmessage(">> init");
$D('iterations').value = 10;
$D('arraySize').value = 10;
arraySize = parseInt($D('arraySize').value, 10) * 1024;
message("Browser: " + browser);
/* Determine browser binary array support */
try {
ctx = $D('canvas').getContext('2d');
new_imageData = new_imageData_createImageData;
new_imageData();
do_imageData = true;
} catch (exc) {
vmessage("createImageData not supported: " + exc);
try {
ctx = $D('canvas').getContext('2d');
new_imageData = new_imageData_getImageData;
blah = new_imageData();
do_imageData = true;
} catch (exc) {
vmessage("getImageData not supported: " + exc);
}
}
if (! do_imageData) {
message("imageData arrays not supported");
}
try {
new_arrayBuffer();
do_arrayBuffer = true;
} catch (exc) {
vmessage("Typed Arrays not supported: " + exc);
}
if (! do_arrayBuffer) {
message("Typed Arrays (ArrayBuffers) not suppoted");
}
vmessage("<< init");
}
+91
View File
@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Native Base64 Tests</title>
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="../include/base64.js"></script>
</head>
<body>
<h1>Native Base64 Tests</h1>
<br>
Messages:<br>
<textarea id="debug" style="font-size: 9px;" cols=80 rows=25></textarea>
<br>
</body>
<script>
function debug(str) {
console.log(str);
cell = $D('debug');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function assertRun(code, result) {
try {
var actual = eval(code);
} catch (exc) {
debug("FAIL: '" + code + "' threw an exception");
fail += 1;
return false;
}
if (actual !== result) {
debug("FAIL: '" + code + "' returned '" + actual + "', expected '" + result + "'");
fail += 1;
return false;
}
debug("PASS: '" + code + "' returned expected '" + result +"'");
pass += 1;
return true;
}
function Base64_decode(data) {
var arr = Base64.decode (data);
return arr.map(function (num) {
return String.fromCharCode(num); } ).join('');
}
window.onload = function() {
var str;
debug('onload');
fail = 0;
pass = 0;
assertRun('window.btoa("hello world")', 'aGVsbG8gd29ybGQ=');
assertRun('window.btoa("a")', 'YQ==');
assertRun('window.btoa("ab")', 'YWI=');
assertRun('window.btoa("abc")', 'YWJj');
assertRun('window.btoa("abcd")', 'YWJjZA==');
assertRun('window.btoa("abcde")', 'YWJjZGU=');
assertRun('window.btoa("abcdef")', 'YWJjZGVm');
assertRun('window.btoa("abcdefg")', 'YWJjZGVmZw==');
assertRun('window.btoa("abcdefgh")', 'YWJjZGVmZ2g=');
assertRun('window.atob("aGVsbG8gd29ybGQ=")', 'hello world');
assertRun('Base64_decode("aGVsbG8gd29ybGQ=")', 'hello world');
assertRun('window.atob("YQ==")', 'a');
assertRun('Base64_decode("YQ==")', 'a');
assertRun('window.atob("YWI=")', 'ab');
assertRun('Base64_decode("YWI=")', 'ab');
assertRun('window.atob("YWJj")', 'abc');
assertRun('Base64_decode("YWJj")', 'abc');
assertRun('window.atob("YWJjZA==")', 'abcd');
assertRun('Base64_decode("YWJjZA==")', 'abcd');
assertRun('window.atob("YWJjZGU=")', 'abcde');
assertRun('Base64_decode("YWJjZGU=")', 'abcde');
assertRun('window.atob("YWJjZGVm")', 'abcdef');
assertRun('Base64_decode("YWJjZGVm")', 'abcdef');
assertRun('typeof window.btoa', 'function');
assertRun('window.btoa("")', '');
assertRun('window.btoa(null)', '');
assertRun('window.atob(window.btoa(window))', window.toString()); // "[object DOMWindow]"
assertRun('window.btoa("\\u0080\\u0081")', 'gIE=');
debug("Tests failed: " + fail);
debug("Tests passed: " + pass);
}
</script>
+12
View File
@@ -0,0 +1,12 @@
// The following results in 'hello [MANGLED]'
//
// Filed as https://github.com/ry/node/issues/issue/402
var sys = require("sys"),
buf = new Buffer(1024), len,
str1 = "aGVsbG8g", // 'hello '
str2 = "d29ybGQ=", // 'world'
len = buf.write(str1, 0, 'base64');
len += buf.write(str2, len, 'base64');
sys.log("decoded result: " + buf.toString('binary', 0, len));
+134
View File
@@ -0,0 +1,134 @@
/*
* From:
* http://www.quirksmode.org/js/detect.html
*/
var Browser = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.majorVersion = this.searchMajorVersion(navigator.userAgent)
|| this.searchMajorVersion(navigator.appVersion)
|| "an unknown version";
this.fullVersion = this.searchFullVersion(navigator.userAgent)
|| this.searchFullVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchFullVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return dataString.substring(index+this.versionSearchString.length+1);
},
searchVersion: function (dataString) {
return this.searchFullVersion(dataString).split(" ")[0];
},
searchMajorVersion: function (dataString) {
return parseFloat(this.searchFullVersion(dataString).split(".")[0]);
},
dataBrowser: [
{
string: navigator.userAgent,
subString: "Chrome",
identity: "Chrome"
},
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari",
versionSearch: "Version"
},
{
prop: window.opera,
identity: "Opera",
versionSearch: "Version"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.userAgent,
subString: "iPhone",
identity: "iPhone/iPod"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]
};
Browser.init();
+148
View File
@@ -0,0 +1,148 @@
<!DOCTYPE html>
<html>
<head>
<title>Canvas Performance Test</title>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="../include/base64.js"></script>
<script src="../include/display.js"></script>
<script src="face.png.js"></script>
</head>
<body>
Iterations: <input id='iterations' style='width:50' value="100">&nbsp;
Width: <input id='width' style='width:50' value="640">&nbsp;
Height: <input id='height' style='width:50' value="480">&nbsp;
<input id='startButton' type='button' value='Do Performance Test'
style='width:150px' onclick="begin();">&nbsp;
<br><br>
<b>Canvas</b> (should see three squares and two happy faces):<br>
<canvas id="canvas" width="200" height="100"
style="border-style: dotted; border-width: 1px;">
Canvas not supported.
</canvas>
<br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
var msg_cnt = 0;
var display, start_width = 300, start_height = 100;
var iterations;
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
msg_cnt += 1;
}
function test_functions () {
var img, x, y, w, h, ctx = display.get_context();
w = display.get_width();
h = display.get_height();
display.fillRect(0, 0, w, h, [240,240,240]);
display.blitStringImage("data:image/png;base64," + face64, 150, 10);
var himg = new Image();
himg.onload = function () {
ctx.drawImage(himg, 200, 40); };
himg.src = "face.png";
/* Test array image data */
data = [];
for (y=0; y< 50; y++) {
for (x=0; x< 50; x++) {
data[(y*50 + x)*4 + 0] = 255 - parseInt((255 / 50) * y, 10);
data[(y*50 + x)*4 + 1] = parseInt((255 / 50) * y, 10);
data[(y*50 + x)*4 + 2] = parseInt((255 / 50) * x, 10);
data[(y*50 + x)*4 + 3] = 255;
}
}
display.blitImage(30, 10, 50, 50, data, 0);
img = display.getTile(5,5,16,16,[0,128,128]);
display.putTile(img);
img = display.getTile(90,15,16,16,[0,0,0]);
display.setSubTile(img, 0,0,16,16,[128,128,0]);
display.putTile(img);
}
function begin () {
$D('startButton').value = "Running";
$D('startButton').disabled = true;
setTimeout(start_delayed, 250);
iterations = $D('iterations').value;
}
function start_delayed () {
var ret;
ret = display.set_prefer_js(true);
if (ret) {
message("Running test: prefer Javascript ops");
var time1 = run_test();
message("prefer Javascript ops: " + time1 + "ms total, " +
(time1 / iterations) + "ms per frame");
} else {
message("Could not run: prefer Javascript ops");
}
display.set_prefer_js(false);
message("Running test: prefer Canvas ops");
var time2 = run_test();
message("prefer Canvas ops: " + time2 + "ms total, " +
(time2 / iterations) + "ms per frame");
if (Util.get_logging() !== 'debug') {
display.resize(start_width, start_height, true);
test_functions();
}
$D('startButton').disabled = false;
$D('startButton').value = "Do Performance Test";
}
function run_test () {
var width, height;
width = $D('width').value;
height = $D('height').value;
display.resize(width, height);
var color, start_time = (new Date()).getTime(), w, h;
for (var i=0; i < iterations; i++) {
color = [128, 128, (255 / iterations) * i, 0];
for (var x=0; x < width; x = x + 16) {
for (var y=0; y < height; y = y + 16) {
w = Math.min(16, width - x);
h = Math.min(16, height - y);
var tile = display.getTile(x, y, w, h, color);
display.setSubTile(tile, 0, 0, w, h, color);
display.putTile(tile);
}
}
}
var end_time = (new Date()).getTime();
return (end_time - start_time);
}
window.onload = function() {
message("in onload");
$D('iterations').value = 10;
display = new Display({'target' : $D('canvas')});
display.resize(start_width, start_height, true);
message("Canvas initialized");
test_functions();
}
</script>
</html>
+135
View File
@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Cursor Change test</title>
<meta charset="UTF-8">
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="../include/base64.js"></script>
<script src="../include/canvas.js"></script>
</head>
<body>
<h1>Roll over the buttons to test cursors</h1>
<br>
<input id=button1 type="button" value="Cursor from file (smiley face)">
<input id=button2 type="button" value="Data URI cursor (crosshair)">
<br>
<br>
<br>
Debug:<br>
<textarea id="debug" style="font-size: 9px;" cols=80 rows=25></textarea>
<br>
<br>
<canvas id="testcanvas" width="100px" height="20px">
Canvas not supported.
</canvas>
</body>
<script>
function debug(str) {
console.log(str);
cell = $D('debug');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function makeCursor() {
var arr = [], x, y, w = 32, h = 32, hx = 16, hy = 16;
var IHDRsz = 40;
var ANDsz = w * h * 4;
var XORsz = Math.ceil( (w * h) / 8.0 );
// Push multi-byte little-endian values
arr.push16le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF );
};
arr.push32le = function (num) {
this.push((num ) & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF );
};
// Main header
arr.push16le(0); // Reserved
arr.push16le(2); // .CUR type
arr.push16le(1); // Number of images, 1 for non-animated arr
// Cursor #1
arr.push(w); // width
arr.push(h); // height
arr.push(0); // colors, 0 -> true-color
arr.push(0); // reserved
arr.push16le(hx); // hotspot x coordinate
arr.push16le(hy); // hotspot y coordinate
arr.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size
arr.push32le(22); // offset of cursor data in the file
// Infoheader for Cursor #1
arr.push32le(IHDRsz); // Infoheader size
arr.push32le(w); // Cursor width
arr.push32le(h*2); // XOR+AND height
arr.push16le(1); // number of planes
arr.push16le(32); // bits per pixel
arr.push32le(0); // type of compression
arr.push32le(XORsz + ANDsz); // Size of Image
arr.push32le(0);
arr.push32le(0);
arr.push32le(0);
arr.push32le(0);
// XOR/color data
for (y = h-1; y >= 0; y--) {
for (x = 0; x < w; x++) {
//if ((x === hx) || (y === (h-hy-1))) {
if ((x === hx) || (y === hy)) {
arr.push(0xe0); // blue
arr.push(0x00); // green
arr.push(0x00); // red
arr.push(0xff); // alpha
} else {
arr.push(0x05); // blue
arr.push(0xe6); // green
arr.push(0x00); // red
arr.push(0x80); // alpha
}
}
}
// AND/bitmask data (seems to be ignored)
for (y = 0; y < h; y++) {
for (x = 0; x < Math.ceil(w / 8); x++) {
arr.push(0x00);
}
}
debug("cursor generated");
return arr;
}
window.onload = function() {
debug("onload");
var canvas, cross, cursor, cursor64;
canvas = new Canvas({'target' : $D("testcanvas")});
debug("canvas indicates Data URI cursor support is: " + canvas.get_cursor_uri());
$D('button1').style.cursor="url(face.png), default";
cursor = makeCursor();
cursor64 = Base64.encode(cursor);
//debug("cursor: " + cursor.slice(0,100) + " (" + cursor.length + ")");
//debug("cursor64: " + cursor64.slice(0,100) + " (" + cursor64.length + ")");
$D('button2').style.cursor="url(data:image/x-icon;base64," + cursor64 + "), default";
debug("onload complete");
}
</script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
var face64 = 'iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAAAA3NCSVQICAjb4U/gAAAAGXRFWHRTb2Z0d2FyZQBnbm9tZS1zY3JlZW5zaG907wO/PgAACJJJREFUSIm1lltsXMUdxr8558zZq9d3OxebJDYhJLhNIAmUWyFKIBUtVaGqSgtUlIJKeahoEahgIZU+VC0oQiVVC60obckDgVIp3KRCQkmhhIhA4oY4wjg2ufmS9drec/bc5vbvw9prJwq85dP/YWfP7Pfb/8w3s8v6339l2fkrbMvGuZQ2mkUTA0bpc4qpyjrX3dTkAATQ5z0WUrqcAwjL/eXirmBqj0yKSTTBwNxMM0+15JuurG/dlClcOH/yWcVEaVBKUR3Eidizr2946Nhr/9q5b//BsudZzDLG5DK4sDt3443XrFm34bkX9x4ZPimkWNBa/+MfrB84+O7rbxz4+JPQD8liljY6n8t9uWfld2/++vp1F3ct6cikU2eSnvr7P7e99OqC9vaTJ0ccMtl8loyJ4igKwzAIK0GglersWv7sM08VCrk4joY/O/rLXz3mTYzmcnnXdZXWcRzHURwEQRCEHUuXdS/vnp4qP/CT2zdvuAKAQwCB4kRse+m1LY//Wojkscd/57opKUQUJ8wyzFaOq7OGGGPcdZ/f/sKbu3YT0YZrr3JT7pq1l3qeH4SBqgRETBljDKXSqXyh/i9PP/W/Q31btz59zVXrUpxb1dYsixUK+c7Fi59/YUdz2yInnbXcLHfTtpu23ZRlu4ZZiRBTp8Z37HjlhW1/evnFZ9/a+VZdLsecFOMpx83ydJanc24q67iuFOr48NC1G6+fKBY7zutIElFNBAC4nN99602XXLzutjvvETqAlcqktVQin0QiLsRxEAUBaRVUfBh1QfcigmzIuw0NTe2LOjNlL07iOArDwA88z0unGWNTk5P1dfkf3XH3BT2r9b23zZKIAHxr81f/uGpF/8G+Fau+VPbKp8ZHpqdKSRiEYWiMMVopJSuVyl+f3UpIQKL34btvvf2BxuZWN5Umo7TWFiNDDHCampob6utLpRKz7Hvv+E5jfR5ELCkNShFXOytOTH7vjrsOfXJ0wcLFF63sXr1mfXtbS6FQB4BZyGYzX7l0TWtrvWVpUGxUMFEa2bv3Q9+bNCaECX2/NFEc3bd/4r19/tR0uLC98c+/3/LVy9fWzhNq56m1pfEPvabnut2OI8EvBMAYAxhgAWz3u3tuvuWeRx/56aYNa0Hy3fc/euiRZx596IZvbF5Dpgw9CdMI0waqaMrEScPgvtdWXH5JzdzC7NElIPQH3GyTk+4ABCgCEpAkMgRGcLb/49WGxqYtTzwNaJDa/tJ7DU1tW558GaYCEwESYGAWwEidTOcWM8tElcGauTP/ivDGd7V3fxv6JGCBIpBDjIMxgIM5B/YfjMJwfGwEMIA40DcQhcn46DGAzX7p6gIwBhj5WUvH8vLYG+nu8+d6qimY2lPXup70GFEEE9baAhRIj5w8cfz4MSESkJw3FLOfnrvSCETqs3xTd2Vyd+1Na/4MmRRt3gBTgfGJKkQhTAQTwgQgv2tpR8X3Vq5YCiiC7lrSXPG9lRe0AmZ2hQxo5jXpspNqEElxPmlOIi5ZThYUgBKYKRgPxgMFMAGM/+D9P2xuLPQ+dBcoAYkHf/bN5sZM74M3gHS1acBUi0gZ4zk8J5NyzdzBGSIJkoANCqsrwgBAg+zN1605Mfw6IIkiUHL9xouODzwBE4ACkKrGBNBkBEgSKSIz39gxRkuRVAduulHLCZtZoARkzybTAFU2m7GjBBSDkmoRJYCc3U5lSBgjAFeJae4Wauan9WSnWlU0aqdtUAXElAicVDNIgfHZaJkZU0pAESgmCJAACUCApJIBKCITg+VVMuWm2+btEwFE1coVLvOKe2HVE8UwUd/OXi0nQZXZ8kH+7HIFoIgoqvKqzWkV9L2zy5jQ6Ig5nX5pOFd/Vc3cmv9zW9eyYfzITmY1giKiMJNtCiYPw1RgPBh/psiHqcAEZAJQBFMlxaDEnyqmc3mjY2NCiy+bHB3Kt2w8I+UzxTPLlAzjygCz6kFBx6qNg/ue84p9M7AZRoWoQhSAqumfacsrnRg6uH9Rd4/RFWafl1RGjLJ5ZknNnIXjh+PQB0BEQkqv9L4sb1t59cMU74GVKxcnhg5sdzN1jQtX5grtqVyj46ZtywIJrUOZeCKYCLxTU+PHkzhZ2vO1XH5MRIfcwvcHP9qRafp5XfN6l3PGGIA5ktJaJEJINXnkvmWrNza0rSBxEFYbnE6veGRq9IPQO54Ep5QItRYAs22Hu1k315QtdDYsuCzf1KHDt0XlbTu3ySuVRo6MNnc/6XLHTbmObc+QotAHIJUSQiSJTKLR4Nh9Pdc+kM44JA+D5RhfBud8ZjeD5WHVMVYHqwAYmGkyUyRPqPDfMnhTxcNW+jKpGj/94NX8eVtTmYWpFHddlzsOABaOzZGkkImQUsrI/1iVfrPq6vszuSyJD0EasGEVmN0KlgXLgYGMT6qkkwEthrQuG53Y2U0icT79YIfb2pup6+Gcp1zOXV4j9VdJxhghpJBSSCmEjL0+XXqsa+0tTYvWQ/aTHJrZW9JEkowwJjYmMjo0OmR8uZ1eNz12+Nih/zgtv0gXVrsur1Jcl1uWNUsK/GoQldZSSCGllEpIGYcndOm36Vyqa/VNmboFRh4ldZR02ZhpMhJwCGnmLGZ8SewXj/bvTkLDW3pT2UUu55w7Lufc5dVNAsCCsf4o8Gqpr8KkUlIqpZRUKim/Y/y/pVLZ1s5V+Zbl3C3Ybp5Iq2RKxhP+xFBxZFAmwi7cmaq/kjuO4zicO9xx5mPOQqrGvYZRWmulldYqGlLBf3X8EfQkSR8A43WMN1nuWid3hZPpcmzbdmzHtmuwarjnkw5FldNIczyljDZKa62NNpoM1QSA1WQx27Jt23Js27It7pzJmLthz/7/nzHOOThcImPoNBIIAMNpJMtiNcBZDZ3PfVIjgtkWsy3riyZ9AaFGMlozhuqCnDsxxv4PC7uS+QV5eeoAAAAASUVORK5CYII=';
+131
View File
@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html>
<head><title>Input Test</title></head>
<body>
<br><br>
Canvas:
<span id="button-selection" style="display: none;">
<input id="button1" type="button" value="L"><input id="button2" type="button" value="M"><input id="button4" type="button" value="R">
</span>
<br>
<canvas id="canvas" width="640" height="20"
style="border-style: dotted; border-width: 1px;">
Canvas not supported.
</canvas>
<br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="../include/base64.js"></script>
<script src="../include/keysymdef.js"></script>
<script src="../include/keyboard.js"></script>
<script src="../include/input.js"></script>
<script src="../include/display.js"></script>
<script>
var msg_cnt = 0, iterations,
width = 400, height = 200,
canvas, keyboard, mouse;
var newline = "\n";
if (Util.Engine.trident) {
var newline = "<br>\n";
}
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + newline;
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
function mouseButton(x, y, down, bmask) {
msg = 'mouse x,y: ' + x + ',' + y + ' down: ' + down;
msg += ' bmask: ' + bmask;
message(msg);
}
function mouseMove(x, y) {
msg = 'mouse x,y: ' + x + ',' + y;
//console.log(msg);
}
function rfbKeyPress(keysym, down) {
var d = down ? "down" : " up ";
var key = keysyms.lookup(keysym);
var msg = "RFB keypress " + d + " keysym: " + keysym;
if (key && key.keyname) {
msg += " key name: " + key.keyname;
}
message(msg);
}
function rawKey(e) {
msg = "raw key event " + e.type +
" (key: " + e.keyCode + ", char: " + e.charCode +
", which: " + e.which +")";
message(msg);
}
function selectButton(num) {
var b, blist = [1,2,4];
if (typeof num === 'undefined') {
// Show the default
num = mouse.get_touchButton();
} else if (num === mouse.get_touchButton()) {
// Set all buttons off (no clicks)
mouse.set_touchButton(0);
num = 0;
} else {
// Turn on one button
mouse.set_touchButton(num);
}
for (b = 0; b < blist.length; b++) {
if (blist[b] === num) {
$D('button' + blist[b]).style.backgroundColor = "black";
$D('button' + blist[b]).style.color = "lightgray";
} else {
$D('button' + blist[b]).style.backgroundColor = "";
$D('button' + blist[b]).style.color = "";
}
}
}
window.onload = function() {
canvas = new Display({'target' : $D('canvas')});
keyboard = new Keyboard({'target': document,
'onKeyPress': rfbKeyPress});
Util.addEvent(document, 'keypress', rawKey);
Util.addEvent(document, 'keydown', rawKey);
Util.addEvent(document, 'keyup', rawKey);
mouse = new Mouse({'target': $D('canvas'),
'onMouseButton': mouseButton,
'onMouseMove': mouseMove});
canvas.resize(width, height, true);
keyboard.grab();
mouse.grab();
message("Display initialized");
if ('ontouchstart' in document.documentElement) {
message("Touch device detected");
$D('button-selection').style.display = "inline";
$D('button1').onclick = function(){ selectButton(1) };
$D('button2').onclick = function(){ selectButton(2) };
$D('button4').onclick = function(){ selectButton(4) };
selectButton();
}
}
</script>
</html>
+29
View File
@@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link rel="stylesheet" href="node_modules/mocha/mocha.css" />
</head>
<body>
<!--
To run tests
cd .../noVNC/tests
npm install chai mocha
open keyboard-tests.html in a browser
-->
<div id="mocha"></div>
<script src="node_modules/chai/chai.js"></script>
<script src="node_modules/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../include/keysymdef.js"></script>
<script src="../include/keyboard.js"></script>
<script src="test.keyboard.js"></script>
<script src="test.helper.js"></script>
<script>
mocha.checkLeaks();
mocha.globals(['navigator']);
mocha.run();
</script>
</body>
</html>
+99
View File
@@ -0,0 +1,99 @@
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');
process.env.PHANTOMJS_EXECUTABLE = phantom_path;
var casper_opts = {
child: {
transport: 'http',
command: casper_path
},
casper: {
logLevel: 'debug',
verbose: true
}
};
var provide_emitter = function(file_paths) {
var spooky = new Spooky(casper_opts, function(err) {
if (err) {
if (err.stack) console.warn(err.stack);
else console.warn(err);
return;
}
spooky.start('about:blank');
file_paths.forEach(function(file_path, path_ind) {
spooky.thenOpen('file://'+file_path);
spooky.then([{ path_ind: path_ind }, function() {
var res_json = {
file_ind: path_ind
};
res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length; });
res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length; });
res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length; });
res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast):not(.pending)').length; });
res_json.num_skipped = this.evaluate(function () { return document.querySelectorAll('li.test.pending').length; });
res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent; });
res_json.suites = this.evaluate(function() {
var traverse_node = function(elem) {
var res;
if (elem.classList.contains('suite')) {
res = {
type: 'suite',
name: elem.querySelector('h1').textContent,
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0,
};
var child_elems = elem.querySelector('ul').children;
res.children = Array.prototype.map.call(child_elems, traverse_node);
return res;
}
else {
var h2_content = elem.querySelector('h2').childNodes;
res = {
type: 'test',
text: h2_content[0].textContent,
};
if (elem.classList.contains('pass')) {
res.pass = true;
if (elem.classList.contains('pending')) {
res.slow = false;
res.skipped = true;
}
else {
res.slow = !elem.classList.contains('fast');
res.skipped = false;
res.duration = h2_content[1].textContent;
}
}
else {
res.error = elem.querySelector('pre.error').textContent;
}
return res;
}
};
var top_suites = document.querySelectorAll('#mocha-report > li.suite');
return Array.prototype.map.call(top_suites, traverse_node);
});
res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent; });
this.emit('test_ready', res_json);
}]);
});
spooky.run();
});
return spooky;
};
module.exports = {
provide_emitter: provide_emitter,
name: 'SpookyJS (CapserJS on PhantomJS)'
};
+288
View File
@@ -0,0 +1,288 @@
#!/usr/bin/env node
var ansi = require('ansi');
var program = require('commander');
var path = require('path');
var fs = require('fs');
var make_list = function(val) {
return val.split(',');
};
program
.option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, [])
.option('-a, --print-all', 'Print all tests, not just the failures')
.option('--disable-color', 'Explicitly disable color')
.option('-c, --color', 'Explicitly enable color (default is to use color when not outputting to a pipe)')
.option('-i, --auto-inject <includefiles>', 'Treat the test list as a set of mocha JS files, and automatically generate HTML files with which to test test. \'includefiles\' should be a comma-separated list of paths to javascript files to include in each of the generated HTML files', make_list, null)
.option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper')
.option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with .js tests)')
.option('-o, --output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)')
.option('-d, --debug', 'Show debug output (the "console" event) from the provider')
.parse(process.argv);
if (program.tests.length === 0) {
program.tests = fs.readdirSync(__dirname).filter(function(f) { return (/^test\.(\w|\.|-)+\.js$/).test(f); });
console.log('using files %s', program.tests);
}
var file_paths = [];
var all_js = program.tests.reduce(function(a,e) { return a && e.slice(-3) == '.js'; }, true);
if (all_js && !program.autoInject) {
var all_modules = {};
// uses the first instance of the string 'requires local modules: '
program.tests.forEach(function (testname) {
var full_path = path.resolve(process.cwd(), testname);
var content = fs.readFileSync(full_path).toString();
var ind = content.indexOf('requires local modules: ');
if (ind > -1) {
ind += 'requires local modules: '.length;
var eol = content.indexOf('\n', ind);
var modules = content.slice(ind, eol).split(/,\s*/);
modules.forEach(function (mod) {
all_modules[path.resolve(__dirname, '../include/', mod)+'.js'] = 1;
});
}
});
program.autoInject = Object.keys(all_modules);
}
if (program.autoInject) {
var temp = require('temp');
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>",
script_tag: function(p) { return "<script src='" + p + "'></script>"; },
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run();\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<script>mocha.setup('bdd');</script>";
template.header = program.autoInject.reduce(function(acc, sn) {
return acc + "\n" + template.script_tag(path.resolve(process.cwd(), sn));
}, template.header);
file_paths = program.tests.map(function(jsn, ind) {
var templ = template.header;
templ += "\n";
templ += template.script_tag(path.resolve(process.cwd(), jsn));
templ += template.footer;
var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' });
fs.writeSync(tempfile.fd, templ);
fs.closeSync(tempfile.fd);
return tempfile.path;
});
}
else {
file_paths = program.tests.map(function(fn) {
return path.resolve(process.cwd(), fn);
});
}
var use_ansi = false;
if (program.color) use_ansi = true;
else if (program.disableColor) use_ansi = false;
else if (process.stdout.isTTY) use_ansi = true;
var cursor = ansi(process.stdout, { enabled: use_ansi });
if (program.outputHtml) {
file_paths.forEach(function(path, path_ind) {
fs.readFile(path, function(err, data) {
if (err) {
console.warn(error.stack);
return;
}
cursor
.bold()
.write(program.tests[path_ind])
.reset()
.write("\n")
.write(Array(program.tests[path_ind].length+1).join('='))
.write("\n\n")
.write(data)
.write("\n");
});
});
}
if (program.generateHtml) {
file_paths.forEach(function(path, path_ind) {
cursor
.bold()
.write(program.tests[path_ind])
.write(": ")
.reset()
.write(path)
.write("\n");
});
console.log('');
}
if (program.generateHtml) {
process.stdin.resume(); // pause until C-c
process.on('SIGINT', function() {
process.stdin.pause(); // exit
});
}
if (!program.outputHtml && !program.generateHtml) {
var failure_count = 0;
var prov = require(path.resolve(__dirname, 'run_from_console.'+program.provider+'.js'));
cursor
.write("Running tests ")
.bold()
.write(program.tests.join(', '))
.reset()
.grey()
.write(' using provider '+prov.name)
.reset()
.write("\n");
//console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name);
var provider = prov.provide_emitter(file_paths);
provider.on('test_ready', function(test_json) {
console.log('');
filename = program.tests[test_json.file_ind];
cursor.bold();
console.log('Results for %s:', filename);
console.log(Array('Results for :'.length+filename.length+1).join('='));
cursor.reset();
console.log('');
cursor
.write(''+test_json.num_tests+' tests run, ')
.green()
.write(''+test_json.num_passes+' passed');
if (test_json.num_slow > 0) {
cursor
.reset()
.write(' (');
cursor
.yellow()
.write(''+test_json.num_slow+' slow')
.reset()
.write(')');
}
cursor
.reset()
.write(', ');
cursor
.red()
.write(''+test_json.num_fails+' failed');
if (test_json.num_skipped > 0) {
cursor
.reset()
.write(', ')
.grey()
.write(''+test_json.num_skipped+' skipped');
}
cursor
.reset()
.write(' -- duration: '+test_json.duration+"s\n");
console.log('');
if (test_json.num_fails > 0 || program.printAll) {
var traverse_tree = function(indentation, node) {
if (node.type == 'suite') {
if (!node.has_subfailures && !program.printAll) return;
if (indentation === 0) {
cursor.bold();
console.log(node.name);
console.log(Array(node.name.length+1).join('-'));
cursor.reset();
}
else {
cursor
.write(Array(indentation+3).join('#'))
.bold()
.write(' '+node.name+' ')
.reset()
.write(Array(indentation+3).join('#'))
.write("\n");
}
console.log('');
for (var i = 0; i < node.children.length; i++) {
traverse_tree(indentation+1, node.children[i]);
}
}
else {
if (!node.pass) {
cursor.magenta();
console.log('- failed: '+node.text+test_json.replay);
cursor.red();
console.log(' '+node.error.split("\n")[0]); // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
cursor.reset();
console.log('');
}
else if (program.printAll) {
if (node.skipped) {
cursor
.grey()
.write('- skipped: '+node.text);
}
else {
if (node.slow) cursor.yellow();
else cursor.green();
cursor
.write('- pass: '+node.text)
.grey()
.write(' ('+node.duration+') ');
}
/*if (node.slow) cursor.yellow();
else cursor.green();*/
cursor
//.write(test_json.replay)
.reset()
.write("\n");
console.log('');
}
}
};
for (var i = 0; i < test_json.suites.length; i++) {
traverse_tree(0, test_json.suites[i]);
}
}
if (test_json.num_fails === 0) {
cursor.fg.green();
console.log('all tests passed :-)');
cursor.reset();
}
});
if (program.debug) {
provider.on('console', function(line) {
console.log(line);
});
}
/*gprom.finally(function(ph) {
ph.exit();
// exit with a status code that actually gives information
if (program.exitWithFailureCount) process.exit(failure_count);
});*/
}
+82
View File
@@ -0,0 +1,82 @@
var Browser = require('zombie');
var path = require('path');
var EventEmitter = require('events').EventEmitter;
var Q = require('q');
var provide_emitter = function(file_paths) {
var emitter = new EventEmitter();
file_paths.reduce(function(prom, file_path, path_ind) {
return prom.then(function(browser) {
browser.visit('file://'+file_path, function() {
if (browser.error) throw new Error(browser.errors);
var res_json = {};
res_json.file_ind = path_ind;
res_json.num_tests = browser.querySelectorAll('li.test').length;
res_json.num_fails = browser.querySelectorAll('li.test.fail').length;
res_json.num_passes = browser.querySelectorAll('li.test.pass').length;
res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length;
res_json.num_skipped = browser.querySelectorAll('li.test.pending').length;
res_json.duration = browser.text('li.duration em');
var traverse_node = function(elem) {
var classList = elem.className.split(' ');
var res;
if (classList.indexOf('suite') > -1) {
res = {
type: 'suite',
name: elem.querySelector('h1').textContent,
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0
};
var child_elems = elem.querySelector('ul').children;
res.children = Array.prototype.map.call(child_elems, traverse_node);
return res;
}
else {
var h2_content = elem.querySelector('h2').childNodes;
res = {
type: 'test',
text: h2_content[0].textContent
};
if (classList.indexOf('pass') > -1) {
res.pass = true;
if (classList.indexOf('pending') > -1) {
res.slow = false;
res.skipped = true;
}
else {
res.slow = classList.indexOf('fast') < 0;
res.skipped = false;
res.duration = h2_content[1].textContent;
}
}
else {
res.error = elem.querySelector('pre.error').textContent;
}
return res;
}
};
var top_suites = browser.querySelectorAll('#mocha-report > li.suite');
res_json.suites = Array.prototype.map.call(top_suites, traverse_node);
res_json.replay = browser.querySelector('a.replay').textContent;
emitter.emit('test_ready', res_json);
});
return new Browser();
});
}, Q(new Browser()));
return emitter;
};
module.exports = {
provide_emitter: provide_emitter,
name: 'ZombieJS'
};
+53
View File
@@ -0,0 +1,53 @@
/*
* Define some useful statistical functions on arrays of numbers
*/
Array.prototype.sum = function() {
var i, sum = 0;
for (i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
Array.prototype.max = function() {
return Math.max.apply(null, this);
}
Array.prototype.min = function() {
return Math.min.apply(null, this);
}
Array.prototype.mean = function() {
return this.sum() / this.length;
}
Array.prototype.average = Array.prototype.mean;
Array.prototype.median = function() {
var sorted = this.sort( function(a,b) { return a-b; }),
len = sorted.length;
if (len % 2) {
return sorted[Math.floor(len / 2)]; // Odd
} else {
return (sorted[len/2 - 1] + sorted[len/2]) / 2; // Even
}
}
Array.prototype.stdDev = function(sample) {
var i, sumSqr = 0, mean = this.mean(), N;
if (sample) {
// Population correction if this is a sample
N = this.length - 1;
} else {
// Standard deviation of just the array
N = this.length;
}
for (i = 0; i < this.length; i++) {
sumSqr += Math.pow(this[i] - mean, 2);
}
return Math.sqrt(sumSqr / N);
}
+260
View File
@@ -0,0 +1,260 @@
var assert = chai.assert;
var expect = chai.expect;
describe('Helpers', function() {
"use strict";
describe('keysymFromKeyCode', function() {
it('should map known keycodes to keysyms', function() {
expect(kbdUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
expect(kbdUtil.keysymFromKeyCode(0x41, true), 'A').to.be.equal(0x41);
expect(kbdUtil.keysymFromKeyCode(0xd, false), 'enter').to.be.equal(0xFF0D);
expect(kbdUtil.keysymFromKeyCode(0x11, false), 'ctrl').to.be.equal(0xFFE3);
expect(kbdUtil.keysymFromKeyCode(0x12, false), 'alt').to.be.equal(0xFFE9);
expect(kbdUtil.keysymFromKeyCode(0xe1, false), 'altgr').to.be.equal(0xFE03);
expect(kbdUtil.keysymFromKeyCode(0x1b, false), 'esc').to.be.equal(0xFF1B);
expect(kbdUtil.keysymFromKeyCode(0x26, false), 'up').to.be.equal(0xFF52);
});
it('should return null for unknown keycodes', function() {
expect(kbdUtil.keysymFromKeyCode(0xc0, false), 'DK æ').to.be.null;
expect(kbdUtil.keysymFromKeyCode(0xde, false), 'DK ø').to.be.null;
});
});
describe('keysyms.fromUnicode', function() {
it('should map ASCII characters to keysyms', function() {
expect(keysyms.fromUnicode('a'.charCodeAt())).to.have.property('keysym', 0x61);
expect(keysyms.fromUnicode('A'.charCodeAt())).to.have.property('keysym', 0x41);
});
it('should map Latin-1 characters to keysyms', function() {
expect(keysyms.fromUnicode('ø'.charCodeAt())).to.have.property('keysym', 0xf8);
expect(keysyms.fromUnicode('é'.charCodeAt())).to.have.property('keysym', 0xe9);
});
it('should map characters that are in Windows-1252 but not in Latin-1 to keysyms', function() {
expect(keysyms.fromUnicode('Š'.charCodeAt())).to.have.property('keysym', 0x01a9);
});
it('should map characters which aren\'t in Latin1 *or* Windows-1252 to keysyms', function() {
expect(keysyms.fromUnicode('ŵ'.charCodeAt())).to.have.property('keysym', 0x1000175);
});
it('should return undefined for unknown codepoints', function() {
expect(keysyms.fromUnicode('\n'.charCodeAt())).to.be.undefined;
expect(keysyms.fromUnicode('\u1F686'.charCodeAt())).to.be.undefined;
});
});
describe('substituteCodepoint', function() {
it('should replace characters which don\'t have a keysym', function() {
expect(kbdUtil.substituteCodepoint('Ș'.charCodeAt())).to.equal('Ş'.charCodeAt());
expect(kbdUtil.substituteCodepoint('ș'.charCodeAt())).to.equal('ş'.charCodeAt());
expect(kbdUtil.substituteCodepoint('Ț'.charCodeAt())).to.equal('Ţ'.charCodeAt());
expect(kbdUtil.substituteCodepoint('ț'.charCodeAt())).to.equal('ţ'.charCodeAt());
});
it('should pass other characters through unchanged', function() {
expect(kbdUtil.substituteCodepoint('T'.charCodeAt())).to.equal('T'.charCodeAt());
});
});
describe('nonCharacterKey', function() {
it('should recognize the right keys', function() {
expect(kbdUtil.nonCharacterKey({keyCode: 0xd}), 'enter').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x08}), 'backspace').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x09}), 'tab').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x10}), 'shift').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x11}), 'ctrl').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x12}), 'alt').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0xe0}), 'meta').to.be.defined;
});
it('should not recognize character keys', function() {
expect(kbdUtil.nonCharacterKey({keyCode: 'A'}), 'A').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: '1'}), '1').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: '.'}), '.').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: ' '}), 'space').to.be.null;
});
});
describe('getKeysym', function() {
it('should prefer char', function() {
expect(kbdUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x61);
});
it('should use charCode if no char', function() {
expect(kbdUtil.getKeysym({char : '', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(kbdUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(kbdUtil.getKeysym({char : 'hello', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
});
it('should use keyCode if no charCode', function() {
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: false})).to.have.property('keysym', 0x62);
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: true})).to.have.property('keysym', 0x42);
});
it('should use which if no keyCode', function() {
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: false})).to.have.property('keysym', 0x63);
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: true})).to.have.property('keysym', 0x43);
});
it('should substitute where applicable', function() {
expect(kbdUtil.getKeysym({char : 'Ș'})).to.have.property('keysym', 0x1aa);
});
});
describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state
describe('Toggle all modifiers', function() {
var sync = kbdUtil.ModifierSync();
it ('should do nothing if all modifiers are up as expected', function() {
expect(sync.keydown({
keyCode: 0x41,
ctrlKey: false,
altKey: false,
altGraphKey: false,
shiftKey: false,
metaKey: false})
).to.have.lengthOf(0);
});
it ('should synthesize events if all keys are unexpectedly down', function() {
var result = sync.keydown({
keyCode: 0x41,
ctrlKey: true,
altKey: true,
altGraphKey: true,
shiftKey: true,
metaKey: true
});
expect(result).to.have.lengthOf(5);
var keysyms = {};
for (var i = 0; i < result.length; ++i) {
keysyms[result[i].keysym] = (result[i].type == 'keydown');
}
expect(keysyms[0xffe3]);
expect(keysyms[0xffe9]);
expect(keysyms[0xfe03]);
expect(keysyms[0xffe1]);
expect(keysyms[0xffe7]);
});
it ('should do nothing if all modifiers are down as expected', function() {
expect(sync.keydown({
keyCode: 0x41,
ctrlKey: true,
altKey: true,
altGraphKey: true,
shiftKey: true,
metaKey: true
})).to.have.lengthOf(0);
});
});
describe('Toggle Ctrl', function() {
var sync = kbdUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
ctrlKey: true,
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keydown'}]);
});
it('should sync if modifier is suddenly up', function() {
expect(sync.keydown({
keyCode: 0x41,
ctrlKey: false
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keyup'}]);
});
});
describe('Toggle Alt', function() {
var sync = kbdUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
altKey: true,
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
});
it('should sync if modifier is suddenly up', function() {
expect(sync.keydown({
keyCode: 0x41,
altKey: false
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keyup'}]);
});
});
describe('Toggle AltGr', function() {
var sync = kbdUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
altGraphKey: true,
})).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keydown'}]);
});
it('should sync if modifier is suddenly up', function() {
expect(sync.keydown({
keyCode: 0x41,
altGraphKey: false
})).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keyup'}]);
});
});
describe('Toggle Shift', function() {
var sync = kbdUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
shiftKey: true,
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keydown'}]);
});
it('should sync if modifier is suddenly up', function() {
expect(sync.keydown({
keyCode: 0x41,
shiftKey: false
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keyup'}]);
});
});
describe('Toggle Meta', function() {
var sync = kbdUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
metaKey: true,
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keydown'}]);
});
it('should sync if modifier is suddenly up', function() {
expect(sync.keydown({
keyCode: 0x41,
metaKey: false
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keyup'}]);
});
});
describe('Modifier keyevents', function() {
it('should not sync a modifier on its own events', function() {
expect(kbdUtil.ModifierSync().keydown({
keyCode: 0x11,
ctrlKey: false
})).to.be.deep.equal([]);
expect(kbdUtil.ModifierSync().keydown({
keyCode: 0x11,
ctrlKey: true
}), 'B').to.be.deep.equal([]);
})
it('should update state on modifier keyevents', function() {
var sync = kbdUtil.ModifierSync();
sync.keydown({
keyCode: 0x11,
});
expect(sync.keydown({
keyCode: 0x41,
ctrlKey: true,
})).to.be.deep.equal([]);
});
it('should sync other modifiers on ctrl events', function() {
expect(kbdUtil.ModifierSync().keydown({
keyCode: 0x11,
altKey: true
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
})
});
describe('sync modifiers on non-key events', function() {
it('should generate sync events when receiving non-keyboard events', function() {
expect(kbdUtil.ModifierSync().syncAny({
altKey: true
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
});
});
describe('do not treat shift as a modifier key', function() {
it('should not treat shift as a shortcut modifier', function() {
expect(kbdUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false;
});
it('should not treat shift as a char modifier', function() {
expect(kbdUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
});
});
});
});
+841
View File
@@ -0,0 +1,841 @@
var assert = chai.assert;
var expect = chai.expect;
describe('Key Event Pipeline Stages', function() {
"use strict";
describe('Decode Keyboard Events', function() {
it('should pass events to the next stage', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.an.object;
done();
}).keydown({keyCode: 0x41});
});
it('should pass the right keysym through', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt.keysym).to.be.deep.equal(keysyms.lookup(0x61));
done();
}).keypress({keyCode: 0x41});
});
it('should pass the right keyid through', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.have.property('keyId', 0x41);
done();
}).keydown({keyCode: 0x41});
});
it('should not sync modifiers on a keypress', function() {
// Firefox provides unreliable modifier state on keypress events
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
++count;
}).keypress({keyCode: 0x41, ctrlKey: true});
expect(count).to.be.equal(1);
});
it('should sync modifiers if necessary', function(done) {
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
switch (count) {
case 0: // fake a ctrl keydown
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xffe3), type: 'keydown'});
++count;
break;
case 1:
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown', keysym: keysyms.lookup(0x61)});
done();
break;
}
}).keydown({keyCode: 0x41, ctrlKey: true});
});
it('should forward keydown events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
done();
}).keydown({keyCode: 0x41})
});
it('should forward keyup events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
done();
}).keyup({keyCode: 0x41});
});
it('should forward keypress events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
done();
}).keypress({keyCode: 0x41});
});
it('should generate stalls if a char modifier is down while a key is pressed', function(done) {
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
switch (count) {
case 0: // fake altgr
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xfe03), type: 'keydown'});
++count;
break;
case 1: // stall before processing the 'a' keydown
expect(evt).to.be.deep.equal({type: 'stall'});
++count;
break;
case 2: // 'a'
expect(evt).to.be.deep.equal({
type: 'keydown',
keyId: 0x41,
keysym: keysyms.lookup(0x61)
});
done();
break;
}
}).keydown({keyCode: 0x41, altGraphKey: true});
});
describe('suppress the right events at the right time', function() {
it('should suppress anything while a shortcut modifier is down', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
obj.keydown({keyCode: 0x11}); // press ctrl
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.true;
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true;
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.true;
expect(obj.keydown({keyCode: 0x3c})).to.be.true; // < key on DK Windows
expect(obj.keydown({keyCode: 0xde})).to.be.true; // Ø key on DK
});
it('should suppress non-character keys', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true;
expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
});
it('should not suppress shift', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
});
it('should generate event for shift keydown', function() {
var called = false;
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.have.property('keysym');
called = true;
}).keydown({keyCode: 0x10});
expect(called).to.be.true;
});
it('should not suppress character keys', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
});
it('should not suppress if a char modifier is down', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {});
obj.keydown({keyCode: 0xe1}); // press altgr
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
});
});
describe('Keypress and keyup events', function() {
it('should always suppress event propagation', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
expect(obj.keypress({keyCode: 'A'.charCodeAt()})).to.be.true;
expect(obj.keypress({keyCode: 0x3c})).to.be.true; // < key on DK Windows
expect(obj.keypress({keyCode: 0x11})).to.be.true;
expect(obj.keyup({keyCode: 'A'.charCodeAt()})).to.be.true;
expect(obj.keyup({keyCode: 0x3c})).to.be.true; // < key on DK Windows
expect(obj.keyup({keyCode: 0x11})).to.be.true;
});
it('should never generate stalls', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt.type).to.not.be.equal('stall');
});
obj.keypress({keyCode: 'A'.charCodeAt()});
obj.keypress({keyCode: 0x3c});
obj.keypress({keyCode: 0x11});
obj.keyup({keyCode: 'A'.charCodeAt()});
obj.keyup({keyCode: 0x3c});
obj.keyup({keyCode: 0x11});
});
});
describe('mark events if a char modifier is down', function() {
it('should not mark modifiers on a keydown event', function() {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 0: //altgr
break;
case 1: // 'a'
expect(evt).to.not.have.property('escape');
break;
}
});
obj.keydown({keyCode: 0xe1}); // press altgr
obj.keydown({keyCode: 'A'.charCodeAt()});
});
it('should indicate on events if a single-key char modifier is down', function(done) {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 0: //altgr
break;
case 1: // 'a'
expect(evt).to.be.deep.equal({
type: 'keypress',
keyId: 'A'.charCodeAt(),
keysym: keysyms.lookup('a'.charCodeAt()),
escape: [0xfe03]
});
done();
return;
}
});
obj.keydown({keyCode: 0xe1}); // press altgr
obj.keypress({keyCode: 'A'.charCodeAt()});
});
it('should indicate on events if a multi-key char modifier is down', function(done) {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) {
switch (times_called++) {
case 0: //ctrl
break;
case 1: //alt
break;
case 2: // 'a'
expect(evt).to.be.deep.equal({
type: 'keypress',
keyId: 'A'.charCodeAt(),
keysym: keysyms.lookup('a'.charCodeAt()),
escape: [0xffe9, 0xffe3]
});
done();
return;
}
});
obj.keydown({keyCode: 0x11}); // press ctrl
obj.keydown({keyCode: 0x12}); // press alt
obj.keypress({keyCode: 'A'.charCodeAt()});
});
it('should not consider a char modifier to be down on the modifier key itself', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
expect(evt).to.not.have.property('escape');
});
obj.keydown({keyCode: 0xe1}); // press altgr
});
});
describe('add/remove keysym', function() {
it('should remove keysym from keydown if a char key and no modifier', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
}).keydown({keyCode: 0x41});
});
it('should not remove keysym from keydown if a shortcut modifier is down', function() {
var times_called = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
switch (times_called++) {
case 1:
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
break;
}
}).keydown({keyCode: 0x41, ctrlKey: true});
expect(times_called).to.be.equal(2);
});
it('should not remove keysym from keydown if a char modifier is down', function() {
var times_called = 0;
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 2:
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
break;
}
}).keydown({keyCode: 0x41, altGraphKey: true});
expect(times_called).to.be.equal(3);
});
it('should not remove keysym from keydown if key is noncharacter', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: keysyms.lookup(0xff09), type: 'keydown'});
}).keydown({keyCode: 0x09});
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: keysyms.lookup(0xffe3), type: 'keydown'});
}).keydown({keyCode: 0x11});
});
it('should never remove keysym from keypress', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
}).keypress({keyCode: 0x41});
});
it('should never remove keysym from keyup', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
}).keyup({keyCode: 0x41});
});
});
// on keypress, keyup(?), always set keysym
// on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down
});
describe('Verify that char modifiers are active', function() {
it('should pass keydown events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should pass keyup events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should pass keypress events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should not pass stall events through', function(done){
var obj = VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
});
obj({type: 'stall'});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should merge keydown and keypress events if they come after a stall', function(done) {
var next_called = false;
var obj = VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(next_called).to.be.false;
next_called = true;
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44)});
done();
});
obj({type: 'stall'});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
expect(next_called).to.be.false;
});
it('should preserve modifier attribute when merging if keysyms differ', function(done) {
var next_called = false;
var obj = VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(next_called).to.be.false;
next_called = true;
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44), escape: [0xffe3]});
done();
});
obj({type: 'stall'});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44), escape: [0xffe3]});
expect(next_called).to.be.false;
});
it('should not preserve modifier attribute when merging if keysyms are the same', function() {
var obj = VerifyCharModifier(function(evt){
expect(evt).to.not.have.property('escape');
});
obj({type: 'stall'});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x42), escape: [0xffe3]});
});
it('should not merge keydown and keypress events if there is no stall', function(done) {
var times_called = 0;
var obj = VerifyCharModifier(function(evt){
switch(times_called) {
case 0:
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
break;
case 1:
expect(evt).to.deep.equal({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
done();
break;
}
++times_called;
});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
});
it('should not merge keydown and keypress events if separated by another event', function(done) {
var times_called = 0;
var obj = VerifyCharModifier(function(evt){
switch(times_called) {
case 0:
expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
break;
case 1:
expect(evt,2).to.deep.equal({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)});
break;
case 2:
expect(evt,3).to.deep.equal({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)});
done();
break;
}
++times_called;
});
obj({type: 'stall'});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
obj({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)});
obj({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)});
});
});
describe('Track Key State', function() {
it('should do nothing on keyup events if no keys are down', function() {
var obj = TrackKeyState(function(evt) {
expect(true).to.be.false;
});
obj({type: 'keyup', keyId: 0x41});
});
it('should insert into the queue on keydown if no keys are down', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
}
elem = null;
});
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0x41};
obj(elem);
expect(elem).to.be.null;
expect(times_called).to.be.equal(2);
});
it('should insert into the queue on keypress if no keys are down', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
}
elem = null;
});
expect(elem).to.be.null;
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0x41};
obj(elem);
expect(elem).to.be.null;
expect(times_called).to.be.equal(2);
});
it('should add keysym to last key entry if keyId matches', function() {
// this implies that a single keyup will release both keysyms
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)};
keysymsdown[keysyms.lookup(0x43).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0x41};
obj(elem);
expect(times_called).to.be.equal(4);
});
it('should create new key entry if keyId matches and keysym does not', function() {
// this implies that a single keyup will release both keysyms
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)};
keysymsdown[keysyms.lookup(0x43).keysym] = true;
obj(elem);
expect(times_called).to.be.equal(2);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0};
obj(elem);
expect(times_called).to.be.equal(3);
elem = {type: 'keyup', keyId: 0};
obj(elem);
expect(times_called).to.be.equal(4);
});
it('should merge key entry if keyIds are zero and keysyms match', function() {
// this implies that a single keyup will release both keysyms
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(times_called).to.be.equal(2);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0};
obj(elem);
expect(times_called).to.be.equal(3);
});
it('should add keysym as separate entry if keyId does not match last event', function() {
// this implies that separate keyups are required
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keypress', keyId: 0x42, keysym: keysyms.lookup(0x43)};
keysymsdown[keysyms.lookup(0x43).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0x41};
obj(elem);
expect(times_called).to.be.equal(4);
elem = {type: 'keyup', keyId: 0x42};
obj(elem);
expect(times_called).to.be.equal(4);
});
it('should add keysym as separate entry if keyId does not match last event and first is zero', function() {
// this implies that separate keyups are required
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x43)};
keysymsdown[keysyms.lookup(0x43).keysym] = true;
obj(elem);
expect(elem).to.be.null;
expect(times_called).to.be.equal(2);
elem = {type: 'keyup', keyId: 0};
obj(elem);
expect(times_called).to.be.equal(3);
elem = {type: 'keyup', keyId: 0x42};
obj(elem);
expect(times_called).to.be.equal(4);
});
it('should add keysym as separate entry if keyId does not match last event and second is zero', function() {
// this implies that a separate keyups are required
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
delete keysymsdown[evt.keysym.keysym];
}
else {
expect(evt).to.be.deep.equal(elem);
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
elem = null;
}
});
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)};
keysymsdown[keysyms.lookup(0x42).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)};
keysymsdown[keysyms.lookup(0x43).keysym] = true;
obj(elem);
expect(elem).to.be.null;
elem = {type: 'keyup', keyId: 0x41};
obj(elem);
expect(times_called).to.be.equal(3);
elem = {type: 'keyup', keyId: 0};
obj(elem);
expect(times_called).to.be.equal(4);
});
it('should pop matching key event on keyup', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
case 2:
expect(evt.type).to.be.equal('keydown');
break;
case 3:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x42, keysym: keysyms.lookup(0x62)});
break;
}
});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)});
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)});
obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)});
obj({type: 'keyup', keyId: 0x42});
expect(times_called).to.equal(4);
});
it('should pop the first zero keyevent on keyup with zero keyId', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
case 2:
expect(evt.type).to.be.equal('keydown');
break;
case 3:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x61)});
break;
}
});
obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x61)});
obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x62)});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x63)});
obj({type: 'keyup', keyId: 0x0});
expect(times_called).to.equal(4);
});
it('should pop the last keyevents keysym if no match is found for keyId', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
case 2:
expect(evt.type).to.be.equal('keydown');
break;
case 3:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x44, keysym: keysyms.lookup(0x63)});
break;
}
});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)});
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)});
obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)});
obj({type: 'keyup', keyId: 0x44});
expect(times_called).to.equal(4);
});
describe('Firefox sends keypress even when keydown is suppressed', function() {
it('should discard the keypress', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
expect(times_called).to.be.equal(1);
obj({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)});
});
});
describe('releaseAll', function() {
it('should do nothing if no keys have been pressed', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
++times_called;
});
obj({type: 'releaseall'});
expect(times_called).to.be.equal(0);
});
it('should release the keys that have been pressed', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
switch (times_called++) {
case 2:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x41)});
break;
case 3:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x42)});
break;
}
});
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x42)});
expect(times_called).to.be.equal(2);
obj({type: 'releaseall'});
expect(times_called).to.be.equal(4);
obj({type: 'releaseall'});
expect(times_called).to.be.equal(4);
});
});
});
describe('Escape Modifiers', function() {
describe('Keydown', function() {
it('should pass through when a char modifier is not down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
expect(times_called).to.be.equal(1);
});
it('should generate fake undo/redo events when a char modifier is down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
switch(times_called++) {
case 0:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe9)});
break;
case 1:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe3)});
break;
case 2:
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]});
break;
case 3:
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe9)});
break;
case 4:
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe3)});
break;
}
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]});
expect(times_called).to.be.equal(5);
});
});
describe('Keyup', function() {
it('should pass through when a char modifier is down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]});
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]});
expect(times_called).to.be.equal(1);
});
it('should pass through when a char modifier is not down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)});
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)});
expect(times_called).to.be.equal(1);
});
});
});
});
+43
View File
@@ -0,0 +1,43 @@
html,body {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
.flex-layout {
width: 100%;
height: 100%;
display: box;
display: -webkit-box;
display: -moz-box;
display: -ms-box;
box-orient: vertical;
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
-ms-box-orient: vertical;
box-align: stretch;
-webkit-box-align: stretch;
-moz-box-align: stretch;
-ms-box-align: stretch;
}
.flex-box {
box-flex: 1;
-webkit-box-flex: 1;
-moz-box-flex: 1;
-ms-box-flex: 1;
}
.container {
margin: 0px;
padding: 0px;
}
.canvas {
position: absolute;
border-style: dotted;
border-width: 1px;
}
+204
View File
@@ -0,0 +1,204 @@
<!DOCTYPE html>
<html>
<head><title>Viewport Test</title>
<link rel="stylesheet" href="viewport.css">
<!--
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
-->
<meta name=viewport content="width=device-width, initial-scale=1.0, user-scalable=no">
</head>
<body>
<div class="flex-layout">
<div>
Canvas:
<input id="move-selector" type="button" value="Move"
onclick="toggleMove();">
<br>
</div>
<div class="container flex-box">
<canvas id="canvas" class="canvas">
Canvas not supported.
</canvas>
<br>
</div>
<div>
<br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea>
</div>
</div>
</body>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script src="../include/base64.js"></script>
<script src="../include/keysymdef.js"></script>
<script src="../include/keyboard.js"></script>
<script src="../include/input.js"></script>
<script src="../include/display.js"></script>
<script>
var msg_cnt = 0, iterations,
penDown = false, doMove = false,
inMove = false, lastPos = {},
padW = 0, padH = 0,
display, ctx, keyboard, mouse;
var newline = "\n";
if (Util.Engine.trident) {
var newline = "<br>\n";
}
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + newline;
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
function mouseButton(x, y, down, bmask) {
//msg = 'mouse x,y: ' + x + ',' + y + ' down: ' + down;
//msg += ' bmask: ' + bmask;
//message(msg);
if (doMove) {
if (down && !inMove) {
inMove = true;
lastPos = {'x': x, 'y': y};
} else if (!down && inMove) {
inMove = false;
//dirtyRedraw();
}
return;
}
if (down && ! penDown) {
penDown = true;
ctx.beginPath();
ctx.moveTo(x, y);
} else if (!down && penDown) {
penDown = false;
ctx.closePath();
}
}
function mouseMove(x, y) {
var deltaX, deltaY;
if (inMove) {
//deltaX = x - lastPos.x; // drag viewport
deltaX = lastPos.x - x; // drag frame buffer
//deltaY = y - lastPos.y; // drag viewport
deltaY = lastPos.y - y; // drag frame buffer
lastPos = {'x': x, 'y': y};
display.viewportChange(deltaX, deltaY);
return;
}
if (penDown) {
ctx.lineTo(x, y);
ctx.stroke();
}
}
function dirtyRedraw() {
if (inMove) {
// Wait for user to stop moving viewport
return;
}
var d = display.getCleanDirtyReset();
for (i = 0; i < d.dirtyBoxes.length; i++) {
//showBox(d.dirtyBoxes[i], "dirty[" + i + "]: ");
drawArea(d.dirtyBoxes[i]);
}
}
function drawArea(b) {
var data = [], pixel, x, y;
//message("draw "+b.x+","+b.y+" ("+b.w+","+b.h+")");
for (var i = 0; i < b.w; i++) {
x = b.x + i;
for (var j = 0; j < b.h; j++) {
y = b.y + j;
pixel = (j * b.w * 4 + i * 4);
data[pixel + 0] = ((x * y) / 13) % 256;
data[pixel + 1] = ((x * y) + 392) % 256;
data[pixel + 2] = ((x + y) + 256) % 256;
data[pixel + 3] = 255;
}
}
//message("i: " + i + ", j: " + j + ", pixel: " + pixel);
display.blitImage(b.x, b.y, b.w, b.h, data, 0);
}
function toggleMove() {
if (doMove) {
doMove = false;
$D('move-selector').style.backgroundColor = "";
$D('move-selector').style.color = "";
} else {
doMove = true;
$D('move-selector').style.backgroundColor = "black";
$D('move-selector').style.color = "lightgray";
}
}
function detectPad() {
var c = $D('canvas'), p = c.parentNode;
c.width = 10;
c.height = 10;
padW = c.offsetWidth - 10;
padH = c.offsetHeight - 10;
message("padW: " + padW + ", padH: " + padH);
}
function doResize() {
var p = $D('canvas').parentNode;
message("doResize1: [" + (p.offsetWidth - padW) +
"," + (p.offsetHeight - padH) + "]");
display.viewportChange(0, 0,
p.offsetWidth - padW, p.offsetHeight - padH);
/*
var pos, new_w, new_h;pos
pos = Util.getPosition($D('canvas').parentNode);
new_w = window.innerWidth - pos.x;
new_h = window.innerHeight - pos.y;
display.viewportChange(0, 0, new_w, new_h);
*/
}
window.onload = function() {
detectPad();
display = new Display({'target': $D('canvas')});
display.resize(1600, 1024);
display.set_viewport(true);
ctx = display.get_context();
mouse = new Mouse({'target': $D('canvas'),
'onMouseButton': mouseButton,
'onMouseMove': mouseMove});
mouse.grab();
Util.addEvent(window, 'resize', doResize);
// Shrink viewport for first resize call so that the
// scrollbars are disabled
display.viewportChange(0, 0, 10, 10);
setTimeout(doResize, 1);
setInterval(dirtyRedraw, 50);
message("Display initialized");
};
</script>
</html>
+209
View File
@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html>
<head>
<title>VNC Performance Benchmark</title>
</head>
<body>
Passes: <input id='passes' style='width:50' value=3>&nbsp;
<input id='startButton' type='button' value='Start' style='width:100px'
onclick="do_test();" disabled>&nbsp;
<br><br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=15></textarea>
<br><br>
<div id="VNC_screen">
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
<table border=0 width=100%><tr>
<td><div id="VNC_status">Loading</div></td>
</tr></table>
</div>
<canvas id="VNC_canvas" width="640px" height="20px">
Canvas not supported.
</canvas>
</div>
</body>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script type="text/javascript">
var INCLUDE_URI= "../include/";
// TODO: Data file should override
var VNC_frame_encoding = "base64";
</script>
<script src="../include/util.js"></script>
<script src="../include/playback.js"></script>
<script src="../data/multi.js"></script>
<script>
// Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js"]);
var start_time, VNC_frame_data, pass, passes, encIdx,
encOrder = ['raw', 'rre', 'hextile', 'tightpng', 'copyrect'],
encTot = {}, encMin = {}, encMax = {},
passCur, passTot, passMin, passMax;
function msg(str) {
console.log(str);
var cell = $D('messages');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function dbgmsg(str) {
if (Util.get_logging() === 'debug') {
msg(str);
}
}
updateState = function (rfb, state, oldstate, mesg) {
switch (state) {
case 'failed':
case 'fatal':
msg("noVNC sent '" + state +
"' state during pass " + pass +
", iteration " + iteration +
" frame " + frame_idx);
test_state = 'failed';
break;
case 'loaded':
$D('startButton').disabled = false;
break;
}
if (typeof mesg !== 'undefined') {
$D('VNC_status').innerHTML = mesg;
}
}
function do_test() {
$D('startButton').value = "Running";
$D('startButton').disabled = true;
mode = 'perftest'; // full-speed
passes = $D('passes').value;
pass = 1;
encIdx = 0;
// Render each encoding once for each pass
iterations = 1;
// Initialize stats counters
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
encTot[i] = 0;
encMin[i] = 2<<23; // Something sufficiently large
encMax[i] = 0;
}
passCur = 0;
passTot = 0;
passMin = 2<<23;
passMax = 0;
// Fire away
next_encoding();
}
function next_encoding() {
var encName;
if (encIdx >= encOrder.length) {
// Accumulate pass stats
if (passCur < passMin) {
passMin = passCur;
}
if (passCur > passMax) {
passMax = passCur;
}
msg("Pass " + pass + " took " + passCur + " ms");
passCur = 0;
encIdx = 0;
pass += 1;
if (pass > passes) {
// We are finished
// Shut-off event interception
rfb.get_mouse().ungrab();
rfb.get_keyboard().ungrab();
$D('startButton').disabled = false;
$D('startButton').value = "Start";
finish_passes();
return; // We are finished, terminate
}
}
encName = encOrder[encIdx];
dbgmsg("Rendering pass " + pass + " encoding '" + encName + "'");
VNC_frame_data = VNC_frame_data_multi[encName];
iteration = 0;
start_time = (new Date()).getTime();
next_iteration();
}
// Finished rendering current encoding
function finish() {
var total_time, end_time = (new Date()).getTime();
total_time = end_time - start_time;
dbgmsg("Encoding " + encOrder[encIdx] + " took " + total_time + "ms");
passCur += total_time;
passTot += total_time;
// Accumulate stats
encTot[encIdx] += total_time;
if (total_time < encMin[encIdx]) {
encMin[encIdx] = total_time;
}
if (total_time > encMax[encIdx]) {
encMax[encIdx] = total_time;
}
encIdx += 1;
next_encoding();
}
function finish_passes() {
var i, enc, avg, passAvg;
msg("STATS (for " + passes + " passes)");
// Encoding stats
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
avg = (encTot[i] / passes).toFixed(1);
msg(" " + enc + ": " + encTot[i] + " ms, " +
encMin[i] + "/" + avg + "/" + encMax[i] +
" (min/avg/max)");
}
// Print pass stats
passAvg = (passTot / passes).toFixed(1);
msg("\n All passes: " + passTot + " ms, " +
passMin + "/" + passAvg + "/" + passMax +
" (min/avg/max)");
}
window.onscriptsload = function() {
var i, enc;
dbgmsg("Frame lengths:");
for (i = 0; i < encOrder.length; i++) {
enc = encOrder[i];
dbgmsg(" " + enc + ": " + VNC_frame_data_multi[enc].length);
}
rfb = new RFB({'target': $D('VNC_canvas'),
'updateState': updateState});
rfb.testMode(send_array, VNC_frame_encoding);
}
</script>
</html>
+138
View File
@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html>
<head>
<title>VNC Playback</title>
</head>
<body>
Iterations: <input id='iterations' style='width:50'>&nbsp;
Perftest:<input type='radio' id='mode1' name='mode' checked>&nbsp;
Realtime:<input type='radio' id='mode2' name='mode'>&nbsp;&nbsp;
<input id='startButton' type='button' value='Start' style='width:100px'
onclick="start();" disabled>&nbsp;
<br><br>
Results:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
<br><br>
<div id="VNC_screen">
<div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">
<table border=0 width=100%><tr>
<td><div id="VNC_status">Loading</div></td>
</tr></table>
</div>
<canvas id="VNC_canvas" width="640px" height="20px">
Canvas not supported.
</canvas>
</div>
</body>
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script type="text/javascript">
var INCLUDE_URI= "../include/";
// TODO: Data file should override
var VNC_frame_encoding = "base64";
</script>
<script src="../include/util.js"></script>
<script src="../include/webutil.js"></script>
<script>
var fname, start_time;
function message(str) {
console.log(str);
var cell = $D('messages');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
fname = WebUtil.getQueryVar('data', null);
if (fname) {
message("Loading " + fname);
// Load supporting scripts
Util.load_scripts(["base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js", "playback.js", fname]);
} else {
message("Must specify data=FOO in query string.");
}
updateState = function (rfb, state, oldstate, msg) {
switch (state) {
case 'failed':
case 'fatal':
message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx);
test_state = 'failed';
break;
case 'loaded':
$D('startButton').disabled = false;
break;
}
if (typeof msg !== 'undefined') {
$D('VNC_status').innerHTML = msg;
}
}
function start() {
$D('startButton').value = "Running";
$D('startButton').disabled = true;
iterations = $D('iterations').value;
iteration = 0;
start_time = (new Date()).getTime();
if ($D('mode1').checked) {
message("Starting performance playback (fullspeed) [" + iterations + " iteration(s)]");
mode = 'perftest';
} else {
message("Starting realtime playback [" + iterations + " iteration(s)]");
mode = 'realtime';
}
recv_message = rfb.testMode(send_array, VNC_frame_encoding);
next_iteration();
}
function finish() {
// Finished with all iterations
var total_time, end_time = (new Date()).getTime();
total_time = end_time - start_time;
iter_time = parseInt(total_time / iterations, 10);
message(iterations + " iterations took " + total_time + "ms, " +
iter_time + "ms per iteration");
// Shut-off event interception
rfb.get_mouse().ungrab();
rfb.get_keyboard().ungrab();
$D('startButton').disabled = false;
$D('startButton').value = "Start";
}
window.onscriptsload = function () {
iterations = WebUtil.getQueryVar('iterations', 3);
$D('iterations').value = iterations;
mode = WebUtil.getQueryVar('mode', 3);
if (mode === 'realtime') {
$D('mode2').checked = true;
} else {
$D('mode1').checked = true;
}
if (fname) {
message("VNC_frame_data.length: " + VNC_frame_data.length);
rfb = new RFB({'target': $D('VNC_canvas'),
'updateState': updateState});
}
}
</script>
</html>