(function(){ var XPush = (function() { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { http = require('http'); io = require('socket.io-client'); } var SESSION = 'session'; var CHANNEL = 'channel'; var ST = {A:'app',C:'channel',U:'userId',US:'users',D:'deviceId',N:'notiId',S:'server' ,MG:'message',NM:'name',PW:'password',GR:'groups',DT:'datas',MD:'mode',TS:'timestamp' ,SS:'socketId',CD:'createDate',UD:'updateDate'}; var socketOptions ={ transports: ['websocket'] ,'force new connection': true }; var oldDebug; var debug = function() { }; /** * Constructor of Xpush * @module Xpush * @constructor * @param {string} host - address of session server to connect * @param {string} appId - application id * @param {string} [eventHandler] - a function for processing the session event * @param {boolean} [autoInitFlag] - flag for whether to automatically initialize * @example * // Create new Xpush Object * var xpush = new Xpush( 'http://demo.stalk.io:8000', 'sample' ); * @example * // Create new Xpush Object with event Handler * var xpush = new Xpush( 'http://demo.stalk.io:8000', 'sample', function (type, data){ * console.log( " type : ", type ); * console.log( " data : ", data ); * }); */ var XPush = function(host, appId, eventHandler, autoInitFlag){ if(!host){alert('params(1) must have hostname'); return;} if(!appId){alert('params(2) must have appId'); return;} var self = this; self.appId = appId; // applicationKey self._channels = {}; // channel List //self.initStatus; // manage async problem self.headers = {}; // request header //self.liveSockets = {}; // ch : Connection self._sessionConnection; self.maxConnection = 5; self.maxTimeout = 30000; self.channelNameList = []; self.hostname = host; self.receiveMessageStack = []; self.isExistUnread = true; self.autoInitFlag = true; self._userEventNames = []; if( autoInitFlag !=undefined ){ self.autoInitFlag = autoInitFlag; } self.on('newChannel',function(data){ self.channelNameList.push( data.chNm ); }, true); if(eventHandler){ self._isEventHandler = true; self.on('___session_event', eventHandler); } return self; }; XPush.Context = { SIGNUP : '/user/register', LOGIN : '/auth', Channel : '/channel', Signout : '/signout', Message : '/msg', NODE : '/node' }; /** * enable debugging * @name enableDebug * @memberof Xpush * @function * @example * // enable debug * xpush.enableDebug(); */ XPush.prototype.enableDebug = function(){ if( oldDebug ){ return; } if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { debug = Function.prototype.bind.call(console.log, console); } else { if (window.console) { if (Function.prototype.bind) { debug = Function.prototype.bind.call(console.log, console); } else { debug = function() { Function.prototype.apply.call(console.log, console, arguments); }; } } } }; /** * disable debugging * @name disableDebug * @memberof Xpush * @function * @example * // disable debug * xpush.disableDebug(); */ XPush.prototype.disableDebug = function(){ // Init debug funciton debug = function(){ }; oldDebug = undefined; }; /** * Register user using the userId and password. * @name signup * @memberof Xpush * @function * @param {string} userId - User Id * @param {string} password - Password * @param {string} [deviceId=WEB] - Device Id * @param {callback} cb - callback function to be performed after register * @example * // Add new user * xpush.signup( 'james', '1234', function(err,data){ * console.log('register success : ' + data); * }); */ XPush.prototype.signup = function(userId, password, deviceId, cb){ var self = this; if(typeof(deviceId) == 'function' && !cb){ cb = deviceId; deviceId = 'WEB'; } var sendData = {A:self.appId , U: userId, PW: password, D: deviceId}; self.ajax( XPush.Context.SIGNUP , 'POST', sendData, cb); }; /** * Login user using the userId and password. * @name login * @memberof Xpush * @function * @param {string} userId - User Id * @param {string} password - Password * @param {string} [deviceId=WEB] - Device Id * @param {string} [mode] - mode * @param {callback} cb - callback function to be performed after login * @example * * xpush.login( 'james', '1234', function(err,data){ * console.log('register success : ', data); * }); * @example * // login with deviceId * xpush.login( 'james', '1234', 'android', function(err,data){ * console.log('login success : ', data); * }); */ XPush.prototype.login = function(userId, password, deviceId, mode, cb){ var self = this; if(typeof(deviceId) == 'function' && !mode && !cb){ cb = deviceId; deviceId = 'WEB'; } if(typeof(mode) == 'function' && !cb){ cb = mode; mode = undefined; } self.userId = userId; self.deviceId = deviceId; var sendData = {A: self.appId, U: userId, PW: password, D: deviceId}; if(mode) sendData.MD = mode; self.ajax( XPush.Context.LOGIN , 'POST', sendData, function(err, result){ if(err){ if(cb) cb(err, result); return; } if(result.status == 'ok'){ // result.result = {"token":"HS6pNwzBoK","server":"215","serverUrl":"http://www.notdol.com:9990"}; var c = self._sessionConnection = new Connection(self, SESSION, result.result); c.connect(function(){ debug("xpush : login end", self.userId); self._initSessionSocket(self._sessionConnection._socket, function(){ if(cb) cb(result.message, result.result); // @ TODO from yohan. }); }); }else{ if(cb) cb(result.message); alert('xpush : login error'+ result.message); } }); }; /** * Set the userId and deviceId to current xpush object. only if the session socket is already connected * @name setSessionInfo * @memberof Xpush * @function * @param {string} userId - User Id * @param {string} [deviceId] - Device Id * @param {callback} cb - callback function to be performed after set * @example * // Set session info * xpush.setSessionInfo( 'james', function(){} ); * @example * // Set session info with deviceId * xpush.setSessionInfo( 'james', 'WEB', function(){} ); */ XPush.prototype.setSessionInfo = function(userId, deviceId, cb){ var self = this; if(typeof(deviceId) == 'function' && !cb){ cb = deviceId; deviceId = 'WEB'; } self.userId = userId; self.deviceId = deviceId; cb(); }; /** * Disconnect the session socket and channel socket. * @name logout * @memberof Xpush * @function * @example * // logout * xpush.logout(); */ XPush.prototype.logout = function(){ var self = this; if( self != undefined ) { // Disconnect session connection if( self._sessionConnection != undefined ){ self._sessionConnection.disconnect(); } // Disconnect channel connections if( self._channels != undefined ){ for( var key in self._channels ){ if( self._channels[key]._connected ){ self._channels[key].disconnect(); } } } } }; /** * Generates a new channel. * @name createChannel * @memberof Xpush * @function * @param {string} users - userId array to invite channel * @param {string} [channel] - Channel Id * @param {Object} [datas] - JSON for additional channel information * @param {callback} cb - callback function to be performed after generate * @example * // create random channel without data * xpush.createChannel(['james', 'notdol'], function(err, data){ * console.log( 'create channel success : ', data); * }); * @example * // create a channel without data * xpush.createChannel(['james'], 'channel02', function(err,data){ * console.log( 'create channel success : ', data); * }); * @example * // create a channel with data * xpush.createChannel(['james'], 'channel03', {'NM':'james'}, function(err,data){ * console.log( 'create channel success : ', data); * }); */ XPush.prototype.createChannel = function(users, channel, datas, cb){ var self = this; var channels = self._channels; if( arguments.length === 3 ){ // users, channel, cb if( typeof(arguments[1]) == 'string' && typeof(arguments[2]) == 'function' ){ cb = datas; datas = {}; } else if ( typeof(arguments[1]) == 'object' && typeof(arguments[2]) == 'function' ){ cb = datas; datas = channel; channel = undefined; } } else if(typeof(channel) == 'function' && !datas && !cb){ cb = channel; channel = undefined; datas = {}; } var newChannel; var channelNm = channel; //Add logined user if not in users if( users.indexOf(self.userId) < 0 ){ users.push(self.userId); } self.sEmit('channel-create',{C: channel, U: users, DT:datas},function(err, result){ //_id: "53b039e6a2f41316d7046732" //app: "stalk-io" //channel: "b14qQ6wI" //created: "2014-06-29T16:08:06.684Z" if(err && err != 'WARN-EXISTED') { if(cb){ cb(err, result); } } channelNm = result.C || channelNm; self._getChannelInfo(channelNm,function(err,data){ //channel , seq, server.channel,name,url if(err){ debug(" == node channel " ,err); }else if ( data.status == 'ok'){ newChannel.setServerInfo(data.result); //newChannel.chNm = channelNm; channels[channelNm] = newChannel; //if(oldChNm){ // delete channels[oldChNm]; //} if(cb)cb(null, channelNm); } }); }); newChannel = self._makeChannel(channelNm); return newChannel; }; /** * Generate a new `CHANNEL_ONLY` channel. * @name createSimpleChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {Object} [userObj] - UserObject( U : userID, D : deviceId ) * @param {callback} cb - callback function to be performed after generate * @example * // create simple channel without userObject * xpush.createSimpleChannel('channel01', function(){ * console.log( 'create simple channel success' ); * }); * @example * // create simple channel with userObject * xpush.createSimpleChannel('channel02', {'U':'james','D':'WEB'}, function(){ * console.log( 'create simple channel success' ); * }); */ XPush.prototype.createSimpleChannel = function(channel, userObj, cb){ var self = this; var ch = self._makeChannel(channel); self._getChannelInfo(channel,function(err,data){ if(err){ debug(" == node channel " ,err); if(cb) cb(err); }else if ( data.status == 'ok'){ if( typeof(userObj) == 'function' && !cb ){ cb = userObj; userObj = undefined; } if(userObj){ self.userId = userObj.U || 'someone'; self.deviceId = userObj.D || 'WEB'; }else { self.userId = 'someone'; self.deviceId = 'WEB'; } ch.info = data.result; ch._server = {serverUrl : data.result.server.url}; ch.chNm = data.result.channel; ch.connect(function(){ if(cb) cb(); }, 'CHANNEL_ONLY'); } }); return ch; }; /** * Query the channel list from the server. * @name getChannels * @memberof Xpush * @function * @param {callback} cb - callback function to be performed after query * @example * // Get channels * xpush.getChannels(function(err,datas){ * console.log( 'channels : ' + datas ); * }); */ XPush.prototype.getChannels = function(cb){ var self = this; debug("xpush : getChannels ",self.userId); self.sEmit('channel-list',function(err, result){ //app(A), channel(C), created(CD) , users(US) debug("xpush : getChannels end ",result); ['A','C','CD','US'].forEach(function(item){ UTILS.changeKey(result,item); }); if(result.length > 0){ result.forEach(function(r){ ['D','N','U'].forEach(function(item){ UTILS.changeKey(r.users,item); }); }); } cb(err,result); }); }; /** * Modify the channel information of the server. * @name updateChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {Object} query - mongo DB query form as a JSON * @param {callback} cb - callback function to be performed after modify * // update channel * @example * xpush.updateChannel( 'channel02', { $set:{'DT':{'NM':'notdol1'}}}, function(err, result){ * console.log( 'result : ', result ); * }); */ XPush.prototype.updateChannel = function(channel, query, cb){ var self = this; var param = { 'A': self.appId, 'C': channel, 'Q' : query }; self.sEmit('channel-update', param, function(err, result){ //app(A), channel(C), created(CD) , users(US) debug("xpush : channel-update end ",result); cb(err,result); }); }; /** * The channel list where the user is connected to the current channel will be viewed in redis. * @name getChannelsActive * @memberof Xpush * @function * @param {Object} data - ( 'key': '' ) * @param {callback} cb - callback function to be performed after retrieve * @example * // Retrieve channels that start with channel * xpush.getChannelsActive( {'key':'channel*'}, function(results){ * console.log( 'results : ', results ); * }); */ XPush.prototype.getChannelsActive = function(data, cb){ //data.key(option) var self = this; self.sEmit('channel-list-active',data, function(err, result){ //app, channel, created cb(err, result); }); }; /** * Get the current channel information in xpush object. * @name getChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @return {Object} return Channel Object * @example * var channel01 = xpush.getChannel('channel01'); */ XPush.prototype.getChannel = function(channel){ var self = this; var channels = self._channels; for(var k in channels){ if(k == channel) return channels[k]; } return undefined; }; /** * Query the channel information of the server. * @name getChannelData * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {callback} cb - callback function to be performed after query * @example * xpush.getChannelData( channel, function(err,data){ * console.log( 'retrieve channel success : ', data); * }); */ XPush.prototype.getChannelData = function(channel, cb){ var self = this; self.sEmit('channel-get', {C: channel, U: /*userId*/{} }, function(err, result){ if(cb) cb(err,result); }); }; /** * Join to the channel. * @name joinChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {Object} param - JSON Data ( U, DT ) * @param {callback} cb - callback function to be performed after join * @example * xpush.joinChannel( 'channel03', {'U':['notdol']}, function(result){ * console.log( 'result : ', result); * }); */ XPush.prototype.joinChannel = function(channel, param, cb){ var self = this; self.getChannelAsync(channel, function (err, ch){ ch.joinChannel( param, function( data ){ cb( data ); }); }); }; /** * Exit from the channel * @name exitChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {callback} cb - callback function to be performed after exit * @example * xpush.exitChannel( 'channel03', function(err, result){ * console.log( 'result : ', result); * }); */ XPush.prototype.exitChannel = function(channel, cb){ var self = this; self.sEmit('channel-exit', {C: channel}, function(err, result){ if(cb) cb(err,result); }); }; /** * Bring the channel information asynchronously. If the channel information is not present in the object, it queries the channel information from the server. * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {callback} cb - callback function to be performed after get * @example * xpush.getChannelAsync( 'channel03', function(err, result){ * console.log( 'result : ', result); * }); */ XPush.prototype.getChannelAsync = function(channel, cb){ var self = this; var ch = self.getChannel(channel); if(!ch){ self._channels[channel] = ch; ch = self._makeChannel(channel); self._getChannelInfo(channel,function(err,data){ if(err){ debug(" == node channel " ,err); cb(err); }else if ( data.status == 'ok'){ ch.setServerInfo(data.result, function(){ cb(false, ch); }); } }); }else{ cb(false, ch); } }; /** * Upload the file DOM object using the socket stream to * @name uploadStream * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {Object} inputObj - JSON Objec( 'file' : file DOM Oject for upload, 'type' : '' ) * @param {function} fnPrg - function to show the upload progress * @param {callback} fnCallback - callback function to be performed after upload * @example * var fileObj = document.getElementById('file'); * xpush.uploadStream( 'channel03', { * file: fileObj * }, function(data, idx){ * console.log( 'progress : ' + data ); * }, function(data,idx){ * console.log( 'upload result : ' + data ); * }); */ XPush.prototype.uploadStream = function(channel, inputObj, fnPrg, fnCallback){ var self = this; self.getChannelAsync(channel, function (err, ch){ var blobs = []; var streams = []; for(var i=0; i<inputObj.file.files.length; i++){ var file = inputObj.file.files[i]; var bufferSize = 128; // larger than 1M if( file.size > ( 1024 * 1024 ) ){ bufferSize = 256; } else if ( file.size > ( 4 * 1024 * 1024 ) ){ bufferSize = 512; } var size = 0; streams[i] = ss.createStream({highWaterMark: bufferSize * 1024}); blobs[i] = ss.createBlobReadStream(file, {highWaterMark: bufferSize * 1024}); blobs[i].on('data', function(chunk) { size += chunk.length; fnPrg(Math.floor(size / file.size * 100), i); }); var _data = {}; _data.orgName = file.name; if(inputObj.overwrite) _data.name = file.name; if(inputObj.type) _data.type = inputObj.type; ch.upload(streams[i], _data, function(result){ fnCallback(result, i); }); blobs[i].pipe(streams[i]); } }); }; /** * Upload files using the REST API because the mobile is not supported file dom object * @name uploadFile * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {string} fileUri - fileUri to upload * @param {Object} inputObj - JSON Objec( 'type' : '', 'name' : Original File name ) * @param {function} fnPrg - function to show the upload progress * @param {callback} fnCallback - callback function to be performed after upload * @example * xpush.uploadFile('channelId', 'content://media/external/images/media/636', * {type : 'image', name:'image.png' }, * function ( data ){ * console.log( data ); * }, * function (data){ * console.log( data.response ); * }); */ XPush.prototype.uploadFile = function(channel, fileUri, inputObj, fnPrg, fnCallback){ var self = this; self.getChannelAsync(channel, function(err, ch){ if(window.FileTransfer && window.FileUploadOptions){ var url = ch._server.serverUrl+'/upload'; var options = new FileUploadOptions(); options.fileKey="post"; options.chunkedMode = false; options.params = { 'key1': 'VAL1', 'key2': 'VAL2' }; options.headers = { 'XP-A': self.appId, 'XP-C': channel, 'XP-U': JSON.stringify({ U: self.userId, D: self.deviceId }) //[U]^[D]^[TK] @ TODO add user token }; options.headers['XP-FU-org'] = inputObj.name; if(inputObj.overwrite) options.headers['XP-FU-nm'] = inputObj.name; if(inputObj.type) options.headers['XP-FU-tp'] = inputObj.type; var ft = new FileTransfer(); if( fnPrg != undefined ){ ft.onprogress = function(progressEvent) { if (progressEvent.lengthComputable) { var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100); fnPrg( perc); } }; } ft.upload(fileUri, encodeURI(url), function(data){ fnCallback(data); //$scope.picData = FILE_URI; //$scope.$apply(); }, function(e) { debug("On fail " + e); }, options); } }); }; /** * Get the file url which is uploaded completely * @name getFileUrl * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {string} fileName - received return name after file uploading * @return {string} url where you can download the file * @example * var url = xpush.getFileUrl( 'channel03', data.result.name ) */ XPush.prototype.getFileUrl = function(channel, fileName){ var self = this; var ch = self.getChannel(channel); var result = ch.info.server.url + '/download/' + ch._xpush.appId + '/'+ch.info.channel + '/'+ch._xpush.userId + '/'+ch._socket.io.engine.id + '/'+fileName; return result; }; /** * If the channel is a connection object and returns a channel connection, otherwise create a new `Connection`. * @private * @function * @param {string} channel - Channel Id * @return {Connection} Connect Object */ XPush.prototype._makeChannel = function(channel){ var self = this; debug('xpush : connection _makeChannel ',channel); for( var key in self._channels ){ if( key == channel && self._channels[key] != undefined && self._channels[key]._connected ){ return self._channels[key]; } } var ch = new Connection(self,CHANNEL); if(channel) { ch.channel = channel; self._channels[channel] = ch; } return ch; }; XPush.prototype.calcChannel = function(ch){ var self = this; if(self._channels.length >= self.maxConnection){ self._deleteChannel(self._channels[self._channels.length-1]); } /* if(ch){ if(self._channels[0] != ch){ for(var i = 1 ; i < self._channels.length ; i++){ if( self._channels[i] == ch){ self._channels.unshift( self._channels.splice(i,1)); } } } } */ }; /** * Disconnect the channel socket, connection objects are deleted from managed list. * @private * @function * @param {Object} channel - Channel Id */ XPush.prototype._deleteChannel = function(channelObject){ var self = this; for(var k in self._channels){ if(self._channels[k] == channelObject){ self._channels[k].disconnect(); delete self._channels[k]; break; } } }; /** * confirm whether channel is exist or not * @name isExistChannel * @memberof Xpush * @function * @param {string} channel - Channel Id * @return {boolean} * @example * var isExist = xpush.isExistChannel('channel03'); */ XPush.prototype.isExistChannel = function(channel){ var self = this; for(var i = 0 ; i < self.channelNameList.length ; i++){ if(self.channelNameList[i] == channel){ return true; } } return false; }; /** * Query the user list from the server. * @name getUserList * @memberof Xpush * @function * @param {Object} [params] - param for search user. * @param {function} cb - callback function to be performed after query * @example * xpush.getUserList( {'page':{'num':1,'size':10} },function(err, users){ * console.log( users ); * }); */ XPush.prototype.getUserList = function(params, cb){ if(typeof(params) == 'function'){ cb = params; params = {}; } params = params == undefined ? {}: params; var self = this; debug("xpush : getUsertList ",params); self.sEmit('user-list' , params, function(err, result){ if(cb) cb(err, result.users, result.count); }); }; /** * Query the user list from the server. Paging is possible. * @name queryUser * @memberof Xpush * @function * @param {Object} _params - ( query, column ) * @param {callback} cb - callback function to be performed after query * @example * var param = {query : {'DT.NM':'james'}, column: { U: 1, DT: 1, _id: 0 } }; * xpush.queryUser( param, function( err, userArray, count ){ * console.log( userArray ); * }); */ XPush.prototype.queryUser = function(_params, cb){ var self = this; if(!_params.query) { console.error('Query User', 'query is not existed.'); }; if(!_params.column) { console.error('Query User', 'column is not existed.'); }; var params = { query : _params.query, column: _params.column }; if(_params.options) { params['options'] = _params.options; } else { params['options'] = {}; } debug("xpush : queryUser ",params); self.sEmit('user-query' , params, function(err, result){ if(cb) cb(err, result.users, result.count); }); }; /** * Transmits the data. * @name send * @memberof Xpush * @function * @param {string} channel - Channel Id * @param {string} name - EventName * @param {Object} data - String or JSON object to Send * @example * xpush.send( 'ch01', 'message', {'MG':'Hello world'} ); */ XPush.prototype.send = function(channel, name, data){ var self = this; self.getChannelAsync(channel, function (err, ch){ ch.send(name,data); }); }; /** * Query the unread server message. * After inquiry, call the `message-received` API to delete the viewed message. * @name getUnreadMessage * @memberof Xpush * @function * @param {callback} cb - callback function to be performed after query * @example * xpush.getUnreadMessage( function(err, result){ * console.log( result ); * }); */ XPush.prototype.getUnreadMessage = function(cb){ var self = this; debug("xpush : getUnreadMessage ",self.userId); self.sEmit('message-unread',function(err, result){ //app, channel, created debug("xpush : getUnreadMessage end ", result); if(result && result.length > 0){ result.sort(UTILS.messageTimeSort); } self.isExistUnread = false; self.sEmit('message-received'); cb(err, result); }); }; /** * Get the channel server information to connect * @private * @function * @param {string} channel - Channel Id * @param {callback} cb - callback function to be performed after get */ XPush.prototype._getChannelInfo = function(channel, cb){ var self = this; debug("xpush : _getChannelInfo ",channel); self.ajax( XPush.Context.NODE+'/'+self.appId+'/'+channel , 'GET', {}, cb); }; /** * Query the user list on the server that contains the group. * @name getGroupUsers * @memberof Xpush * @function * @param {string} groupId - Find groupId * @param {callback} cb - callback function to be performed after query * @example * xpush.getGroupUsers( 'james', function( err, users ){ * console.log( users ); * )}; */ XPush.prototype.getGroupUsers = function(groupId,cb){ var self = this; if(typeof(arguments[0]) == 'function') {cb = arguments[0]; groupId = undefined;} groupId = groupId ? groupId : self.userId; self.sEmit('group-list',{'GR': groupId}, function(err,result){ cb(err,result); }); }; /** * Add a group id to one or multiple users. * @name addUserToGroup * @memberof Xpush * @function * @param {string} [groupId] - userId * @param {array} userIds - users array to add * @param {callback} cb - callback function to be performed after add * @example * xpush.addUserToGroup( 'james', ['notdol','john'], function( err, result ){ * console.log( result ); * )}; */ XPush.prototype.addUserToGroup = function(groupId, userIds, cb){ var self = this; if(typeof(arguments[1]) == 'function') {cb = arguments[1]; userIds = groupId; groupId = undefined;} groupId = groupId ? groupId : self.userId; userIds = userIds ? userIds : []; self.sEmit('group-add',{'GR': groupId, 'U': userIds}, function(err,result){ //app, channel, created cb(err,result); }); }; /** * Delete the user from the group. * @name removeUserFromGroup * @memberof Xpush * @function * @param {string} [groupId] - userId * @param {string} userId - user's ID to delete * @param {callback} cb - callback function to be performed after delete * @example * xpush.removeUserFromGroup( 'james', 'notdol', function( err, result ){ * console.log( result ); * )}; */ XPush.prototype.removeUserFromGroup = function(groupId, userId, cb){ var self = this; if(typeof(arguments[1]) == 'function') {cb = arguments[1]; userId = groupId; groupId = undefined;} groupId = groupId ? groupId : self.userId; self.sEmit('group-remove',{'GR': groupId, 'U': userId}, function(err, result){ cb(err,result); }); }; /** XPush.prototype.getGroups = function(){ // not defined yet }; XPush.prototype.signout = function(cb){ //session end var self = this; var sendData = { }; self.ajax( XPush.Context.Signout , 'POST', sendData, cb); }; */ /** * Add an event after the session socket initialization. if it is `autoInitFlag` true, get the channel information and the unread message. * @private * @function * @param {Object} socket.io * @param {callback} cb - callback function to be performed after init */ XPush.prototype._initSessionSocket = function(socket,cb){ var self = this; socket.on('_event',function(data){ debug('xpush : session receive ', data.event, data.C,data.NM,data.DT, self.userId); // data.event = NOTIFICATION // channel,name, timestamp, data= {} switch(data.event){ case 'NOTIFICATION': var ch = self.getChannel(data.C); // if `autoInitFlag` is true, make channel automatically if( self.autoInitFlag ){ if(!ch){ ch = self._makeChannel(data.C); self._getChannelInfo(data.C,function(err,data){ if(err){ debug(" == node channel " ,err); }else if ( data.status == 'ok'){ ch.setServerInfo(data.result); } }); //self.emit('channel-created', {ch: ch, chNm: data.channel}); if(!self.isExistChannel(data.channel)) { self.emit('newChannel', ch); } } ch.emit(data.NM , data.DT); } self.emit(data.NM, data.C, data.NM, data.DT); break; case 'CONNECT' : self.emit('___session_event', 'SESSION', data); break; case 'DISCONNECT' : self.emit('___session_event', 'SESSION', data); break; case 'LOGOUT' : self.emit('___session_event', 'LOGOUT', data); break; } }); socket.on('channel',function(data){ debug('xpush : session receive ', 'channel', data, self.userId); switch(data.event){ case 'UPDATE': // event: update , app,channel,server,count break; case 'REMOVE' : // event: remove , app,channel break; } }); socket.on('connected',function(){ debug('xpush : session receive ', CHANNEL, arguments, self.userId); }); // if `autoInitFlag` is true, get channels if( self.autoInitFlag ){ self.getChannels(function(err,data){ self.channelNameList = data; self.getUnreadMessage(function(err, data){ if(data && data.length > 0 ){ for(var i = data.length-1 ; i >= 0; i--){ data[i].MG.DT = JSON.parse(data[i].MG.DT); self.receiveMessageStack.unshift([data[i].NM, data[i].MG.DT.C, data[i].NM, data[i].MG.DT]); } self.isExistUnread = false; while(self.receiveMessageStack.length > 0 ){ var t = self.receiveMessageStack.shift(); self.emit.apply(self, t ); } if(cb) cb(); }else{ if(cb) cb(); } }); }); } else { if(cb) cb(); } socket.on('disconnect',function(){ self.isExistUnread = true; }); }; var _rest = function( context, method, data, headers, cb){ var self = this; if(typeof(headers) == 'function' && !cb){ cb = headers; headers = undefined; } var hostname = self.hostname.replace( "http://", "" ); if( hostname.indexOf( ":" ) > 0 ) hostname = hostname.split(":")[0]; var options = { host: hostname, port:8000, path: context, method: method }; if( headers ){ options.headers = headers; } else { options.headers = {}; } options.headers['Content-Type'] = 'application/json'; var result = ''; var request = http.request( options, function(res) { res.setEncoding('utf8'); res.on("data", function(chunk) { result = result + chunk; }); res.on("end", function() { var r = JSON.parse(result); if(r.status != 'ok'){ cb(r.status,r.message); }else{ cb(null,r); } }); }).on('error', function(e) { debug("ajax error: " + e.message); cb('',result); }); if( method.toLowerCase() !== 'GET'.toLowerCase() ){ request.write(JSON.stringify(data)); } request.end(); } var _ajax = function( context, method, data, headers, cb){ var self = this; if(typeof(headers) == 'function' && !cb){ cb = headers; headers = false; } var xhr; try{ xhr = new XMLHttpRequest(); }catch (e){ try{ xhr = new XDomainRequest(); } catch (e){ try{ xhr = new ActiveXObject('Msxml2.XMLHTTP'); }catch (e){ try{ xhr = new ActiveXObject('Microsoft.XMLHTTP'); }catch (e){ console.error('\nYour browser is not compatible with XPUSH AJAX'); } } } } var _url = self.hostname+context; var param = Object.keys(data).map(function(k) { return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]); }).join('&'); method = (method.toLowerCase() == "get") ? "GET":"POST"; param = (param == null || param == "") ? null : param; if(method == "GET" && param != null){ _url = _url + "?" + param; } xhr.open(method, _url, true); xhr.onreadystatechange = function() { if(xhr.readyState < 4) { return; } if(xhr.status !== 200) { debug("xpush : ajax error", self.hostname+context,param); cb(xhr.status,{}); } if(xhr.readyState === 4) { var r = JSON.parse(xhr.responseText); if(r.status != 'ok'){ cb(r.status,r.message); }else{ cb(null,r); } } }; debug("xpush : ajax ", self.hostname+context,method,param); if(headers) { for (var key in headers) { if (headers.hasOwnProperty(key)) { xhr.setRequestHeader(key, headers[key]); } } } xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send( (method == "POST") ? param : null); return; }; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { XPush.prototype.ajax = _rest; } else { XPush.prototype.ajax = _ajax; } /** * Generate an event by using the session socket. * @private * @function * @param {string} socketě event key * @param {Object} [params] - object to send * @param {callback} cb - callback function to be performed after event occured */ XPush.prototype.sEmit = function(key, params, cb){ var self = this; var returnFunction = function(result){ if(result.status == 'ok'){ cb(null, result.result); }else{ if(result.status.indexOf('WARN') == 0){ debug("xpush : ", key, result.status, result.message); }else{ debug("xpush : ", key, result.status, result.message); } cb(result.status, result.message); } }; if( typeof(arguments[1]) == 'function' ){ cb = params; self._sessionConnection._socket.emit(key, returnFunction); }else{ self._sessionConnection._socket.emit(key, params, returnFunction); } return; }; /** * Register the event and function to the event stack. The specified function is event is called the event. * @name on * @memberof Xpush * @function * @param {string} event key * @param {function} function * @example * xpush.on( 'message', function(channel, name, data){ * console.log( channel, name, data ); * }); */ XPush.prototype.on = function(event, fct, systemFlag){ var self = this; if( !systemFlag ){ self._userEventNames.push( event ); } self._events = self._events || {}; self._events[event] = self._events[event] || []; self._events[event].push(fct); if( self._sessionConnection ){ self._sessionConnection.attchOnEvent( event ); } for ( var key in self._channels ){ self._channels[key].attchOnEvent( event ); } }; /** * Removes the event and function in the event stack * @name off * @memberof Xpush * @function * @param {string} event key * @param {function} function * @example * xpush.off( 'message', function(channel, name, data){ * console.log( channel, name, data ); * }); */ XPush.prototype.off = function(event, fct){ var self = this; self._events = self._events || {}; if( event in self._events === false ) return; self._events[event].splice(self._events[event].indexOf(fct), 1); }; /** * Remove all event on the event stack. * @name off * @memberof Xpush * @function * @param {string} event key * @param {function} function * @example * xpush.clearEvent(); */ XPush.prototype.clearEvent = function(){ var self = this; var sessionEvent = self._events['___session_event']; self._events = {}; self._events['___session_event'] = sessionEvent; self._userEventNames = []; }; /** * Call a function that is registered in the event stack. * When unread message exists, message will be stacked without causing the function of that event soon because it is being initialized status. * @private * @memberof Xpush * @function * @param {string} event key */ XPush.prototype.emit = function(event){ var self = this; if(self.isExistUnread) { self.receiveMessageStack.push(arguments); }else{ self._events = self._events || {}; if( event in self._events === false ) return; for(var i = 0; i < self._events[event].length; i++){ debug("xpush : test ",arguments); self._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); } } }; /** * Represents a Connection * @module Connection * @constructor * @param {Xpush} Object - Xpush obejct * @param {string} type - 'session' or channel' * @param {string} server - Server Url to connect */ var Connection = function(xpush , type, server){ this._xpush = xpush; this._server = server; this._type = type; if(this._type == SESSION){ this.chNm = SESSION; } this._socketStatus; // disconnected, connected this._socket; this.checkTimer; this.info; this.messageStack = []; this.isFirtConnect = true; this._connected = false; this.timeout = 30000; //self.on('received', function(data){ //self._xpush.calcChannel(self); //}); return this; }; /** * Check connectionTimeout * @name checkConnectionTimeout * @memberof Connection * @function * @param {string} b - Server Url to connect */ Connection.prototype.checkConnectionTimeout = function(b){ var self = this; if(self.checkTimer) clearTimeout(self.checkTimer); if(b){ self.checkTimer = setTimeout(function(){ self._socket.disconnect(); }, self.timeout); } }; /** * Set server url and connect to the server. * @name setServerInfo * @memberof Connection * @function * @param {Object} info - Server Url to connect * @param {callback} cb - setServerInfoCallback */ Connection.prototype.setServerInfo = function(info,cb){ debug("xpush : setServerInfo ", info); var self = this; self.info = info; self._server = {serverUrl : info.server.url}; self.chNm = info.channel; self.connect(function(){ debug("xpush : setServerInfo end ", arguments,self._xpush.userId, self.chNm); //self.connectionCallback(); if(cb) cb(); }); }; /** * Connect to the server. * @name setServerInfo * @memberof Connection * @function * @param {callback} cb - connectCallback * @param {string} mode - Optional. `CHANANEL_ONLY` */ Connection.prototype.connect = function(cb, mode){ var self = this; var query = 'A='+self._xpush.appId+'&'+ 'U='+self._xpush.userId+'&'+ 'D='+self._xpush.deviceId+'&'+ 'TK='+self._server.token; //'mode=CHANNEL_ONLY'; if(self._type == CHANNEL){ query = 'A='+self._xpush.appId+'&'+ 'C='+self.chNm+'&'+ 'U='+self._xpush.userId+'&'+ 'D='+self._xpush.deviceId+'&'+ 'S='+self.info.server.name; if(mode){ if(mode == 'CHANNEL_ONLY'){ self._xpush.isExistUnread = false; } query = query +'&MD='+ mode; } } self._socket = io.connect(self._server.serverUrl+'/'+self._type+'?'+query, socketOptions); debug( 'xpush : socketconnect', self._server.serverUrl+'/'+self._type+'?'+query); self._socket.on('connect', function(){ debug( 'channel connection completed' ); while(self.messageStack.length > 0 ){ var t = self.messageStack.shift(); //.self.send(t.NM, t.DT); self._socket.emit('send', {NM: t.NM , DT: t.DT}); } self._connected = true; if(!self.isFirtConnect) return; self.isFirtConnect = false; self.connectionCallback(cb); }); self._socket.on('disconnect',function(){ self._connected = false; }); }; /** * The function is occured when socket is connected. * @name connectionCallback * @memberof Connection * @function * @param {callback} cb - connectionCallback */ Connection.prototype.connectionCallback = function(cb){ var self = this; debug("xpush : connection ",'connectionCallback',self._type, self._xpush.userId,self.chNm); for( var key in self._xpush._userEventNames ){ self.attchOnEvent( self._xpush._userEventNames[key] ); } if(self._xpush._isEventHandler) { self._socket.on('_event',function(data){ switch(data.event){ case 'CONNECTION' : self._xpush.emit('___session_event', 'CHANNEL', data); break; case 'DISCONNECT' : self._xpush.emit('___session_event', 'CHANNEL', data); break; } }); } if(cb)cb(); }; /** * Close the socket connection. * @name disconnect * @memberof Connection * @function */ Connection.prototype.disconnect = function(){ debug("xpush : socketdisconnect ", this.chNm, this._xpush.userId); this._socket.disconnect(); }; /** * If socket is connected, send data right away, * @name send * @memberof Connection * @param {string} name - Event name * @param {object} data - JSON data * @param {callback} cb - sendCallback * @function */ Connection.prototype.send = function(name, data, cb){ var self = this; if(self._connected){ self._socket.emit('send', {NM: name , DT: data}); }else{ self.messageStack.push({NM: name, DT: data}); } }; /** * If socket is connected, join the channel * @name joinChannel * @memberof Connection * @param {object} data - JSON data * @param {callback} cb - joinChannelCallback * @function */ Connection.prototype.joinChannel = function(param, cb){ var self = this; if(self._socket.connected){ self._socket.emit('join', param, function( data ){ cb( data ); }); } }; /** * Upload the stream * @name upload * @memberof Connection * @param {object} stream - stream object * @param {object} data - file info data ( 'orgName', 'name', 'type') * @param {callback} cb - uploadCallback * @function */ Connection.prototype.upload = function(stream, data, cb){ var self = this; if(self._socket.connected){ ss(self._socket).emit('file-upload', stream, data, cb); } }; /** * Add on event in current socket * @name attchOnEvent * @memberof Connection * @function * @param {string} event key */ Connection.prototype.attchOnEvent = function(eventNm){ var self = this; if(self._socket){ self._socket.on( eventNm ,function(data){ debug("xpush : channel receive, " +eventNm, self.chNm, data, self._xpush.userId); self._xpush.emit(eventNm, self.chNm, eventNm , data); }); } }; /** * Stack the function into event array. The function will excute when an event occur. * @name on * @memberof Connection * @function * @param {string} event key * @param {function} function */ Connection.prototype.on = function(event, fct){ var self = this; self._events = self._events || {}; self._events[event] = self._events[event] || []; self._events[event].push(fct); }; /** * Remove the function at event array * @name off * @memberof Connection * @function * @param {string} event key * @param {function} function */ Connection.prototype.off = function(event, fct){ var self = this; self._events = self._events || {}; if( event in self._events === false ) return; self._events[event].splice(self._events[event].indexOf(fct), 1); }; /** * Apply the event * @name emit * @memberof Connection * @function * @param {string} event key */ Connection.prototype.emit = function(event /* , args... */){ var self = this; self._events = self._events || {}; if( event in self._events === false ) return; for(var i = 0; i < self._events[event].length; i++){ self._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); } }; var UTILS = {}; UTILS.messageTimeSort = function(a,b){ // created data return a.created > b.created; }; UTILS.changeKey = function(data, key){ if(data instanceof Array){ data.forEach(function(d){ d[ ST[key] ] = d[key]; delete d[key]; }); }else{ data[ ST[key] ] = data[key]; delete data[key]; } }; return XPush; })(); if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = XPush; } else { if (typeof define === 'function' && define.amd) { define([], function() { return XPush; }); } else { window.XPush = XPush; } } })();