util.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2010 Joel Martin
  4. * Licensed under LGPL-3 (see LICENSE.LGPL-3)
  5. *
  6. * See README.md for usage and integration instructions.
  7. */
  8. "use strict";
  9. /*jslint bitwise: false, white: false */
  10. /*global window, console, document, navigator, ActiveXObject*/
  11. // Globals defined here
  12. var Util = {}, $;
  13. /*
  14. * Simple DOM selector by ID
  15. */
  16. if (!window.$) {
  17. $ = function (id) {
  18. if (document.getElementById) {
  19. return document.getElementById(id);
  20. } else if (document.all) {
  21. return document.all[id];
  22. } else if (document.layers) {
  23. return document.layers[id];
  24. }
  25. return undefined;
  26. };
  27. }
  28. /*
  29. * Make arrays quack
  30. */
  31. Array.prototype.shift8 = function () {
  32. return this.shift();
  33. };
  34. Array.prototype.push8 = function (num) {
  35. this.push(num & 0xFF);
  36. };
  37. Array.prototype.shift16 = function () {
  38. return (this.shift() << 8) +
  39. (this.shift() );
  40. };
  41. Array.prototype.push16 = function (num) {
  42. this.push((num >> 8) & 0xFF,
  43. (num ) & 0xFF );
  44. };
  45. Array.prototype.push16le = function (num) {
  46. this.push((num ) & 0xFF,
  47. (num >> 8) & 0xFF );
  48. };
  49. Array.prototype.shift32 = function () {
  50. return (this.shift() << 24) +
  51. (this.shift() << 16) +
  52. (this.shift() << 8) +
  53. (this.shift() );
  54. };
  55. Array.prototype.get32 = function (off) {
  56. return (this[off ] << 24) +
  57. (this[off + 1] << 16) +
  58. (this[off + 2] << 8) +
  59. (this[off + 3] );
  60. };
  61. Array.prototype.push32 = function (num) {
  62. this.push((num >> 24) & 0xFF,
  63. (num >> 16) & 0xFF,
  64. (num >> 8) & 0xFF,
  65. (num ) & 0xFF );
  66. };
  67. Array.prototype.push32le = function (num) {
  68. this.push((num ) & 0xFF,
  69. (num >> 8) & 0xFF,
  70. (num >> 16) & 0xFF,
  71. (num >> 24) & 0xFF );
  72. };
  73. Array.prototype.shiftStr = function (len) {
  74. var arr = this.splice(0, len);
  75. return arr.map(function (num) {
  76. return String.fromCharCode(num); } ).join('');
  77. };
  78. Array.prototype.pushStr = function (str) {
  79. var i, n = str.length;
  80. for (i=0; i < n; i+=1) {
  81. this.push(str.charCodeAt(i));
  82. }
  83. };
  84. Array.prototype.shiftBytes = function (len) {
  85. return this.splice(0, len);
  86. };
  87. /*
  88. * ------------------------------------------------------
  89. * Namespaced in Util
  90. * ------------------------------------------------------
  91. */
  92. /*
  93. * Logging/debug routines
  94. */
  95. Util.init_logging = function (level) {
  96. if (typeof window.console === "undefined") {
  97. if (typeof window.opera !== "undefined") {
  98. window.console = {
  99. 'log' : window.opera.postError,
  100. 'warn' : window.opera.postError,
  101. 'error': window.opera.postError };
  102. } else {
  103. window.console = {
  104. 'log' : function(m) {},
  105. 'warn' : function(m) {},
  106. 'error': function(m) {}};
  107. }
  108. }
  109. Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
  110. switch (level) {
  111. case 'debug': Util.Debug = function (msg) { console.log(msg); };
  112. case 'info': Util.Info = function (msg) { console.log(msg); };
  113. case 'warn': Util.Warn = function (msg) { console.warn(msg); };
  114. case 'error': Util.Error = function (msg) { console.error(msg); };
  115. case 'none':
  116. break;
  117. default:
  118. throw("invalid logging type '" + level + "'");
  119. }
  120. };
  121. // Initialize logging level
  122. Util.init_logging( (document.location.href.match(
  123. /logging=([A-Za-z0-9\._\-]*)/) ||
  124. ['', 'warn'])[1] );
  125. Util.dirObj = function (obj, depth, parent) {
  126. var i, msg = "", val = "";
  127. if (! depth) { depth=2; }
  128. if (! parent) { parent= ""; }
  129. // Print the properties of the passed-in object
  130. for (i in obj) {
  131. if ((depth > 1) && (typeof obj[i] === "object")) {
  132. // Recurse attributes that are objects
  133. msg += Util.dirObj(obj[i], depth-1, parent + "." + i);
  134. } else {
  135. //val = new String(obj[i]).replace("\n", " ");
  136. val = obj[i].toString().replace("\n", " ");
  137. if (val.length > 30) {
  138. val = val.substr(0,30) + "...";
  139. }
  140. msg += parent + "." + i + ": " + val + "\n";
  141. }
  142. }
  143. return msg;
  144. };
  145. // Read a query string variable
  146. Util.getQueryVar = function(name, defVal) {
  147. var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
  148. if (typeof defVal === 'undefined') { defVal = null; }
  149. return (document.location.href.match(re) || ['',defVal])[1];
  150. };
  151. // Set defaults for Crockford style function namespaces
  152. Util.conf_default = function(cfg, api, v, val, force_bool) {
  153. if (typeof cfg[v] === 'undefined') {
  154. cfg[v] = val;
  155. }
  156. // Default getter
  157. if (typeof api['get_' + v] === 'undefined') {
  158. api['get_' + v] = function () {
  159. return cfg[v];
  160. };
  161. }
  162. // Default setter
  163. if (typeof api['set_' + v] === 'undefined') {
  164. api['set_' + v] = function (val) {
  165. if (force_bool) {
  166. if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
  167. val = false;
  168. } else {
  169. val = true;
  170. }
  171. }
  172. cfg[v] = val;
  173. };
  174. }
  175. };
  176. /*
  177. * Cross-browser routines
  178. */
  179. // Get DOM element position on page
  180. Util.getPosition = function (obj) {
  181. var x = 0, y = 0;
  182. if (obj.offsetParent) {
  183. do {
  184. x += obj.offsetLeft;
  185. y += obj.offsetTop;
  186. obj = obj.offsetParent;
  187. } while (obj);
  188. }
  189. return {'x': x, 'y': y};
  190. };
  191. // Get mouse event position in DOM element
  192. Util.getEventPosition = function (e, obj, scale) {
  193. var evt, docX, docY, pos;
  194. //if (!e) evt = window.event;
  195. evt = (e ? e : window.event);
  196. if (evt.pageX || evt.pageY) {
  197. docX = evt.pageX;
  198. docY = evt.pageY;
  199. } else if (evt.clientX || evt.clientY) {
  200. docX = evt.clientX + document.body.scrollLeft +
  201. document.documentElement.scrollLeft;
  202. docY = evt.clientY + document.body.scrollTop +
  203. document.documentElement.scrollTop;
  204. }
  205. pos = Util.getPosition(obj);
  206. if (typeof scale === "undefined") {
  207. scale = 1;
  208. }
  209. return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
  210. };
  211. // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
  212. Util.addEvent = function (obj, evType, fn){
  213. if (obj.attachEvent){
  214. var r = obj.attachEvent("on"+evType, fn);
  215. return r;
  216. } else if (obj.addEventListener){
  217. obj.addEventListener(evType, fn, false);
  218. return true;
  219. } else {
  220. throw("Handler could not be attached");
  221. }
  222. };
  223. Util.removeEvent = function(obj, evType, fn){
  224. if (obj.detachEvent){
  225. var r = obj.detachEvent("on"+evType, fn);
  226. return r;
  227. } else if (obj.removeEventListener){
  228. obj.removeEventListener(evType, fn, false);
  229. return true;
  230. } else {
  231. throw("Handler could not be removed");
  232. }
  233. };
  234. Util.stopEvent = function(e) {
  235. if (e.stopPropagation) { e.stopPropagation(); }
  236. else { e.cancelBubble = true; }
  237. if (e.preventDefault) { e.preventDefault(); }
  238. else { e.returnValue = false; }
  239. };
  240. // Set browser engine versions. Based on mootools.
  241. Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
  242. Util.Engine = {
  243. 'presto': (function() {
  244. return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
  245. 'trident': (function() {
  246. return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
  247. 'webkit': (function() {
  248. try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
  249. //'webkit': (function() {
  250. // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
  251. 'gecko': (function() {
  252. return (!document.getBoxObjectFor && !window.mozInnerScreenX) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
  253. };
  254. Util.Flash = (function(){
  255. var v, version;
  256. try {
  257. v = navigator.plugins['Shockwave Flash'].description;
  258. } catch(err1) {
  259. try {
  260. v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  261. } catch(err2) {
  262. v = '0 r0';
  263. }
  264. }
  265. version = v.match(/\d+/g);
  266. return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
  267. }());
  268. /*
  269. * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
  270. */
  271. // No days means only for this browser session
  272. Util.createCookie = function(name,value,days) {
  273. var date, expires;
  274. if (days) {
  275. date = new Date();
  276. date.setTime(date.getTime()+(days*24*60*60*1000));
  277. expires = "; expires="+date.toGMTString();
  278. }
  279. else {
  280. expires = "";
  281. }
  282. document.cookie = name+"="+value+expires+"; path=/";
  283. };
  284. Util.readCookie = function(name, defaultValue) {
  285. var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
  286. for(i=0; i < ca.length; i += 1) {
  287. c = ca[i];
  288. while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
  289. if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
  290. }
  291. return (typeof defaultValue !== 'undefined') ? defaultValue : null;
  292. };
  293. Util.eraseCookie = function(name) {
  294. Util.createCookie(name,"",-1);
  295. };
  296. /*
  297. * Alternate stylesheet selection
  298. */
  299. Util.getStylesheets = function() { var i, links, sheets = [];
  300. links = document.getElementsByTagName("link");
  301. for (i = 0; i < links.length; i += 1) {
  302. if (links[i].title &&
  303. links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
  304. sheets.push(links[i]);
  305. }
  306. }
  307. return sheets;
  308. };
  309. // No sheet means try and use value from cookie, null sheet used to
  310. // clear all alternates.
  311. Util.selectStylesheet = function(sheet) {
  312. var i, link, sheets = Util.getStylesheets();
  313. if (typeof sheet === 'undefined') {
  314. sheet = 'default';
  315. }
  316. for (i=0; i < sheets.length; i += 1) {
  317. link = sheets[i];
  318. if (link.title === sheet) {
  319. Util.Debug("Using stylesheet " + sheet);
  320. link.disabled = false;
  321. } else {
  322. //Util.Debug("Skipping stylesheet " + link.title);
  323. link.disabled = true;
  324. }
  325. }
  326. return sheet;
  327. };