util.js 7.5 KB

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