aboutsummaryrefslogtreecommitdiffstats
path: root/actioncable/app/assets/javascripts/action_cable.js
diff options
context:
space:
mode:
Diffstat (limited to 'actioncable/app/assets/javascripts/action_cable.js')
-rw-r--r--actioncable/app/assets/javascripts/action_cable.js421
1 files changed, 218 insertions, 203 deletions
diff --git a/actioncable/app/assets/javascripts/action_cable.js b/actioncable/app/assets/javascripts/action_cable.js
index 07151b9d25..65e32d6c3f 100644
--- a/actioncable/app/assets/javascripts/action_cable.js
+++ b/actioncable/app/assets/javascripts/action_cable.js
@@ -1,16 +1,22 @@
(function(global, factory) {
- typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global.ActionCable = factory();
-})(this, function() {
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActionCable = {});
+})(this, function(exports) {
"use strict";
- var INTERNAL = {
- message_types: {
- welcome: "welcome",
- ping: "ping",
- confirmation: "confirm_subscription",
- rejection: "reject_subscription"
- },
- default_mount_path: "/cable",
- protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
+ var adapters = {
+ logger: window.console,
+ WebSocket: window.WebSocket
+ };
+ var logger = {
+ log: function log() {
+ if (this.enabled) {
+ var _adapters$logger;
+ for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
+ messages[_key] = arguments[_key];
+ }
+ messages.push(Date.now());
+ (_adapters$logger = adapters.logger).log.apply(_adapters$logger, [ "[ActionCable]" ].concat(messages));
+ }
+ }
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
return typeof obj;
@@ -22,6 +28,127 @@
throw new TypeError("Cannot call a class as a function");
}
};
+ var now = function now() {
+ return new Date().getTime();
+ };
+ var secondsSince = function secondsSince(time) {
+ return (now() - time) / 1e3;
+ };
+ var clamp = function clamp(number, min, max) {
+ return Math.max(min, Math.min(max, number));
+ };
+ var ConnectionMonitor = function() {
+ function ConnectionMonitor(connection) {
+ classCallCheck(this, ConnectionMonitor);
+ this.visibilityDidChange = this.visibilityDidChange.bind(this);
+ this.connection = connection;
+ this.reconnectAttempts = 0;
+ }
+ ConnectionMonitor.prototype.start = function start() {
+ if (!this.isRunning()) {
+ this.startedAt = now();
+ delete this.stoppedAt;
+ this.startPolling();
+ document.addEventListener("visibilitychange", this.visibilityDidChange);
+ logger.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms");
+ }
+ };
+ ConnectionMonitor.prototype.stop = function stop() {
+ if (this.isRunning()) {
+ this.stoppedAt = now();
+ this.stopPolling();
+ document.removeEventListener("visibilitychange", this.visibilityDidChange);
+ logger.log("ConnectionMonitor stopped");
+ }
+ };
+ ConnectionMonitor.prototype.isRunning = function isRunning() {
+ return this.startedAt && !this.stoppedAt;
+ };
+ ConnectionMonitor.prototype.recordPing = function recordPing() {
+ this.pingedAt = now();
+ };
+ ConnectionMonitor.prototype.recordConnect = function recordConnect() {
+ this.reconnectAttempts = 0;
+ this.recordPing();
+ delete this.disconnectedAt;
+ logger.log("ConnectionMonitor recorded connect");
+ };
+ ConnectionMonitor.prototype.recordDisconnect = function recordDisconnect() {
+ this.disconnectedAt = now();
+ logger.log("ConnectionMonitor recorded disconnect");
+ };
+ ConnectionMonitor.prototype.startPolling = function startPolling() {
+ this.stopPolling();
+ this.poll();
+ };
+ ConnectionMonitor.prototype.stopPolling = function stopPolling() {
+ clearTimeout(this.pollTimeout);
+ };
+ ConnectionMonitor.prototype.poll = function poll() {
+ var _this = this;
+ this.pollTimeout = setTimeout(function() {
+ _this.reconnectIfStale();
+ _this.poll();
+ }, this.getPollInterval());
+ };
+ ConnectionMonitor.prototype.getPollInterval = function getPollInterval() {
+ var _constructor$pollInte = this.constructor.pollInterval, min = _constructor$pollInte.min, max = _constructor$pollInte.max, multiplier = _constructor$pollInte.multiplier;
+ var interval = multiplier * Math.log(this.reconnectAttempts + 1);
+ return Math.round(clamp(interval, min, max) * 1e3);
+ };
+ ConnectionMonitor.prototype.reconnectIfStale = function reconnectIfStale() {
+ if (this.connectionIsStale()) {
+ logger.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + this.getPollInterval() + " ms, time disconnected = " + secondsSince(this.disconnectedAt) + " s, stale threshold = " + this.constructor.staleThreshold + " s");
+ this.reconnectAttempts++;
+ if (this.disconnectedRecently()) {
+ logger.log("ConnectionMonitor skipping reopening recent disconnect");
+ } else {
+ logger.log("ConnectionMonitor reopening");
+ this.connection.reopen();
+ }
+ }
+ };
+ ConnectionMonitor.prototype.connectionIsStale = function connectionIsStale() {
+ return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold;
+ };
+ ConnectionMonitor.prototype.disconnectedRecently = function disconnectedRecently() {
+ return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
+ };
+ ConnectionMonitor.prototype.visibilityDidChange = function visibilityDidChange() {
+ var _this2 = this;
+ if (document.visibilityState === "visible") {
+ setTimeout(function() {
+ if (_this2.connectionIsStale() || !_this2.connection.isOpen()) {
+ logger.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
+ _this2.connection.reopen();
+ }
+ }, 200);
+ }
+ };
+ return ConnectionMonitor;
+ }();
+ ConnectionMonitor.pollInterval = {
+ min: 3,
+ max: 30,
+ multiplier: 5
+ };
+ ConnectionMonitor.staleThreshold = 6;
+ var INTERNAL = {
+ message_types: {
+ welcome: "welcome",
+ disconnect: "disconnect",
+ ping: "ping",
+ confirmation: "confirm_subscription",
+ rejection: "reject_subscription"
+ },
+ disconnect_reasons: {
+ unauthorized: "unauthorized",
+ invalid_request: "invalid_request",
+ server_restart: "server_restart"
+ },
+ default_mount_path: "/cable",
+ protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
+ };
var message_types = INTERNAL.message_types, protocols = INTERNAL.protocols;
var supportedProtocols = protocols.slice(0, protocols.length - 1);
var indexOf = [].indexOf;
@@ -31,7 +158,7 @@
this.open = this.open.bind(this);
this.consumer = consumer;
this.subscriptions = this.consumer.subscriptions;
- this.monitor = new ActionCable.ConnectionMonitor(this);
+ this.monitor = new ConnectionMonitor(this);
this.disconnected = true;
}
Connection.prototype.send = function send(data) {
@@ -44,14 +171,14 @@
};
Connection.prototype.open = function open() {
if (this.isActive()) {
- ActionCable.log("Attempted to open WebSocket, but existing socket is " + this.getState());
+ logger.log("Attempted to open WebSocket, but existing socket is " + this.getState());
return false;
} else {
- ActionCable.log("Opening WebSocket, current state is " + this.getState() + ", subprotocols: " + protocols);
+ logger.log("Opening WebSocket, current state is " + this.getState() + ", subprotocols: " + protocols);
if (this.webSocket) {
this.uninstallEventHandlers();
}
- this.webSocket = new ActionCable.WebSocket(this.consumer.url, protocols);
+ this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
this.installEventHandlers();
this.monitor.start();
return true;
@@ -69,14 +196,14 @@
}
};
Connection.prototype.reopen = function reopen() {
- ActionCable.log("Reopening WebSocket, current state is " + this.getState());
+ logger.log("Reopening WebSocket, current state is " + this.getState());
if (this.isActive()) {
try {
return this.close();
} catch (error) {
- ActionCable.log("Failed to reopen WebSocket", error);
+ logger.log("Failed to reopen WebSocket", error);
} finally {
- ActionCable.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
+ logger.log("Reopening WebSocket in " + this.constructor.reopenDelay + "ms");
setTimeout(this.open, this.constructor.reopenDelay);
}
} else {
@@ -103,8 +230,8 @@
};
Connection.prototype.getState = function getState() {
if (this.webSocket) {
- for (var state in WebSocket) {
- if (WebSocket[state] === this.webSocket.readyState) {
+ for (var state in adapters.WebSocket) {
+ if (adapters.WebSocket[state] === this.webSocket.readyState) {
return state.toLowerCase();
}
}
@@ -130,12 +257,18 @@
if (!this.isProtocolSupported()) {
return;
}
- var _JSON$parse = JSON.parse(event.data), identifier = _JSON$parse.identifier, message = _JSON$parse.message, type = _JSON$parse.type;
+ var _JSON$parse = JSON.parse(event.data), identifier = _JSON$parse.identifier, message = _JSON$parse.message, reason = _JSON$parse.reason, reconnect = _JSON$parse.reconnect, type = _JSON$parse.type;
switch (type) {
case message_types.welcome:
this.monitor.recordConnect();
return this.subscriptions.reload();
+ case message_types.disconnect:
+ logger.log("Disconnecting. Reason: " + reason);
+ return this.close({
+ allowReconnect: reconnect
+ });
+
case message_types.ping:
return this.monitor.recordPing();
@@ -150,17 +283,17 @@
}
},
open: function open() {
- ActionCable.log("WebSocket onopen event, using '" + this.getProtocol() + "' subprotocol");
+ logger.log("WebSocket onopen event, using '" + this.getProtocol() + "' subprotocol");
this.disconnected = false;
if (!this.isProtocolSupported()) {
- ActionCable.log("Protocol is unsupported. Stopping monitor and disconnecting.");
+ logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
return this.close({
allowReconnect: false
});
}
},
close: function close(event) {
- ActionCable.log("WebSocket onclose event");
+ logger.log("WebSocket onclose event");
if (this.disconnected) {
return;
}
@@ -171,139 +304,9 @@
});
},
error: function error() {
- ActionCable.log("WebSocket onerror event");
+ logger.log("WebSocket onerror event");
}
};
- var now = function now() {
- return new Date().getTime();
- };
- var secondsSince = function secondsSince(time) {
- return (now() - time) / 1e3;
- };
- var clamp = function clamp(number, min, max) {
- return Math.max(min, Math.min(max, number));
- };
- var ConnectionMonitor = function() {
- function ConnectionMonitor(connection) {
- classCallCheck(this, ConnectionMonitor);
- this.visibilityDidChange = this.visibilityDidChange.bind(this);
- this.connection = connection;
- this.reconnectAttempts = 0;
- }
- ConnectionMonitor.prototype.start = function start() {
- if (!this.isRunning()) {
- this.startedAt = now();
- delete this.stoppedAt;
- this.startPolling();
- document.addEventListener("visibilitychange", this.visibilityDidChange);
- ActionCable.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms");
- }
- };
- ConnectionMonitor.prototype.stop = function stop() {
- if (this.isRunning()) {
- this.stoppedAt = now();
- this.stopPolling();
- document.removeEventListener("visibilitychange", this.visibilityDidChange);
- ActionCable.log("ConnectionMonitor stopped");
- }
- };
- ConnectionMonitor.prototype.isRunning = function isRunning() {
- return this.startedAt && !this.stoppedAt;
- };
- ConnectionMonitor.prototype.recordPing = function recordPing() {
- this.pingedAt = now();
- };
- ConnectionMonitor.prototype.recordConnect = function recordConnect() {
- this.reconnectAttempts = 0;
- this.recordPing();
- delete this.disconnectedAt;
- ActionCable.log("ConnectionMonitor recorded connect");
- };
- ConnectionMonitor.prototype.recordDisconnect = function recordDisconnect() {
- this.disconnectedAt = now();
- ActionCable.log("ConnectionMonitor recorded disconnect");
- };
- ConnectionMonitor.prototype.startPolling = function startPolling() {
- this.stopPolling();
- this.poll();
- };
- ConnectionMonitor.prototype.stopPolling = function stopPolling() {
- clearTimeout(this.pollTimeout);
- };
- ConnectionMonitor.prototype.poll = function poll() {
- var _this = this;
- this.pollTimeout = setTimeout(function() {
- _this.reconnectIfStale();
- _this.poll();
- }, this.getPollInterval());
- };
- ConnectionMonitor.prototype.getPollInterval = function getPollInterval() {
- var _constructor$pollInte = this.constructor.pollInterval, min = _constructor$pollInte.min, max = _constructor$pollInte.max, multiplier = _constructor$pollInte.multiplier;
- var interval = multiplier * Math.log(this.reconnectAttempts + 1);
- return Math.round(clamp(interval, min, max) * 1e3);
- };
- ConnectionMonitor.prototype.reconnectIfStale = function reconnectIfStale() {
- if (this.connectionIsStale()) {
- ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = " + this.reconnectAttempts + ", pollInterval = " + this.getPollInterval() + " ms, time disconnected = " + secondsSince(this.disconnectedAt) + " s, stale threshold = " + this.constructor.staleThreshold + " s");
- this.reconnectAttempts++;
- if (this.disconnectedRecently()) {
- ActionCable.log("ConnectionMonitor skipping reopening recent disconnect");
- } else {
- ActionCable.log("ConnectionMonitor reopening");
- this.connection.reopen();
- }
- }
- };
- ConnectionMonitor.prototype.connectionIsStale = function connectionIsStale() {
- return secondsSince(this.pingedAt ? this.pingedAt : this.startedAt) > this.constructor.staleThreshold;
- };
- ConnectionMonitor.prototype.disconnectedRecently = function disconnectedRecently() {
- return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
- };
- ConnectionMonitor.prototype.visibilityDidChange = function visibilityDidChange() {
- var _this2 = this;
- if (document.visibilityState === "visible") {
- setTimeout(function() {
- if (_this2.connectionIsStale() || !_this2.connection.isOpen()) {
- ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState);
- _this2.connection.reopen();
- }
- }, 200);
- }
- };
- return ConnectionMonitor;
- }();
- ConnectionMonitor.pollInterval = {
- min: 3,
- max: 30,
- multiplier: 5
- };
- ConnectionMonitor.staleThreshold = 6;
- var Consumer = function() {
- function Consumer(url) {
- classCallCheck(this, Consumer);
- this.url = url;
- this.subscriptions = new ActionCable.Subscriptions(this);
- this.connection = new ActionCable.Connection(this);
- }
- Consumer.prototype.send = function send(data) {
- return this.connection.send(data);
- };
- Consumer.prototype.connect = function connect() {
- return this.connection.open();
- };
- Consumer.prototype.disconnect = function disconnect() {
- return this.connection.close({
- allowReconnect: false
- });
- };
- Consumer.prototype.ensureActiveConnection = function ensureActiveConnection() {
- if (!this.connection.isActive()) {
- return this.connection.open();
- }
- };
- return Consumer;
- }();
var extend = function extend(object, properties) {
if (properties != null) {
for (var key in properties) {
@@ -350,7 +353,7 @@
var params = (typeof channel === "undefined" ? "undefined" : _typeof(channel)) === "object" ? channel : {
channel: channel
};
- var subscription = new ActionCable.Subscription(this.consumer, params, mixin);
+ var subscription = new Subscription(this.consumer, params, mixin);
return this.add(subscription);
};
Subscriptions.prototype.add = function add(subscription) {
@@ -424,53 +427,65 @@
};
return Subscriptions;
}();
- var ActionCable = {
- Connection: Connection,
- ConnectionMonitor: ConnectionMonitor,
- Consumer: Consumer,
- INTERNAL: INTERNAL,
- Subscription: Subscription,
- Subscriptions: Subscriptions,
- WebSocket: window.WebSocket,
- logger: window.console,
- createConsumer: function createConsumer(url) {
- if (url == null) {
- var urlConfig = this.getConfig("url");
- url = urlConfig ? urlConfig : this.INTERNAL.default_mount_path;
- }
- return new Consumer(this.createWebSocketURL(url));
- },
- getConfig: function getConfig(name) {
- var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
- return element ? element.getAttribute("content") : undefined;
- },
- createWebSocketURL: function createWebSocketURL(url) {
- if (url && !/^wss?:/i.test(url)) {
- var a = document.createElement("a");
- a.href = url;
- a.href = a.href;
- a.protocol = a.protocol.replace("http", "ws");
- return a.href;
- } else {
- return url;
- }
- },
- startDebugging: function startDebugging() {
- this.debugging = true;
- },
- stopDebugging: function stopDebugging() {
- this.debugging = null;
- },
- log: function log() {
- if (this.debugging) {
- var _logger;
- for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
- messages[_key] = arguments[_key];
- }
- messages.push(Date.now());
- (_logger = this.logger).log.apply(_logger, [ "[ActionCable]" ].concat(messages));
+ var Consumer = function() {
+ function Consumer(url) {
+ classCallCheck(this, Consumer);
+ this.url = url;
+ this.subscriptions = new Subscriptions(this);
+ this.connection = new Connection(this);
+ }
+ Consumer.prototype.send = function send(data) {
+ return this.connection.send(data);
+ };
+ Consumer.prototype.connect = function connect() {
+ return this.connection.open();
+ };
+ Consumer.prototype.disconnect = function disconnect() {
+ return this.connection.close({
+ allowReconnect: false
+ });
+ };
+ Consumer.prototype.ensureActiveConnection = function ensureActiveConnection() {
+ if (!this.connection.isActive()) {
+ return this.connection.open();
}
+ };
+ return Consumer;
+ }();
+ function createConsumer(url) {
+ if (url == null) {
+ var urlConfig = getConfig("url");
+ url = urlConfig ? urlConfig : INTERNAL.default_mount_path;
}
- };
- return ActionCable;
+ return new Consumer(createWebSocketURL(url));
+ }
+ function getConfig(name) {
+ var element = document.head.querySelector("meta[name='action-cable-" + name + "']");
+ return element ? element.getAttribute("content") : undefined;
+ }
+ function createWebSocketURL(url) {
+ if (url && !/^wss?:/i.test(url)) {
+ var a = document.createElement("a");
+ a.href = url;
+ a.href = a.href;
+ a.protocol = a.protocol.replace("http", "ws");
+ return a.href;
+ } else {
+ return url;
+ }
+ }
+ exports.Connection = Connection;
+ exports.ConnectionMonitor = ConnectionMonitor;
+ exports.Consumer = Consumer;
+ exports.INTERNAL = INTERNAL;
+ exports.Subscription = Subscription;
+ exports.Subscriptions = Subscriptions;
+ exports.adapters = adapters;
+ exports.logger = logger;
+ exports.createConsumer = createConsumer;
+ exports.getConfig = getConfig;
+ exports.createWebSocketURL = createWebSocketURL;
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
});