]>
git.proxmox.com Git - mirror_novnc.git/blob - core/util.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Licensed under MPL 2.0 (see LICENSE.txt)
6 * See README.md for usage and integration instructions.
9 /* jshint white: false, nonstandard: true */
10 /*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
15 * ------------------------------------------------------
17 * ------------------------------------------------------
21 * Logging/debug routines
24 Util
._log_level
= 'warn';
25 Util
.init_logging = function (level
) {
27 if (typeof level
=== 'undefined') {
28 level
= Util
._log_level
;
30 Util
._log_level
= level
;
33 Util
.Debug
= Util
.Info
= Util
.Warn
= Util
.Error = function (msg
) {};
34 if (typeof window
.console
!== "undefined") {
38 Util
.Debug = function (msg
) { console
.log(msg
); };
40 Util
.Info = function (msg
) { console
.info(msg
); };
42 Util
.Warn = function (msg
) { console
.warn(msg
); };
44 Util
.Error = function (msg
) { console
.error(msg
); };
48 throw new Error("invalid logging type '" + level
+ "'");
53 Util
.get_logging = function () {
54 return Util
._log_level
;
56 // Initialize logging level
59 Util
.make_property = function (proto
, name
, mode
, type
) {
64 getter = function (idx
) {
65 if (typeof idx
!== 'undefined') {
66 return this['_' + name
][idx
];
68 return this['_' + name
];
72 getter = function () {
73 return this['_' + name
];
77 var make_setter = function (process_val
) {
79 return function (val
, idx
) {
80 if (typeof idx
!== 'undefined') {
81 this['_' + name
][idx
] = process_val(val
);
83 this['_' + name
] = process_val(val
);
87 return function (val
, idx
) {
88 if (typeof idx
!== 'undefined') {
89 this['_' + name
][idx
] = val
;
91 this['_' + name
] = val
;
98 if (type
=== 'bool') {
99 setter
= make_setter(function (val
) {
100 if (!val
|| (val
in {'0': 1, 'no': 1, 'false': 1})) {
106 } else if (type
=== 'int') {
107 setter
= make_setter(function (val
) { return parseInt(val
, 10); });
108 } else if (type
=== 'float') {
109 setter
= make_setter(parseFloat
);
110 } else if (type
=== 'str') {
111 setter
= make_setter(String
);
112 } else if (type
=== 'func') {
113 setter
= make_setter(function (val
) {
115 return function () {};
120 } else if (type
=== 'arr' || type
=== 'dom' || type
== 'raw') {
121 setter
= make_setter();
123 throw new Error('Unknown property type ' + type
); // some sanity checking
127 if (typeof proto
['get_' + name
] === 'undefined') {
128 proto
['get_' + name
] = getter
;
131 // set the setter if needed
132 if (typeof proto
['set_' + name
] === 'undefined') {
134 proto
['set_' + name
] = setter
;
135 } else if (mode
=== 'wo') {
136 proto
['set_' + name
] = function (val
, idx
) {
137 if (typeof this['_' + name
] !== 'undefined') {
138 throw new Error(name
+ " can only be set once");
140 setter
.call(this, val
, idx
);
145 // make a special setter that we can use in set defaults
146 proto
['_raw_set_' + name
] = function (val
, idx
) {
147 setter
.call(this, val
, idx
);
148 //delete this['_init_set_' + name]; // remove it after use
152 Util
.make_properties = function (constructor, arr
) {
154 for (var i
= 0; i
< arr
.length
; i
++) {
155 Util
.make_property(constructor.prototype, arr
[i
][0], arr
[i
][1], arr
[i
][2]);
159 Util
.set_defaults = function (obj
, conf
, defaults
) {
160 var defaults_keys
= Object
.keys(defaults
);
161 var conf_keys
= Object
.keys(conf
);
164 for (i
= 0; i
< defaults_keys
.length
; i
++) { keys_obj
[defaults_keys
[i
]] = 1; }
165 for (i
= 0; i
< conf_keys
.length
; i
++) { keys_obj
[conf_keys
[i
]] = 1; }
166 var keys
= Object
.keys(keys_obj
);
168 for (i
= 0; i
< keys
.length
; i
++) {
169 var setter
= obj
['_raw_set_' + keys
[i
]];
171 Util
.Warn('Invalid property ' + keys
[i
]);
175 if (keys
[i
] in conf
) {
176 setter
.call(obj
, conf
[keys
[i
]]);
178 setter
.call(obj
, defaults
[keys
[i
]]);
186 Util
.decodeUTF8 = function (utf8string
) {
188 return decodeURIComponent(escape(utf8string
));
194 * Cross-browser routines
197 Util
.getPosition = function(obj
) {
199 // NB(sross): the Mozilla developer reference seems to indicate that
200 // getBoundingClientRect includes border and padding, so the canvas
201 // style should NOT include either.
202 var objPosition
= obj
.getBoundingClientRect();
203 return {'x': objPosition
.left
+ window
.pageXOffset
, 'y': objPosition
.top
+ window
.pageYOffset
,
204 'width': objPosition
.width
, 'height': objPosition
.height
};
207 Util
.getPointerEvent = function (e
) {
209 evt
= (e
? e
: window
.event
);
210 evt
= (evt
.changedTouches
? evt
.changedTouches
[0] : evt
.touches
? evt
.touches
[0] : evt
);
214 // Get mouse event position in DOM element
215 Util
.getEventPosition = function (e
, obj
, scale
) {
217 var evt
, docX
, docY
, pos
;
218 evt
= Util
.getPointerEvent(e
);
219 if (evt
.pageX
|| evt
.pageY
) {
222 } else if (evt
.clientX
|| evt
.clientY
) {
223 docX
= evt
.clientX
+ document
.body
.scrollLeft
+
224 document
.documentElement
.scrollLeft
;
225 docY
= evt
.clientY
+ document
.body
.scrollTop
+
226 document
.documentElement
.scrollTop
;
228 pos
= Util
.getPosition(obj
);
229 if (typeof scale
=== "undefined") {
232 var realx
= docX
- pos
.x
;
233 var realy
= docY
- pos
.y
;
234 var x
= Math
.max(Math
.min(realx
, pos
.width
- 1), 0);
235 var y
= Math
.max(Math
.min(realy
, pos
.height
- 1), 0);
236 return {'x': x
/ scale
, 'y': y
/ scale
, 'realx': realx
/ scale
, 'realy': realy
/ scale
};
239 Util
.stopEvent = function (e
) {
245 Util
.isTouchDevice
= ('ontouchstart' in document
.documentElement
) ||
246 // requried for Chrome debugger
247 (document
.ontouchstart
!== undefined) ||
248 // required for MS Surface
249 (navigator
.maxTouchPoints
> 0) ||
250 (navigator
.msMaxTouchPoints
> 0);
251 window
.addEventListener('touchstart', function onFirstTouch() {
252 Util
.isTouchDevice
= true;
253 window
.removeEventListener('touchstart', onFirstTouch
, false);
256 Util
._cursor_uris_supported
= null;
258 Util
.browserSupportsCursorURIs = function () {
259 if (Util
._cursor_uris_supported
=== null) {
261 var target
= document
.createElement('canvas');
262 target
.style
.cursor
= 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
264 if (target
.style
.cursor
) {
265 Util
.Info("Data URI scheme cursor supported");
266 Util
._cursor_uris_supported
= true;
268 Util
.Warn("Data URI scheme cursor not supported");
269 Util
._cursor_uris_supported
= false;
272 Util
.Error("Data URI scheme cursor test exception: " + exc
);
273 Util
._cursor_uris_supported
= false;
277 return Util
._cursor_uris_supported
;
280 // Set browser engine versions. Based on mootools.
281 Util
.Features
= {xpath
: !!(document
.evaluate
), air
: !!(window
.runtime
), query
: !!(document
.querySelector
)};
285 // 'presto': (function () { return (!window.opera) ? false : true; }()),
286 var detectPresto = function () {
287 return !!window
.opera
;
290 // 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
291 var detectTrident = function () {
292 if (!window
.ActiveXObject
) {
295 if (window
.XMLHttpRequest
) {
296 return (document
.querySelectorAll
) ? 6 : 5;
303 // 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
304 var detectInitialWebkit = function () {
306 if (navigator
.taintEnabled
) {
309 if (Util
.Features
.xpath
) {
310 return (Util
.Features
.query
) ? 525 : 420;
320 var detectActualWebkit = function (initial_ver
) {
321 var re
= /WebKit\/([0-9\.]*) /;
322 var str_ver
= (navigator
.userAgent
.match(re
) || ['', initial_ver
])[1];
323 return parseFloat(str_ver
, 10);
326 // 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
327 var detectGecko = function () {
329 if (!document
.getBoxObjectFor
&& window
.mozInnerScreenX
== null) {
332 return (document
.getElementsByClassName
) ? 19 : 18;
338 // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
339 //'presto': (function() {
340 // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
341 'presto': detectPresto(),
342 'trident': detectTrident(),
343 'webkit': detectInitialWebkit(),
344 'gecko': detectGecko()
347 if (Util
.Engine
.webkit
) {
348 // Extract actual webkit version if available
349 Util
.Engine
.webkit
= detectActualWebkit(Util
.Engine
.webkit
);
353 Util
.Flash
= (function () {
357 v
= navigator
.plugins
['Shockwave Flash'].description
;
360 v
= new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
365 version
= v
.match(/\d+/g);
366 return {version
: parseInt(version
[0] || 0 + '.' + version
[1], 10) || 0, build
: parseInt(version
[2], 10) || 0};
370 Util
.Localisation
= {
371 // Currently configured language
374 // Configure suitable language based on user preferences
375 setup: function (supportedLanguages
) {
378 Util
.Localisation
.language
= 'en'; // Default: US English
381 * Navigator.languages only available in Chrome (32+) and FireFox (32+)
382 * Fall back to navigator.language for other browsers
384 if (typeof window
.navigator
.languages
== 'object') {
385 userLanguages
= window
.navigator
.languages
;
387 userLanguages
= [navigator
.language
|| navigator
.userLanguage
];
390 for (var i
= 0;i
< userLanguages
.length
;i
++) {
391 var userLang
= userLanguages
[i
];
392 userLang
= userLang
.toLowerCase();
393 userLang
= userLang
.replace("_", "-");
394 userLang
= userLang
.split("-");
397 if ((userLang
[0] === 'en') &&
398 ((userLang
[1] === undefined) || (userLang
[1] === 'us'))) {
402 // First pass: perfect match
403 for (var j
= 0;j
< supportedLanguages
.length
;j
++) {
404 var supLang
= supportedLanguages
[j
];
405 supLang
= supLang
.toLowerCase();
406 supLang
= supLang
.replace("_", "-");
407 supLang
= supLang
.split("-");
409 if (userLang
[0] !== supLang
[0])
411 if (userLang
[1] !== supLang
[1])
414 Util
.Localisation
.language
= supportedLanguages
[j
];
418 // Second pass: fallback
419 for (var j
= 0;j
< supportedLanguages
.length
;j
++) {
420 supLang
= supportedLanguages
[j
];
421 supLang
= supLang
.toLowerCase();
422 supLang
= supLang
.replace("_", "-");
423 supLang
= supLang
.split("-");
425 if (userLang
[0] !== supLang
[0])
427 if (supLang
[1] !== undefined)
430 Util
.Localisation
.language
= supportedLanguages
[j
];
436 // Retrieve localised text
438 if (typeof Language
!== 'undefined' && Language
[id
]) {
446 /* [module] export default Util; */