vendor/assets/javascripts/centrifuge.js in centrifuge-0.0.4 vs vendor/assets/javascripts/centrifuge.js in centrifuge-0.0.5
- old
+ new
@@ -1,9 +1,6 @@
-/**
- * Centrifuge javascript client
- * v0.5.2
- */
+// v0.7.0
;(function () {
'use strict';
/**
* Oliver Caldwell
@@ -562,14 +559,106 @@
} catch (x) {
return undefined;
}
}
+ // http://krasimirtsonev.com/blog/article/Cross-browser-handling-of-Ajax-requests-in-absurdjs
+ var AJAX = {
+ request: function(url, method, ops) {
+ ops.data = ops.data || {};
+
+ var getParams = function(data, request_url) {
+ var arr = [], str;
+ for(var name in data) {
+ if (typeof data[name] === 'object') {
+ for (var i in data[name]) {
+ arr.push(name + '=' + encodeURIComponent(data[name][i]));
+ }
+ } else {
+ arr.push(name + '=' + encodeURIComponent(data[name]));
+ }
+ }
+ str = arr.join('&');
+ if(str != '') {
+ return request_url ? (request_url.indexOf('?') < 0 ? '?' + str : '&' + str) : str;
+ }
+ return '';
+ };
+ var api = {
+ host: {},
+ setHeaders: function(headers) {
+ for(var name in headers) {
+ this.xhr && this.xhr.setRequestHeader(name, headers[name]);
+ }
+ },
+ process: function(ops) {
+ var self = this;
+ this.xhr = null;
+ if (window.ActiveXObject) {
+ this.xhr = new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ else if (window.XMLHttpRequest) {
+ this.xhr = new XMLHttpRequest();
+ }
+ if(this.xhr) {
+ this.xhr.onreadystatechange = function() {
+ if(self.xhr.readyState == 4 && self.xhr.status == 200) {
+ var result = self.xhr.responseText;
+ if (typeof JSON != 'undefined') {
+ result = JSON.parse(result);
+ } else {
+ throw "JSON undefined";
+ }
+ self.doneCallback && self.doneCallback.apply(self.host, [result, self.xhr]);
+ } else if(self.xhr.readyState == 4) {
+ self.failCallback && self.failCallback.apply(self.host, [self.xhr]);
+ }
+ self.alwaysCallback && self.alwaysCallback.apply(self.host, [self.xhr]);
+ }
+ }
+ if(method == 'get') {
+ this.xhr.open("GET", url + getParams(ops.data, url), true);
+ } else {
+ this.xhr.open(method, url, true);
+ this.setHeaders({
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Content-type': 'application/x-www-form-urlencoded'
+ });
+ }
+ if(ops.headers && typeof ops.headers == 'object') {
+ this.setHeaders(ops.headers);
+ }
+ setTimeout(function() {
+ method == 'get' ? self.xhr.send() : self.xhr.send(getParams(ops.data));
+ }, 20);
+ return this;
+ },
+ done: function(callback) {
+ this.doneCallback = callback;
+ return this;
+ },
+ fail: function(callback) {
+ this.failCallback = callback;
+ return this;
+ },
+ always: function(callback) {
+ this.alwaysCallback = callback;
+ return this;
+ }
+ };
+ return api.process(ops);
+ }
+ };
+
function endsWith(value, suffix) {
return value.indexOf(suffix, value.length - suffix.length) !== -1;
}
+ function startsWith(value, prefix) {
+ return value.lastIndexOf(prefix, 0) === 0;
+ }
+
function stripSlash(value) {
if (value.substring(value.length - 1) == "/") {
value = value.substring(0, value.length - 1);
}
return value;
@@ -606,14 +695,18 @@
this._messageId = 0;
this._clientId = null;
this._subscriptions = {};
this._messages = [];
this._isBatching = false;
+ this._isAuthBatching = false;
+ this._authChannels = {};
+ this._refreshTimeout = null;
this._config = {
retry: 3000,
info: null,
debug: false,
+ insecure: false,
server: null,
protocols_whitelist: [
'websocket',
'xdr-streaming',
'xhr-streaming',
@@ -621,28 +714,33 @@
'iframe-htmlfile',
'xdr-polling',
'xhr-polling',
'iframe-xhr-polling',
'jsonp-polling'
- ]
+ ],
+ privateChannelPrefix: "$",
+ refreshEndpoint: "/centrifuge/refresh",
+ authEndpoint: "/centrifuge/auth",
+ authHeaders: {},
+ refreshHeaders: {}
};
if (options) {
this.configure(options);
}
}
extend(Centrifuge, EventEmitter);
- var centrifuge_proto = Centrifuge.prototype;
+ var centrifugeProto = Centrifuge.prototype;
- centrifuge_proto._debug = function () {
+ centrifugeProto._debug = function () {
if (this._config.debug === true) {
log('debug', arguments);
}
};
- centrifuge_proto._configure = function (configuration) {
+ centrifugeProto._configure = function (configuration) {
this._debug('Configuring centrifuge object with', configuration);
if (!configuration) {
configuration = {};
}
@@ -651,26 +749,39 @@
if (!this._config.url) {
throw 'Missing required configuration parameter \'url\' specifying the Centrifuge server URL';
}
- if (!this._config.token) {
- throw 'Missing required configuration parameter \'token\' specifying the sign of authorization request';
- }
-
if (!this._config.project) {
throw 'Missing required configuration parameter \'project\' specifying project ID in Centrifuge';
}
if (!this._config.user && this._config.user !== '') {
- throw 'Missing required configuration parameter \'user\' specifying user\'s unique ID in your application';
+ if (!this._config.insecure) {
+ throw 'Missing required configuration parameter \'user\' specifying user\'s unique ID in your application';
+ } else {
+ this._debug("user not found but this is OK for insecure mode - anonymous access will be used");
+ this._config.user = "";
+ }
}
if (!this._config.timestamp) {
- throw 'Missing required configuration parameter \'timestamp\'';
+ if (!this._config.insecure) {
+ throw 'Missing required configuration parameter \'timestamp\'';
+ } else {
+ this._debug("token not found but this is OK for insecure mode");
+ }
}
+ if (!this._config.token) {
+ if (!this._config.insecure) {
+ throw 'Missing required configuration parameter \'token\' specifying the sign of authorization request';
+ } else {
+ this._debug("timestamp not found but this is OK for insecure mode");
+ }
+ }
+
this._config.url = stripSlash(this._config.url);
if (endsWith(this._config.url, 'connection')) {
//noinspection JSUnresolvedVariable
if (typeof window.SockJS === 'undefined') {
@@ -678,34 +789,38 @@
}
this._sockjs = true;
}
};
- centrifuge_proto._setStatus = function (newStatus) {
+ centrifugeProto._setStatus = function (newStatus) {
if (this._status !== newStatus) {
this._debug('Status', this._status, '->', newStatus);
this._status = newStatus;
}
};
- centrifuge_proto._isDisconnected = function () {
+ centrifugeProto._isDisconnected = function () {
return this._isConnected() === false;
};
- centrifuge_proto._isConnected = function () {
+ centrifugeProto._isConnected = function () {
return this._status === 'connected';
};
- centrifuge_proto._nextMessageId = function () {
+ centrifugeProto._isConnecting = function () {
+ return this._status === 'connecting';
+ };
+
+ centrifugeProto._nextMessageId = function () {
return ++this._messageId;
};
- centrifuge_proto._clearSubscriptions = function () {
+ centrifugeProto._clearSubscriptions = function () {
this._subscriptions = {};
};
- centrifuge_proto._send = function (messages) {
+ centrifugeProto._send = function (messages) {
// We must be sure that the messages have a clientId.
// This is not guaranteed since the handshake may take time to return
// (and hence the clientId is not known yet) and the application
// may create other messages.
for (var i = 0; i < messages.length; ++i) {
@@ -719,12 +834,16 @@
this._debug('Send', message);
this._transport.send(JSON.stringify(message));
}
};
- centrifuge_proto._connect = function (callback) {
+ centrifugeProto._connect = function (callback) {
+ if (this.isConnected()) {
+ return;
+ }
+
this._clientId = null;
this._reconnect = true;
this._clearSubscriptions();
@@ -757,23 +876,23 @@
this._transport.onopen = function () {
var centrifugeMessage = {
'method': 'connect',
'params': {
- 'token': self._config.token,
'user': self._config.user,
- 'project': self._config.project,
- 'timestamp': self._config.timestamp
+ 'project': self._config.project
}
};
if (self._config.info !== null) {
- self._debug("connect using additional info");
- centrifugeMessage['params']['info'] = self._config.info;
- } else {
- self._debug("connect without additional info");
+ centrifugeMessage["params"]["info"] = self._config.info;
}
+
+ if (!self._config.insecure) {
+ centrifugeMessage["params"]["timestamp"] = self._config.timestamp;
+ centrifugeMessage["params"]["token"] = self._config.token;
+ }
self.send(centrifugeMessage);
};
this._transport.onerror = function (error) {
self._debug(error);
@@ -797,58 +916,81 @@
self._debug('Received', data);
self._receive(data);
};
};
- centrifuge_proto._disconnect = function () {
+ centrifugeProto._disconnect = function () {
this._clientId = null;
this._setStatus('disconnected');
this._subscriptions = {};
this._reconnect = false;
this._transport.close();
};
- centrifuge_proto._getSubscription = function (channel) {
+ centrifugeProto._getSubscription = function (channel) {
var subscription;
subscription = this._subscriptions[channel];
if (!subscription) {
return null;
}
return subscription;
};
- centrifuge_proto._removeSubscription = function (channel) {
+ centrifugeProto._removeSubscription = function (channel) {
try {
delete this._subscriptions[channel];
} catch (e) {
this._debug('nothing to delete for channel ', channel);
}
+ try {
+ delete this._authChannels[channel];
+ } catch (e) {
+ this._debug('nothing to delete from authChannels for channel ', channel);
+ }
};
- centrifuge_proto._connectResponse = function (message) {
+ centrifugeProto._connectResponse = function (message) {
+ if (this.isConnected()) {
+ return;
+ }
if (message.error === null) {
- this._clientId = message.body;
+ if (!message.body) {
+ return;
+ }
+ var isExpired = message.body.expired;
+ if (isExpired) {
+ this.refresh();
+ return;
+ }
+ this._clientId = message.body.client;
this._setStatus('connected');
this.trigger('connect', [message]);
+ if (this._refreshTimeout) {
+ window.clearTimeout(this._refreshTimeout);
+ }
+ if (message.body.ttl !== null) {
+ var self = this;
+ this._refreshTimeout = window.setTimeout(function() {
+ self.refresh.call(self);
+ }, message.body.ttl * 1000);
+ }
} else {
this.trigger('error', [message]);
this.trigger('connect:error', [message]);
}
};
- centrifuge_proto._disconnectResponse = function (message) {
+ centrifugeProto._disconnectResponse = function (message) {
if (message.error === null) {
this.disconnect();
- //this.trigger('disconnect', [message]);
- //this.trigger('disconnect:success', [message]);
} else {
this.trigger('error', [message]);
this.trigger('disconnect:error', [message.error]);
}
};
- centrifuge_proto._subscribeResponse = function (message) {
+ centrifugeProto._subscribeResponse = function (message) {
if (message.error !== null) {
this.trigger('error', [message]);
}
var body = message.body;
if (body === null) {
@@ -866,11 +1008,11 @@
subscription.trigger('subscribe:error', [message.error]);
subscription.trigger('error', [message]);
}
};
- centrifuge_proto._unsubscribeResponse = function (message) {
+ centrifugeProto._unsubscribeResponse = function (message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
@@ -879,11 +1021,11 @@
subscription.trigger('unsubscribe', [body]);
this._centrifuge._removeSubscription(channel);
}
};
- centrifuge_proto._publishResponse = function (message) {
+ centrifugeProto._publishResponse = function (message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
@@ -894,11 +1036,11 @@
subscription.trigger('publish:error', [message.error]);
this.trigger('error', [message]);
}
};
- centrifuge_proto._presenceResponse = function (message) {
+ centrifugeProto._presenceResponse = function (message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
@@ -910,11 +1052,11 @@
subscription.trigger('presence:error', [message.error]);
this.trigger('error', [message]);
}
};
- centrifuge_proto._historyResponse = function (message) {
+ centrifugeProto._historyResponse = function (message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
@@ -926,41 +1068,53 @@
subscription.trigger('history:error', [message.error]);
this.trigger('error', [message]);
}
};
- centrifuge_proto._joinResponse = function(message) {
+ centrifugeProto._joinResponse = function(message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
}
subscription.trigger('join', [body]);
};
- centrifuge_proto._leaveResponse = function(message) {
+ centrifugeProto._leaveResponse = function(message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (!subscription) {
return;
}
subscription.trigger('leave', [body]);
};
- centrifuge_proto._messageResponse = function (message) {
+ centrifugeProto._messageResponse = function (message) {
var body = message.body;
var channel = body.channel;
var subscription = this.getSubscription(channel);
if (subscription === null) {
return;
}
subscription.trigger('message', [body]);
};
- centrifuge_proto._dispatchMessage = function(message) {
+ centrifugeProto._refreshResponse = function (message) {
+ if (this._refreshTimeout) {
+ window.clearTimeout(this._refreshTimeout);
+ }
+ if (message.body.ttl !== null) {
+ var self = this;
+ self._refreshTimeout = window.setTimeout(function () {
+ self.refresh.call(self);
+ }, message.body.ttl * 1000);
+ }
+ };
+
+ centrifugeProto._dispatchMessage = function(message) {
if (message === undefined || message === null) {
return;
}
var method = message.method;
@@ -997,19 +1151,22 @@
case 'leave':
this._leaveResponse(message);
break;
case 'ping':
break;
+ case 'refresh':
+ this._refreshResponse(message);
+ break;
case 'message':
this._messageResponse(message);
break;
default:
break;
}
};
- centrifuge_proto._receive = function (data) {
+ centrifugeProto._receive = function (data) {
if (Object.prototype.toString.call(data) === Object.prototype.toString.call([])) {
for (var i in data) {
if (data.hasOwnProperty(i)) {
var msg = data[i];
this._dispatchMessage(msg);
@@ -1018,75 +1175,162 @@
} else if (Object.prototype.toString.call(data) === Object.prototype.toString.call({})) {
this._dispatchMessage(data);
}
};
- centrifuge_proto._flush = function() {
+ centrifugeProto._flush = function() {
var messages = this._messages.slice(0);
this._messages = [];
this._send(messages);
};
- centrifuge_proto._ping = function () {
+ centrifugeProto._ping = function () {
var centrifugeMessage = {
"method": "ping",
"params": {}
};
this.send(centrifugeMessage);
};
/* PUBLIC API */
- centrifuge_proto.getClientId = function () {
+ centrifugeProto.getClientId = function () {
return this._clientId;
};
- centrifuge_proto.isConnected = centrifuge_proto._isConnected;
+ centrifugeProto.isConnected = centrifugeProto._isConnected;
- centrifuge_proto.isDisconnected = centrifuge_proto._isDisconnected;
+ centrifugeProto.isConnecting = centrifugeProto._isConnecting;
- centrifuge_proto.configure = function (configuration) {
+ centrifugeProto.isDisconnected = centrifugeProto._isDisconnected;
+
+ centrifugeProto.configure = function (configuration) {
this._configure.call(this, configuration);
};
- centrifuge_proto.connect = centrifuge_proto._connect;
+ centrifugeProto.connect = centrifugeProto._connect;
- centrifuge_proto.disconnect = centrifuge_proto._disconnect;
+ centrifugeProto.disconnect = centrifugeProto._disconnect;
- centrifuge_proto.getSubscription = centrifuge_proto._getSubscription;
+ centrifugeProto.getSubscription = centrifugeProto._getSubscription;
- centrifuge_proto.ping = centrifuge_proto._ping;
+ centrifugeProto.ping = centrifugeProto._ping;
- centrifuge_proto.send = function (message) {
+ centrifugeProto.send = function (message) {
if (this._isBatching === true) {
this._messages.push(message);
} else {
this._send([message]);
}
};
- centrifuge_proto.startBatching = function () {
+ centrifugeProto.startBatching = function () {
// start collecting messages without sending them to Centrifuge until flush
// method called
this._isBatching = true;
};
- centrifuge_proto.stopBatching = function(flush) {
+ centrifugeProto.stopBatching = function(flush) {
// stop collecting messages
flush = flush || false;
this._isBatching = false;
if (flush === true) {
this.flush();
}
};
- centrifuge_proto.flush = function() {
+ centrifugeProto.flush = function() {
+ // send batched messages to Centrifuge
this._flush();
};
- centrifuge_proto.subscribe = function (channel, callback) {
+ centrifugeProto.startAuthBatching = function() {
+ // start collecting private channels to create bulk authentication
+ // request to authEndpoint when stopAuthBatching will be called
+ this._isAuthBatching = true;
+ };
+ centrifugeProto.stopAuthBatching = function(callback) {
+ // create request to authEndpoint with collected private channels
+ // to ask if this client can subscribe on each channel
+ this._isAuthBatching = false;
+ var authChannels = this._authChannels;
+ this._authChannels = {};
+ var channels = [];
+
+ for (var channel in authChannels) {
+ var subscription = this.getSubscription(channel);
+ if (!subscription) {
+ continue;
+ }
+ channels.push(channel);
+ }
+
+ if (channels.length == 0) {
+ if (callback) {
+ callback();
+ }
+ return;
+ }
+
+ var data = {
+ "client": this.getClientId(),
+ "channels": channels
+ };
+
+ var self = this;
+
+ AJAX.request(this._config.authEndpoint, "post", {
+ "headers": this._config.authHeaders,
+ "data": data
+ }).done(function(data) {
+ for (var i in channels) {
+ var channel = channels[i];
+ var channelResponse = data[channel];
+ if (!channelResponse) {
+ // subscription:error
+ self._subscribeResponse({
+ "error": 404,
+ "body": {
+ "channel": channel
+ }
+ });
+ continue;
+ }
+ if (!channelResponse.status || channelResponse.status === 200) {
+ var centrifugeMessage = {
+ "method": "subscribe",
+ "params": {
+ "channel": channel,
+ "client": self.getClientId(),
+ "info": channelResponse.info,
+ "sign": channelResponse.sign
+ }
+ };
+ self.send(centrifugeMessage);
+ } else {
+ self._subscribeResponse({
+ "error": channelResponse.status,
+ "body": {
+ "channel": channel
+ }
+ });
+ }
+ }
+ }).fail(function() {
+ log("info", "authorization request failed");
+ return false;
+ }).always(function(){
+ if (callback) {
+ callback();
+ }
+ })
+
+ };
+
+ centrifugeProto.subscribe = function (channel, callback) {
+
if (arguments.length < 1) {
throw 'Illegal arguments number: required 1, got ' + arguments.length;
}
if (!isString(channel)) {
throw 'Illegal argument type: channel must be a string';
@@ -1105,11 +1349,11 @@
subscription.subscribe(callback);
return subscription;
}
};
- centrifuge_proto.unsubscribe = function (channel) {
+ centrifugeProto.unsubscribe = function (channel) {
if (arguments.length < 1) {
throw 'Illegal arguments number: required 1, got ' + arguments.length;
}
if (!isString(channel)) {
throw 'Illegal argument type: channel must be a string';
@@ -1122,40 +1366,83 @@
if (subscription !== null) {
subscription.unsubscribe();
}
};
- centrifuge_proto.publish = function (channel, data, callback) {
+ centrifugeProto.publish = function (channel, data, callback) {
var subscription = this.getSubscription(channel);
if (subscription === null) {
this._debug("subscription not found for channel " + channel);
return null;
}
subscription.publish(data, callback);
return subscription;
};
- centrifuge_proto.presence = function (channel, callback) {
+ centrifugeProto.presence = function (channel, callback) {
var subscription = this.getSubscription(channel);
if (subscription === null) {
this._debug("subscription not found for channel " + channel);
return null;
}
subscription.presence(callback);
return subscription;
};
- centrifuge_proto.history = function (channel, callback) {
+ centrifugeProto.history = function (channel, callback) {
var subscription = this.getSubscription(channel);
if (subscription === null) {
this._debug("subscription not found for channel " + channel);
return null;
}
subscription.history(callback);
return subscription;
};
+ centrifugeProto.refresh = function () {
+ // ask web app for connection parameters - project ID, user ID,
+ // timestamp, info and token
+ var self = this;
+ this._debug('refresh');
+ AJAX.request(this._config.refreshEndpoint, "post", {
+ "headers": this._config.refreshHeaders,
+ "data": {}
+ }).done(function(data) {
+ self._config.user = data.user;
+ self._config.project = data.project;
+ self._config.timestamp = data.timestamp;
+ self._config.info = data.info;
+ self._config.token = data.token;
+ if (self._reconnect && self.isDisconnected()) {
+ self.connect();
+ } else {
+ var centrifugeMessage = {
+ "method": "refresh",
+ "params": {
+ 'user': self._config.user,
+ 'project': self._config.project,
+ 'timestamp': self._config.timestamp,
+ 'info': self._config.info,
+ 'token': self._config.token
+ }
+ };
+ self.send(centrifugeMessage);
+ }
+ }).fail(function(xhr){
+ // 403 or 500 - does not matter - if connection check activated then Centrifuge
+ // will disconnect client eventually
+ self._debug(xhr);
+ self._debug("error getting connect parameters");
+ if (self._refreshTimeout) {
+ window.clearTimeout(self._refreshTimeout);
+ }
+ self._refreshTimeout = window.setTimeout(function(){
+ self.refresh.call(self);
+ }, 3000);
+ });
+ };
+
function Subscription(centrifuge, channel) {
/**
* The constructor for a centrifuge object, identified by an optional name.
* The default name is the string 'default'.
* @param name the optional name of this centrifuge object
@@ -1164,45 +1451,64 @@
this.channel = channel;
}
extend(Subscription, EventEmitter);
- var sub_proto = Subscription.prototype;
+ var subscriptionProto = Subscription.prototype;
- sub_proto.getChannel = function () {
+ subscriptionProto.getChannel = function () {
return this.channel;
};
- sub_proto.getCentrifuge = function () {
+ subscriptionProto.getCentrifuge = function () {
return this._centrifuge;
};
- sub_proto.subscribe = function (callback) {
+ subscriptionProto.subscribe = function (callback) {
+ /*
+ If channel name does not start with privateChannelPrefix - then we
+ can just send subscription message to Centrifuge. If channel name
+ starts with privateChannelPrefix - then this is a private channel
+ and we should ask web application backend for permission first.
+ */
var centrifugeMessage = {
"method": "subscribe",
"params": {
"channel": this.channel
}
};
- this._centrifuge.send(centrifugeMessage);
+
+ if (startsWith(this.channel, this._centrifuge._config.privateChannelPrefix)) {
+ // private channel
+ if (this._centrifuge._isAuthBatching) {
+ this._centrifuge._authChannels[this.channel] = true;
+ } else {
+ this._centrifuge.startAuthBatching();
+ this.subscribe(callback);
+ this._centrifuge.stopAuthBatching();
+ }
+ } else {
+ this._centrifuge.send(centrifugeMessage);
+ }
+
if (callback) {
this.on('message', callback);
}
};
- sub_proto.unsubscribe = function () {
+ subscriptionProto.unsubscribe = function () {
this._centrifuge._removeSubscription(this.channel);
var centrifugeMessage = {
"method": "unsubscribe",
"params": {
"channel": this.channel
}
};
this._centrifuge.send(centrifugeMessage);
};
- sub_proto.publish = function (data, callback) {
+ subscriptionProto.publish = function (data, callback) {
var centrifugeMessage = {
"method": "publish",
"params": {
"channel": this.channel,
"data": data
@@ -1212,11 +1518,11 @@
this.on('publish:success', callback);
}
this._centrifuge.send(centrifugeMessage);
};
- sub_proto.presence = function (callback) {
+ subscriptionProto.presence = function (callback) {
var centrifugeMessage = {
"method": "presence",
"params": {
"channel": this.channel
}
@@ -1225,10 +1531,10 @@
this.on('presence', callback);
}
this._centrifuge.send(centrifugeMessage);
};
- sub_proto.history = function (callback) {
+ subscriptionProto.history = function (callback) {
var centrifugeMessage = {
"method": "history",
"params": {
"channel": this.channel
}