9e01c5701e
This is WebKit bug https://bugs.webkit.org/show_bug.cgi?id=46319 The workaround is to wrap Canvas render functions with a function that sets a flush timer. The flush function sets the right margin and then 1ms later sets it back. This triggers the canvas to redraw with the correct contents. Two downsides: - rendering is slower, but only on the busted versions of webkit. Correct and useful is better than fast and useless. - There is a barely perceptible jitter of the control buttons because the canvas size is changing by one pixel. To support this functionality, we also have to read out the exact webkit version from the user agent in the render engine detection code in include/util.js.
234 lines
7.0 KiB
JavaScript
234 lines
7.0 KiB
JavaScript
/*
|
|
* noVNC: HTML5 VNC client
|
|
* Copyright (C) 2010 Joel Martin
|
|
* Licensed under LGPL-3 (see LICENSE.txt)
|
|
*
|
|
* See README.md for usage and integration instructions.
|
|
*/
|
|
|
|
"use strict";
|
|
/*jslint bitwise: false, white: false */
|
|
/*global window, console, document, navigator, ActiveXObject */
|
|
|
|
// Globals defined here
|
|
var Util = {};
|
|
|
|
|
|
/*
|
|
* Make arrays quack
|
|
*/
|
|
|
|
Array.prototype.push8 = function (num) {
|
|
this.push(num & 0xFF);
|
|
};
|
|
|
|
Array.prototype.push16 = function (num) {
|
|
this.push((num >> 8) & 0xFF,
|
|
(num ) & 0xFF );
|
|
};
|
|
Array.prototype.push32 = function (num) {
|
|
this.push((num >> 24) & 0xFF,
|
|
(num >> 16) & 0xFF,
|
|
(num >> 8) & 0xFF,
|
|
(num ) & 0xFF );
|
|
};
|
|
|
|
/*
|
|
* ------------------------------------------------------
|
|
* Namespaced in Util
|
|
* ------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* Logging/debug routines
|
|
*/
|
|
|
|
Util._log_level = 'warn';
|
|
Util.init_logging = function (level) {
|
|
if (typeof level === 'undefined') {
|
|
level = Util._log_level;
|
|
} else {
|
|
Util._log_level = level;
|
|
}
|
|
if (typeof window.console === "undefined") {
|
|
if (typeof window.opera !== "undefined") {
|
|
window.console = {
|
|
'log' : window.opera.postError,
|
|
'warn' : window.opera.postError,
|
|
'error': window.opera.postError };
|
|
} else {
|
|
window.console = {
|
|
'log' : function(m) {},
|
|
'warn' : function(m) {},
|
|
'error': function(m) {}};
|
|
}
|
|
}
|
|
|
|
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
|
|
switch (level) {
|
|
case 'debug': Util.Debug = function (msg) { console.log(msg); };
|
|
case 'info': Util.Info = function (msg) { console.log(msg); };
|
|
case 'warn': Util.Warn = function (msg) { console.warn(msg); };
|
|
case 'error': Util.Error = function (msg) { console.error(msg); };
|
|
case 'none':
|
|
break;
|
|
default:
|
|
throw("invalid logging type '" + level + "'");
|
|
}
|
|
};
|
|
Util.get_logging = function () {
|
|
return Util._log_level;
|
|
}
|
|
// Initialize logging level
|
|
Util.init_logging();
|
|
|
|
|
|
// Set defaults for Crockford style function namespaces
|
|
Util.conf_default = function(cfg, api, v, type, defval, desc) {
|
|
// Description
|
|
api['get_' + v + '_desc'] = desc;
|
|
// Default getter
|
|
if (typeof api['get_' + v] === 'undefined') {
|
|
api['get_' + v] = function () {
|
|
return cfg[v];
|
|
};
|
|
}
|
|
// Default setter
|
|
if (typeof api['set_' + v] === 'undefined') {
|
|
api['set_' + v] = function (val) {
|
|
if (type in {'boolean':1, 'bool':1}) {
|
|
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
|
|
val = false;
|
|
} else {
|
|
val = true;
|
|
}
|
|
} else if (type in {'integer':1, 'int':1}) {
|
|
val = parseInt(val, 10);
|
|
}
|
|
cfg[v] = val;
|
|
};
|
|
}
|
|
|
|
if (typeof cfg[v] === 'undefined') {
|
|
// Set to default
|
|
api['set_' + v](defval);
|
|
} else {
|
|
// Coerce existing setting to the right type
|
|
api['set_' + v](cfg[v]);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Cross-browser routines
|
|
*/
|
|
|
|
// Get DOM element position on page
|
|
Util.getPosition = function (obj) {
|
|
var x = 0, y = 0;
|
|
if (obj.offsetParent) {
|
|
do {
|
|
x += obj.offsetLeft;
|
|
y += obj.offsetTop;
|
|
obj = obj.offsetParent;
|
|
} while (obj);
|
|
}
|
|
return {'x': x, 'y': y};
|
|
};
|
|
|
|
// Get mouse event position in DOM element
|
|
Util.getEventPosition = function (e, obj, scale) {
|
|
var evt, docX, docY, pos;
|
|
//if (!e) evt = window.event;
|
|
evt = (e ? e : window.event);
|
|
if (evt.pageX || evt.pageY) {
|
|
docX = evt.pageX;
|
|
docY = evt.pageY;
|
|
} else if (evt.clientX || evt.clientY) {
|
|
docX = evt.clientX + document.body.scrollLeft +
|
|
document.documentElement.scrollLeft;
|
|
docY = evt.clientY + document.body.scrollTop +
|
|
document.documentElement.scrollTop;
|
|
}
|
|
pos = Util.getPosition(obj);
|
|
if (typeof scale === "undefined") {
|
|
scale = 1;
|
|
}
|
|
return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
|
|
};
|
|
|
|
|
|
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
|
|
Util.addEvent = function (obj, evType, fn){
|
|
if (obj.attachEvent){
|
|
var r = obj.attachEvent("on"+evType, fn);
|
|
return r;
|
|
} else if (obj.addEventListener){
|
|
obj.addEventListener(evType, fn, false);
|
|
return true;
|
|
} else {
|
|
throw("Handler could not be attached");
|
|
}
|
|
};
|
|
|
|
Util.removeEvent = function(obj, evType, fn){
|
|
if (obj.detachEvent){
|
|
var r = obj.detachEvent("on"+evType, fn);
|
|
return r;
|
|
} else if (obj.removeEventListener){
|
|
obj.removeEventListener(evType, fn, false);
|
|
return true;
|
|
} else {
|
|
throw("Handler could not be removed");
|
|
}
|
|
};
|
|
|
|
Util.stopEvent = function(e) {
|
|
if (e.stopPropagation) { e.stopPropagation(); }
|
|
else { e.cancelBubble = true; }
|
|
|
|
if (e.preventDefault) { e.preventDefault(); }
|
|
else { e.returnValue = false; }
|
|
};
|
|
|
|
|
|
// Set browser engine versions. Based on mootools.
|
|
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
|
|
|
Util.Engine = {
|
|
'presto': (function() {
|
|
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
|
'trident': (function() {
|
|
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
|
'webkit': (function() {
|
|
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
|
//'webkit': (function() {
|
|
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
|
|
'gecko': (function() {
|
|
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
|
|
};
|
|
if (Util.Engine.webkit) {
|
|
// Extract actual webkit version if available
|
|
Util.Engine.webkit = (function(v) {
|
|
var re = new RegExp('WebKit/([0-9\.]*) ');
|
|
v = (navigator.userAgent.match(re) || ['', v])[1];
|
|
return parseFloat(v, 10);
|
|
})(Util.Engine.webkit);
|
|
}
|
|
|
|
Util.Flash = (function(){
|
|
var v, version;
|
|
try {
|
|
v = navigator.plugins['Shockwave Flash'].description;
|
|
} catch(err1) {
|
|
try {
|
|
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
|
} catch(err2) {
|
|
v = '0 r0';
|
|
}
|
|
}
|
|
version = v.match(/\d+/g);
|
|
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
|
}());
|