UNPKG

actionhero

Version:

The reusable, scalable, and quick node.js API server for stateless and stateful applications

278 lines (233 loc) 7.63 kB
var ActionheroWebsocketClient = function (options, client) { var self = this self.callbacks = {} self.id = null self.events = {} self.rooms = [] self.state = 'disconnected' self.options = self.defaults() for (var i in options) { self.options[i] = options[i] } if (client) { self.externalClient = true self.client = client } } if (typeof Primus === 'undefined') { var util = require('util') var EventEmitter = require('events').EventEmitter util.inherits(ActionheroWebsocketClient, EventEmitter) } else { ActionheroWebsocketClient.prototype = new Primus.EventEmitter() } ActionheroWebsocketClient.prototype.defaults = function () { %%DEFAULTS%% } // ////////////// // CONNECTION // // ////////////// ActionheroWebsocketClient.prototype.connect = function (callback) { var self = this self.messageId = self.messageId || 0 if (self.client && self.externalClient !== true) { self.client.end() self.client.removeAllListeners() delete self.client self.client = Primus.connect(self.urlWithSession(), self.options) } else if (self.client && self.externalClient === true) { self.client.end() self.client.open() } else { self.client = Primus.connect(self.urlWithSession(), self.options) } self.client.once('open', function () { self.configure(function (details) { self.state = 'connected' self.emit('connected') if (typeof callback === 'function') { callback(null, details) } }) }) self.client.on('error', function (error) { self.emit('error', error) }) self.client.on('reconnect', function () { self.state = 'connected' self.emit('reconnect') }) self.client.on('reconnecting', function () { self.emit('reconnecting') self.state = 'reconnecting' self.emit('disconnected') }) self.client.on('timeout', function () { self.state = 'timeout' self.emit('timeout') }) self.client.on('close', function () { if (self.state !== 'disconnected') { self.state = 'disconnected' self.emit('disconnected') } }) self.client.on('end', function () { if (self.state !== 'disconnected') { self.state = 'disconnected' self.emit('disconnected') } }) self.client.on('data', function (data) { self.handleMessage(data) }) } ActionheroWebsocketClient.prototype.urlWithSession = function () { var self = this var url = self.options.url if (self.options.cookieKey && self.options.cookieKey.length > 0) { var cookieValue = self.getCookie(self.options.cookieKey) if (cookieValue && cookieValue.length > 0 ) { url += '?' + self.options.cookieKey + '=' + cookieValue } } return url } ActionheroWebsocketClient.prototype.getCookie = function (name) { if (typeof document === 'undefined' || !document.cookie) { return } var match = document.cookie.match(new RegExp(name + '=([^;]+)')) if (match) return match[1] } ActionheroWebsocketClient.prototype.configure = function (callback) { var self = this self.rooms.forEach(function (room) { self.send({event: 'roomAdd', room: room}) }) self.detailsView(function (details) { self.id = details.data.id self.fingerprint = details.data.fingerprint self.rooms = details.data.rooms return callback(details) }) } // ///////////// // MESSAGING // // ///////////// ActionheroWebsocketClient.prototype.send = function (args, callback) { // primus will buffer messages when not connected var self = this self.messageId++ args.messageId = args.params ? (args.params.messageId || args.messageId || self.messageId ) : ( args.messageId || self.messageId ) if (typeof callback === 'function') { self.callbacks[args.messageId] = callback } self.client.write(args) } ActionheroWebsocketClient.prototype.handleMessage = function (message) { var self = this self.emit('message', message) var messageId = message.messageId if (message.context === 'response') { if (typeof self.callbacks[messageId] === 'function') { self.callbacks[messageId](message) } delete self.callbacks[messageId] } else if (message.context === 'user') { self.emit('say', message) } else if (message.context === 'alert') { self.emit('alert', message) } else if (message.welcome && message.context === 'api') { self.welcomeMessage = message.welcome self.emit('welcome', message) } else if (message.context === 'api') { self.emit('api', message) } } // /////////// // ACTIONS // // /////////// ActionheroWebsocketClient.prototype.action = function (action, params, callback) { if (!callback && typeof params === 'function') { callback = params params = null } if (!params) { params = {} } params.action = action if (this.state !== 'connected') { this.actionWeb(params, callback) } else { this.actionWebSocket(params, callback) } } ActionheroWebsocketClient.prototype.actionWeb = function (params, callback) { var xmlhttp = new XMLHttpRequest() xmlhttp.onreadystatechange = function () { var response if (xmlhttp.readyState === 4) { if (xmlhttp.status === 200) { response = JSON.parse(xmlhttp.responseText) } else { try { response = JSON.parse(xmlhttp.responseText) } catch (e) { response = { error: {statusText: xmlhttp.statusText, responseText: xmlhttp.responseText} } } } callback(response) } } var method = (params.httpMethod || 'POST').toUpperCase() var url = this.options.url + this.options.apiPath + '?action=' + params.action if (method === 'GET') { for (var param in params) { if (~['action', 'httpMethod'].indexOf(param)) continue url += '&' + param + '=' + params[param] } } xmlhttp.open(method, url, true) xmlhttp.setRequestHeader('Content-Type', 'application/json') xmlhttp.send(JSON.stringify(params)) } ActionheroWebsocketClient.prototype.actionWebSocket = function (params, callback) { this.send({event: 'action', params: params}, callback) } // //////////// // COMMANDS // // //////////// ActionheroWebsocketClient.prototype.say = function (room, message, callback) { this.send({event: 'say', room: room, message: message}, callback) } ActionheroWebsocketClient.prototype.file = function (file, callback) { this.send({event: 'file', file: file}, callback) } ActionheroWebsocketClient.prototype.detailsView = function (callback) { this.send({event: 'detailsView'}, callback) } ActionheroWebsocketClient.prototype.roomView = function (room, callback) { this.send({event: 'roomView', room: room}, callback) } ActionheroWebsocketClient.prototype.roomAdd = function (room, callback) { var self = this self.send({event: 'roomAdd', room: room}, function (data) { self.configure(function () { if (typeof callback === 'function') { callback(data) } }) }) } ActionheroWebsocketClient.prototype.roomLeave = function (room, callback) { var self = this var index = self.rooms.indexOf(room) if (index > -1) { self.rooms.splice(index, 1) } this.send({event: 'roomLeave', room: room}, function (data) { self.configure(function () { if (typeof callback === 'function') { callback(data) } }) }) } ActionheroWebsocketClient.prototype.documentation = function (callback) { this.send({event: 'documentation'}, callback) } ActionheroWebsocketClient.prototype.disconnect = function () { this.state = 'disconnected' this.client.end() this.emit('disconnected') } // depreciated lowercase name var ActionheroWebsocketClient = ActionheroWebsocketClient; ActionheroWebsocketClient;