PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/socket.io/socket.io.js

https://github.com/DerGuteMoritz/pubnub-api
JavaScript | 249 lines | 186 code | 35 blank | 28 comment | 44 complexity | 74e83b558f22ece62f01c1fc279fcae1 MD5 | raw file
Possible License(s): Apache-2.0
  1. (function(){
  2. // =====================================================================
  3. // PubNub Socket.IO
  4. // =====================================================================
  5. var p = PUBNUB
  6. , standard = 'standard'
  7. , uuid = PUBNUB.db.get('uuid') || p.uuid(function(id){
  8. PUBNUB.db.set( 'uuid', uuid = id )
  9. })
  10. , now = function(){return+new Date}
  11. , namespaces = {}
  12. , users = {}
  13. , io = window.io = {
  14. connected : false,
  15. connect : function( host, setup ) {
  16. // PARSE SETUP and HOST
  17. var urlbits = (host+'////').split('/')
  18. , setup = setup || {}
  19. , cuser = setup['user'] || {}
  20. , presence = 'presence' in setup ? setup['presence'] : true
  21. , origin = urlbits[2]
  22. , namespace = urlbits[3] || standard
  23. , socket = create_socket(namespace);
  24. // PASSWORD ENCRYPTION
  25. socket.password = 'sjcl' in window && setup.password;
  26. // PUBNUB ALREADY SETUP?
  27. if (io.connected) return socket;
  28. io.connected = true;
  29. // GEO LOCATION
  30. if (setup.geo) setInterval( locate, 15000 ) && locate();
  31. // SETUP PUBNUB
  32. setup.origin = origin;
  33. p = p.init(setup);
  34. p.disconnected = 0;
  35. p.channel = setup.channel || standard;
  36. // DISCONNECTED
  37. function dropped() {
  38. if (p.disconnected) return;
  39. p.disconnected = 1;
  40. p.each( namespaces, function(ns) {
  41. p.events.fire( ns + 'disconnect', {} )
  42. } );
  43. }
  44. // ESTABLISH CONNECTION
  45. p.subscribe({
  46. channel : p.channel,
  47. disconnect : dropped,
  48. reconnect : function() {p.disconnected = 0;},
  49. connect : function() {
  50. p.disconnected = 0;
  51. p.each( namespaces, function(ns) {
  52. p.events.fire( ns + 'connect', {} )
  53. } );
  54. },
  55. callback : function(evt) {
  56. if (p.disconnected) p.each( namespaces, function(ns) {
  57. p.events.fire( ns + 'reconnect', {} )
  58. } );
  59. p.disconnected = 0;
  60. var data = decrypt( evt.ns, evt.data );
  61. if (evt.ns in namespaces)
  62. data && p.events.fire( evt.ns + evt.name, data );
  63. // USER EVENTS
  64. if (!evt.uuid || evt.uuid === uuid) return;
  65. evt.name === 'ping' && p.each( data.nss, function(ns) {
  66. users[ns] = users[ns] || {};
  67. var user = users[ns][evt.uuid] =
  68. users[ns][evt.uuid] || (function() { return {
  69. geo : evt.geo || [ 0, 0 ],
  70. uuid : evt.uuid,
  71. last : now(),
  72. socket : socket,
  73. namespace : ns,
  74. connected : false,
  75. slot : socket.user_count++
  76. } })();
  77. user.last = now();
  78. user.data = data.cuser;
  79. if (user.connected) return;
  80. user.connected = true;
  81. p.events.fire( ns + 'join', user );
  82. } );
  83. if (evt.name === 'user-disconnect') disconnect(evt.uuid);
  84. }
  85. });
  86. // TCP KEEP ALIVE
  87. if (presence)
  88. p.tcpKeepAlive = setInterval( p.updater( function() {
  89. var nss = p.map( namespaces, function(ns) { return ns } );
  90. send( 'ping', standard, { nss : nss, cuser : cuser } );
  91. }, 2500 ), 500 );
  92. // RETURN SOCKET
  93. return socket;
  94. }
  95. };
  96. // =====================================================================
  97. // Advanced User Presence
  98. // =====================================================================
  99. setInterval( function() {
  100. if (p.disconnected) return;
  101. p.each( users, function(namespace) {
  102. p.each( users[namespace], function(uid) {
  103. var user = users[namespace][uid];
  104. if (now() - user.last < 5500 || uid === uuid) return;
  105. disconnect(user.uuid);
  106. } );
  107. } );
  108. }, 1000 );
  109. function disconnect(uid) {
  110. p.each( namespaces, function(ns) {
  111. var user = users[ns][uid];
  112. if (!user.connected) return;
  113. user.connected = false;
  114. user.socket.user_count--;
  115. p.events.fire( ns + 'leave', user )
  116. } );
  117. }
  118. typeof window !== 'undefined' &&
  119. p.bind( 'beforeunload', window, function() {
  120. send( 'user-disconnect', '', uuid );
  121. return true;
  122. } );
  123. // =====================================================================
  124. // Stanford Crypto Library with AES Encryption
  125. // =====================================================================
  126. function encrypt( namespace, data ) {
  127. var namespace = namespace || standard
  128. , socket = namespaces[namespace];
  129. return 'password' in socket && socket.password && sjcl.encrypt(
  130. socket.password, JSON.stringify(data)+''
  131. ) || data;
  132. }
  133. function decrypt( namespace, data ) {
  134. var namespace = namespace || standard
  135. , socket = namespaces[namespace];
  136. if (!socket.password) return data;
  137. try { return JSON.parse(
  138. sjcl.decrypt( socket.password, data )
  139. ); }
  140. catch(e) { return null; }
  141. }
  142. // =====================================================================
  143. // PUBLISH A MESSAGE + Retry if Failed with fallback
  144. // =====================================================================
  145. function send( event, namespace, data, wait, cb ) {
  146. p.publish({
  147. channel : p.channel,
  148. message : {
  149. name : event,
  150. ns : namespace || standard,
  151. data : encrypt( namespace, data || {} ),
  152. uuid : uuid,
  153. geo : p.location || [ 0, 0 ]
  154. },
  155. callback : function(info) {
  156. if (info[0]) return (cb||function(){})(info);
  157. var retry = (wait || 500) * 2;
  158. setTimeout( function() {
  159. send( event, namespace, data, retry, cb );
  160. }, retry > 5500 ? 5500 : retry );
  161. }
  162. });
  163. }
  164. // =====================================================================
  165. // GEO LOCATION DATA (LATITUDE AND LONGITUDE)
  166. // =====================================================================
  167. function locate(callback) {
  168. var callback = callback || function(){};
  169. navigator && navigator.geolocation &&
  170. navigator.geolocation.getCurrentPosition(function(position) {
  171. p.location = [
  172. position.coords.latitude,
  173. position.coords.longitude
  174. ];
  175. callback(p.location);
  176. }) || callback([ 0, 0 ]);
  177. }
  178. // =====================================================================
  179. // CREATE SOCKET
  180. // =====================================================================
  181. function create_socket(namespace) {
  182. if ( namespace !== standard &&
  183. !(standard in namespaces)
  184. ) create_socket(standard);
  185. return namespaces[namespace] = {
  186. namespace : namespace,
  187. users : users[namespace] = {},
  188. user_count : 1,
  189. get_user_list : function(){
  190. return namespaces[namespace].users;
  191. },
  192. get_user_count : function(){
  193. return namespaces[namespace].user_count;
  194. },
  195. emit : function( event, data, receipt ) {
  196. send( event, namespace, data, 0, receipt );
  197. },
  198. send : function( data, receipt ) {
  199. send( 'message', namespace, data, 0, receipt );
  200. },
  201. on : function( event, fn ) {
  202. if (typeof event === 'string')
  203. p.events.bind( namespace + event, fn );
  204. else if (typeof event === 'object')
  205. p.each( event, function(evt) {
  206. p.events.bind( namespace + evt, fn );
  207. } );
  208. },
  209. disconnect : function() {
  210. delete namespaces[namespace];
  211. }
  212. };
  213. }
  214. })();