/*
* Created by pavel.mash on 14.03.2016.
*/
var _ = require('./libs/lodash/lodash');
var EventEmitter = require('./events');
var
WS_PROTOCOL = 'ubNotifier',
MAX_BUFFERED_COMMANDS = 100;
/**
* @classdesc
*
* WebSocket connection to UnityBase server using ubNotifier protocol.
* The property `supported` indicate `ubNotifier` protocol is supported by server.
* Class mixes an EventEmitter. After got a command from a server the event with command code
* is fired.
*
* The event flow are:
*
* - just after webSocket connection is established `connected` event are fired, when
* - after server accept (verify a user credential) `accepted` are fired - here you can got
* a connectionID of a current connection
* - next is a series of protocol events ( `test_command` for example )
* - in case of protocol error `error` are fired
* - when WebSocket connection are closed `disconnected` event are fired
*
* You do not need to **connect** or **reconnect** a WebSocket manually - UBNotifierWSProtocol recreate
* a WebSocket automatically just after corresponding UBConnection fires a `authorized` event.
*
* For `adminUI` instance of this class is accessible from $App.ubNotifier just after $App.launch
*
* Usage sample:
*
* // Server side
* var notifier = UB.getWSNotifier();
* if (notifier) {
* notifier.broadcast('test_command', {name: 'Homer', like: 'donuts'});
* }
*
* // Client side
* // Will output 'Homer like donuts' after got a `test_command` from server
* $App.ubNotifier.on('test_command', function(params}{
* console.debug(params.name, 'like', params.like)
* }
*
* @class
* @mixes EventEmitter
* @param {UBConnection} connection Warning - only one instance of UBNotifierWSProtocol for a connection is valid.
*/
function UBNotifierWSProtocol(connection){
var notifier = this;
var supported = (connection.supportedWSProtocols.indexOf(WS_PROTOCOL) !== -1);
/**
* Indicate `ubNotifier` protocol is supported by server.
* If not supported all calls to send() will be ignored.
* @type {boolean}
*/
this.supported = supported;
EventEmitter.call(this);
_.assign(this, EventEmitter.prototype);
var
doDebug = function(){},
inDebug = false;
/**
* Enable output all webSocket interaction to console
* @property {boolean} debugMode
*/
Object.defineProperty(notifier, 'debugMode', {
get: function() { return inDebug; },
set: function(newValue) {
if (inDebug !== newValue){
inDebug = newValue;
doDebug = inDebug ? console.debug.bind(console, 'ubNotifier') : function(){}
}
},
enumerable: true,
configurable: true
});
var wsURL = 'ws' + connection.serverUrl.slice(4) + 'ws'; // remove http part, so in case http://.. we got ws://.., in case https://.. -> wss://..
var $ws = null;
var isConnectionAccepted = false;
var bufferedCommands = [];
function _createWSConnection(session){
if (supported) {
$ws = new WebSocket(wsURL + '?SESSION_SIGNATURE=' + session.signature(), 'ubNotifier');
$ws.onopen = function(e){
doDebug('connected to', e.target.url, 'protocol:', e.target.protocol);
/**
* Emitted for {@link UBNotifierWSProtocol} just after WS connection is established, but before it accepted by server.
* Params: url, protocol
* @event connected
*/
notifier.emit('connected', e.target.url, e.target.protocol);
};
$ws.onmessage = function(e){
var msg;
doDebug('Got a raw data', e.data);
try {
msg = JSON.parse(e.data)
} catch (err) {
console.error('ubNotifier: Invalid command from server:', e.data);
}
var
command = msg.command,
params = msg.params,
delayedCmd;
doDebug('Got a command', command, 'with params', params);
switch (command){
case 'accepted': // send a buffered request if any
isConnectionAccepted = true;
while ((delayedCmd = bufferedCommands.shift()) && isConnectionAccepted) {
notifier.sendCommand(delayedCmd.command, delayedCmd.params);
}
bufferedCommands = [];
/**
* If server accept a ubNotifier WS connection this event will be emitted for {@link UBNotifierWSProtocol} with `connectionID` parameter
* @event accepted
*/
notifier.emit('accepted', params.connectionID);
break;
case 'error':
doDebug('Got an error from server', params);
notifier.emit('error', params);
break;
default:
doDebug('Emit event ', command, 'with params', params);
notifier.emit(command, params);
break;
}
};
$ws.onclose = function(e){
isConnectionAccepted = false;
doDebug('Connection closed. Code:', e.code, 'Reason:', e.reason);
notifier.emit('disconnected', e.code, e.reason);
};
}
}
/**
* Sand a command to server
*
* - if WS connection is not accepted yet will buffer the commands and send it just after connection is accepted
* - if `ubNotifier` protocol not supported by server will do nothing
*
* @method
* @param {string} command
* @param {*} params
*/
this.sendCommand = function(command, params){
if (supported){
if (isConnectionAccepted){
$ws.send(JSON.stringify({command: command, params: params}));
} else { //add to delayed buffer
bufferedCommands.push({command: command, params: params});
if (bufferedCommands.length > MAX_BUFFERED_COMMANDS) bufferedCommands.shift();
}
}
};
function _onUBConnectionAuthorized(ubConnection, session, authParams){
isConnectionAccepted = false;
if (connection.supportedWSProtocols.indexOf(WS_PROTOCOL) !== -1){
_createWSConnection(session);
} else {
console.warn('ubNotifier: protocol not supported');
}
}
connection.on('authorized', _onUBConnectionAuthorized);
}
module.exports = UBNotifierWSProtocol;