util.js 9.6 KB

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