util.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2011 Joel Martin
  4. * Licensed under LGPL-3 (see LICENSE.txt)
  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. * Make arrays quack
  15. */
  16. Array.prototype.push8 = function (num) {
  17. this.push(num & 0xFF);
  18. };
  19. Array.prototype.push16 = function (num) {
  20. this.push((num >> 8) & 0xFF,
  21. (num ) & 0xFF );
  22. };
  23. Array.prototype.push32 = function (num) {
  24. this.push((num >> 24) & 0xFF,
  25. (num >> 16) & 0xFF,
  26. (num >> 8) & 0xFF,
  27. (num ) & 0xFF );
  28. };
  29. /*
  30. * ------------------------------------------------------
  31. * Namespaced in Util
  32. * ------------------------------------------------------
  33. */
  34. /*
  35. * Logging/debug routines
  36. */
  37. Util._log_level = 'warn';
  38. Util.init_logging = function (level) {
  39. if (typeof level === 'undefined') {
  40. level = Util._log_level;
  41. } else {
  42. Util._log_level = level;
  43. }
  44. if (typeof window.console === "undefined") {
  45. if (typeof window.opera !== "undefined") {
  46. window.console = {
  47. 'log' : window.opera.postError,
  48. 'warn' : window.opera.postError,
  49. 'error': window.opera.postError };
  50. } else {
  51. window.console = {
  52. 'log' : function(m) {},
  53. 'warn' : function(m) {},
  54. 'error': function(m) {}};
  55. }
  56. }
  57. Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
  58. switch (level) {
  59. case 'debug': Util.Debug = function (msg) { console.log(msg); };
  60. case 'info': Util.Info = function (msg) { console.log(msg); };
  61. case 'warn': Util.Warn = function (msg) { console.warn(msg); };
  62. case 'error': Util.Error = function (msg) { console.error(msg); };
  63. case 'none':
  64. break;
  65. default:
  66. throw("invalid logging type '" + level + "'");
  67. }
  68. };
  69. Util.get_logging = function () {
  70. return Util._log_level;
  71. };
  72. // Initialize logging level
  73. Util.init_logging();
  74. // Set configuration default for Crockford style function namespaces
  75. Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
  76. var getter, setter;
  77. // Default getter function
  78. getter = function (idx) {
  79. if ((type in {'arr':1, 'array':1}) &&
  80. (typeof idx !== 'undefined')) {
  81. return cfg[v][idx];
  82. } else {
  83. return cfg[v];
  84. }
  85. };
  86. // Default setter function
  87. setter = function (val, idx) {
  88. if (type in {'boolean':1, 'bool':1}) {
  89. if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
  90. val = false;
  91. } else {
  92. val = true;
  93. }
  94. } else if (type in {'integer':1, 'int':1}) {
  95. val = parseInt(val, 10);
  96. } else if (type === 'func') {
  97. if (!val) {
  98. val = function () {};
  99. }
  100. }
  101. if (typeof idx !== 'undefined') {
  102. cfg[v][idx] = val;
  103. } else {
  104. cfg[v] = val;
  105. }
  106. };
  107. // Set the description
  108. api[v + '_description'] = desc;
  109. // Set the getter function
  110. if (typeof api['get_' + v] === 'undefined') {
  111. api['get_' + v] = getter;
  112. }
  113. // Set the setter function with extra sanity checks
  114. if (typeof api['set_' + v] === 'undefined') {
  115. api['set_' + v] = function (val, idx) {
  116. if (mode in {'RO':1, 'ro':1}) {
  117. throw(v + " is read-only");
  118. } else if ((mode in {'WO':1, 'wo':1}) &&
  119. (typeof cfg[v] !== 'undefined')) {
  120. throw(v + " can only be set once");
  121. }
  122. setter(val, idx);
  123. };
  124. }
  125. // Set the default value
  126. if (typeof defaults[v] !== 'undefined') {
  127. defval = defaults[v];
  128. } else if ((type in {'arr':1, 'array':1}) &&
  129. (! (defval instanceof Array))) {
  130. defval = [];
  131. }
  132. // Coerce existing setting to the right type
  133. //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
  134. setter(defval);
  135. };
  136. // Set group of configuration defaults
  137. Util.conf_defaults = function(cfg, api, defaults, arr) {
  138. var i;
  139. for (i = 0; i < arr.length; i++) {
  140. Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
  141. arr[i][2], arr[i][3], arr[i][4]);
  142. }
  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, scale) {
  161. var evt, docX, docY, pos;
  162. //if (!e) evt = window.event;
  163. evt = (e ? e : window.event);
  164. evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
  165. if (evt.pageX || evt.pageY) {
  166. docX = evt.pageX;
  167. docY = evt.pageY;
  168. } else if (evt.clientX || evt.clientY) {
  169. docX = evt.clientX + document.body.scrollLeft +
  170. document.documentElement.scrollLeft;
  171. docY = evt.clientY + document.body.scrollTop +
  172. document.documentElement.scrollTop;
  173. }
  174. pos = Util.getPosition(obj);
  175. if (typeof scale === "undefined") {
  176. scale = 1;
  177. }
  178. return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
  179. };
  180. // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
  181. Util.addEvent = function (obj, evType, fn){
  182. if (obj.attachEvent){
  183. var r = obj.attachEvent("on"+evType, fn);
  184. return r;
  185. } else if (obj.addEventListener){
  186. obj.addEventListener(evType, fn, false);
  187. return true;
  188. } else {
  189. throw("Handler could not be attached");
  190. }
  191. };
  192. Util.removeEvent = function(obj, evType, fn){
  193. if (obj.detachEvent){
  194. var r = obj.detachEvent("on"+evType, fn);
  195. return r;
  196. } else if (obj.removeEventListener){
  197. obj.removeEventListener(evType, fn, false);
  198. return true;
  199. } else {
  200. throw("Handler could not be removed");
  201. }
  202. };
  203. Util.stopEvent = function(e) {
  204. if (e.stopPropagation) { e.stopPropagation(); }
  205. else { e.cancelBubble = true; }
  206. if (e.preventDefault) { e.preventDefault(); }
  207. else { e.returnValue = false; }
  208. };
  209. // Set browser engine versions. Based on mootools.
  210. Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
  211. Util.Engine = {
  212. // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
  213. //'presto': (function() {
  214. - // return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
  215. 'presto': (function() { return (!window.opera) ? false : true; }()),
  216. 'trident': (function() {
  217. return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
  218. 'webkit': (function() {
  219. try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
  220. //'webkit': (function() {
  221. // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
  222. 'gecko': (function() {
  223. return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
  224. };
  225. if (Util.Engine.webkit) {
  226. // Extract actual webkit version if available
  227. Util.Engine.webkit = (function(v) {
  228. var re = new RegExp('WebKit/([0-9\.]*) ');
  229. v = (navigator.userAgent.match(re) || ['', v])[1];
  230. return parseFloat(v, 10);
  231. })(Util.Engine.webkit);
  232. }
  233. Util.Flash = (function(){
  234. var v, version;
  235. try {
  236. v = navigator.plugins['Shockwave Flash'].description;
  237. } catch(err1) {
  238. try {
  239. v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  240. } catch(err2) {
  241. v = '0 r0';
  242. }
  243. }
  244. version = v.match(/\d+/g);
  245. return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
  246. }());