PageRenderTime 33ms CodeModel.GetById 9ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

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