util.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. var defaultConfig = {'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
  2. var util = {
  3. noop: function() {},
  4. CLOUD_HOST: '0.peerjs.com',
  5. CLOUD_PORT: 9000,
  6. // Logging logic
  7. logLevel: 0,
  8. setLogLevel: function(level) {
  9. var debugLevel = parseInt(level, 10);
  10. if (!isNaN(parseInt(level, 10))) {
  11. util.logLevel = debugLevel;
  12. } else {
  13. // If they are using truthy/falsy values for debug
  14. util.logLevel = (!!level) ? 3 : 0;
  15. }
  16. util.log = util.warn = util.error = util.noop;
  17. if (util.logLevel > 0) {
  18. util.error = util._printWith('ERROR');
  19. }
  20. if (util.logLevel > 1) {
  21. util.warn = util._printWith('WARNING');
  22. }
  23. if (util.logLevel > 2) {
  24. util.log = util._print;
  25. }
  26. },
  27. setLogFunction: function(fn) {
  28. if (fn.constructor !== Function) {
  29. util.warn('The log function you passed in is not a function. Defaulting to regular logs.');
  30. } else {
  31. util._print = fn;
  32. }
  33. },
  34. _printWith: function(prefix) {
  35. return function() {
  36. var copy = Array.prototype.slice.call(arguments);
  37. copy.unshift(prefix);
  38. util._print.apply(util, copy);
  39. };
  40. },
  41. _print: function () {
  42. var err = false;
  43. var copy = Array.prototype.slice.call(arguments);
  44. copy.unshift('PeerJS: ');
  45. for (var i = 0, l = copy.length; i < l; i++){
  46. if (copy[i] instanceof Error) {
  47. copy[i] = '(' + copy[i].name + ') ' + copy[i].message;
  48. err = true;
  49. }
  50. }
  51. err ? console.error.apply(console, copy) : console.log.apply(console, copy);
  52. },
  53. //
  54. // Returns browser-agnostic default config
  55. defaultConfig: defaultConfig,
  56. //
  57. // Lists which features are supported
  58. // Temporarily set everything to true
  59. supports: (function() {
  60. var data = true;
  61. var audioVideo = true;
  62. var pc, dc;
  63. try {
  64. pc = new RTCPeerConnection(defaultConfig, {optional: [{RtpDataChannels: true}]});
  65. } catch (e) {
  66. data = false;
  67. audioVideo = false;
  68. }
  69. if (data) {
  70. try {
  71. dc = pc.createDataChannel('_PEERJSDATATEST');
  72. } catch (e) {
  73. data = false;
  74. }
  75. }
  76. // FIXME: not really the best check...
  77. if (audioVideo) {
  78. audioVideo = !!pc.addStream;
  79. }
  80. pc.close();
  81. dc.close();
  82. return {
  83. audioVideo: audioVideo,
  84. data: data,
  85. binary: data && (function() {
  86. var pc = new RTCPeerConnection(defaultConfig, {optional: [{RtpDataChannels: true}]});
  87. var dc = pc.createDataChannel('_PEERJSBINARYTEST');
  88. try {
  89. dc.binaryType = 'blob';
  90. } catch (e) {
  91. pc.close();
  92. if (e.name === 'NotSupportedError') {
  93. return false
  94. }
  95. }
  96. pc.close();
  97. dc.close();
  98. return true;
  99. })(),
  100. reliable: data && (function() {
  101. // Reliable (not RTP).
  102. var pc = new RTCPeerConnection(defaultConfig, {});
  103. var dc;
  104. try {
  105. dc = pc.createDataChannel('_PEERJSRELIABLETEST');
  106. } catch (e) {
  107. pc.close();
  108. if (e.name === 'NotSupportedError') {
  109. return false
  110. }
  111. }
  112. pc.close();
  113. dc.close();
  114. return true;
  115. })(),
  116. onnegotiationneeded: (data || audioVideo) && (function() {
  117. var pc = new RTCPeerConnection(defaultConfig, {});
  118. // sync default check.
  119. var called = false;
  120. var pc = new RTCPeerConnection(defaultConfig, {optional: [{RtpDataChannels: true}]});
  121. pc.onnegotiationneeded = function() {
  122. called = true;
  123. // async check.
  124. if (util && util.supports) {
  125. util.supports.onnegotiationneeded = true;
  126. }
  127. };
  128. // FIXME: this is not great because in theory it doesn't work for
  129. // av-only browsers (?).
  130. var dc = pc.createDataChannel('_PEERJSRELIABLETEST');
  131. pc.close();
  132. dc.close();
  133. return called;
  134. })(),
  135. // TODO(michelle): whether this browser can interop with a different
  136. // browser. But the two browsers both have to support interop.
  137. interop: false
  138. };
  139. }()),
  140. //
  141. // Ensure alphanumeric ids
  142. validateId: function(id) {
  143. // Allow empty ids
  144. return !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id);
  145. },
  146. validateKey: function(key) {
  147. // Allow empty keys
  148. return !key || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(key);
  149. },
  150. // OLD
  151. chromeCompatible: true,
  152. firefoxCompatible: true,
  153. chromeVersion: 26,
  154. firefoxVersion: 22,
  155. debug: false,
  156. browserisms: '',
  157. inherits: function(ctor, superCtor) {
  158. ctor.super_ = superCtor;
  159. ctor.prototype = Object.create(superCtor.prototype, {
  160. constructor: {
  161. value: ctor,
  162. enumerable: false,
  163. writable: true,
  164. configurable: true
  165. }
  166. });
  167. },
  168. extend: function(dest, source) {
  169. for(var key in source) {
  170. if(source.hasOwnProperty(key)) {
  171. dest[key] = source[key];
  172. }
  173. }
  174. return dest;
  175. },
  176. pack: BinaryPack.pack,
  177. unpack: BinaryPack.unpack,
  178. log: function () {
  179. if (util.debug) {
  180. var err = false;
  181. var copy = Array.prototype.slice.call(arguments);
  182. copy.unshift('PeerJS: ');
  183. for (var i = 0, l = copy.length; i < l; i++){
  184. if (copy[i] instanceof Error) {
  185. copy[i] = '(' + copy[i].name + ') ' + copy[i].message;
  186. err = true;
  187. }
  188. }
  189. err ? console.error.apply(console, copy) : console.log.apply(console, copy);
  190. }
  191. },
  192. setZeroTimeout: (function(global) {
  193. var timeouts = [];
  194. var messageName = 'zero-timeout-message';
  195. // Like setTimeout, but only takes a function argument. There's
  196. // no time argument (always zero) and no arguments (you have to
  197. // use a closure).
  198. function setZeroTimeoutPostMessage(fn) {
  199. timeouts.push(fn);
  200. global.postMessage(messageName, '*');
  201. }
  202. function handleMessage(event) {
  203. if (event.source == global && event.data == messageName) {
  204. if (event.stopPropagation) {
  205. event.stopPropagation();
  206. }
  207. if (timeouts.length) {
  208. timeouts.shift()();
  209. }
  210. }
  211. }
  212. if (global.addEventListener) {
  213. global.addEventListener('message', handleMessage, true);
  214. } else if (global.attachEvent) {
  215. global.attachEvent('onmessage', handleMessage);
  216. }
  217. return setZeroTimeoutPostMessage;
  218. }(this)),
  219. // Binary stuff
  220. blobToArrayBuffer: function(blob, cb){
  221. var fr = new FileReader();
  222. fr.onload = function(evt) {
  223. cb(evt.target.result);
  224. };
  225. fr.readAsArrayBuffer(blob);
  226. },
  227. blobToBinaryString: function(blob, cb){
  228. var fr = new FileReader();
  229. fr.onload = function(evt) {
  230. cb(evt.target.result);
  231. };
  232. fr.readAsBinaryString(blob);
  233. },
  234. binaryStringToArrayBuffer: function(binary) {
  235. var byteArray = new Uint8Array(binary.length);
  236. for (var i = 0; i < binary.length; i++) {
  237. byteArray[i] = binary.charCodeAt(i) & 0xff;
  238. }
  239. return byteArray.buffer;
  240. },
  241. randomToken: function () {
  242. return Math.random().toString(36).substr(2);
  243. },
  244. //
  245. // When we have proper version/feature mappings we can remove this
  246. isBrowserCompatible: function() {
  247. var c, f;
  248. if (this.chromeCompatible) {
  249. if ((c = navigator.userAgent.split('Chrome/')) && c.length > 1) {
  250. // Get version #.
  251. var v = c[1].split('.')[0];
  252. return parseInt(v) >= this.chromeVersion;
  253. }
  254. }
  255. if (this.firefoxCompatible) {
  256. if ((f = navigator.userAgent.split('Firefox/')) && f.length > 1) {
  257. // Get version #.
  258. var v = f[1].split('.')[0];
  259. return parseInt(v) >= this.firefoxVersion;
  260. }
  261. }
  262. return false;
  263. },
  264. isSecure: function() {
  265. return location.protocol === 'https:';
  266. }
  267. };
  268. exports.util = util;