UNPKG

ng-cable

Version:

Power your Angular application with ActionCable

467 lines (402 loc) 12.8 kB
(function() { this.Cable = { PING_IDENTIFIER: "_ping", createConsumer: function(url) { return new Cable.Consumer(url); } }; }).call(this); (function() { var slice = [].slice, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Cable.Connection = (function() { function Connection(consumer) { this.consumer = consumer; this.open(); } Connection.prototype.send = function(data) { if (this.isOpen()) { this.webSocket.send(JSON.stringify(data)); return true; } else { return false; } }; Connection.prototype.open = function() { if (this.isState("open", "connecting")) { return; } this.webSocket = new WebSocket(this.consumer.url); return this.installEventHandlers(); }; Connection.prototype.close = function() { var ref; if (this.isState("closed", "closing")) { return; } return (ref = this.webSocket) != null ? ref.close() : void 0; }; Connection.prototype.reopen = function() { if (this.isOpen()) { return this.closeSilently((function(_this) { return function() { return _this.open(); }; })(this)); } else { return this.open(); } }; Connection.prototype.isOpen = function() { return this.isState("open"); }; Connection.prototype.isState = function() { var ref, states; states = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ref = this.getState(), indexOf.call(states, ref) >= 0; }; Connection.prototype.getState = function() { var ref, state, value; for (state in WebSocket) { value = WebSocket[state]; if (value === ((ref = this.webSocket) != null ? ref.readyState : void 0)) { return state.toLowerCase(); } } return null; }; Connection.prototype.closeSilently = function(callback) { if (callback == null) { callback = function() {}; } this.uninstallEventHandlers(); this.installEventHandler("close", callback); this.installEventHandler("error", callback); try { return this.webSocket.close(); } finally { this.uninstallEventHandlers(); } }; Connection.prototype.installEventHandlers = function() { var eventName, results; results = []; for (eventName in this.events) { results.push(this.installEventHandler(eventName)); } return results; }; Connection.prototype.installEventHandler = function(eventName, handler) { if (handler == null) { handler = this.events[eventName].bind(this); } return this.webSocket.addEventListener(eventName, handler); }; Connection.prototype.uninstallEventHandlers = function() { var eventName, results; results = []; for (eventName in this.events) { results.push(this.webSocket.removeEventListener(eventName)); } return results; }; Connection.prototype.events = { message: function(event) { var identifier, message, ref; ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message; return this.consumer.subscriptions.notify(identifier, "received", message); }, open: function() { return this.consumer.subscriptions.reload(); }, close: function() { return this.consumer.subscriptions.notifyAll("disconnected"); }, error: function() { this.consumer.subscriptions.notifyAll("disconnected"); return this.closeSilently(); } }; Connection.prototype.toJSON = function() { return { state: this.getState() }; }; return Connection; })(); }).call(this); (function() { Cable.ConnectionMonitor = (function() { var clamp, now, secondsSince; ConnectionMonitor.prototype.identifier = Cable.PING_IDENTIFIER; ConnectionMonitor.prototype.pollInterval = { min: 2, max: 30 }; ConnectionMonitor.prototype.staleThreshold = { startedAt: 4, pingedAt: 8 }; function ConnectionMonitor(consumer) { this.consumer = consumer; this.consumer.subscriptions.add(this); this.start(); } ConnectionMonitor.prototype.connected = function() { this.reset(); return this.pingedAt = now(); }; ConnectionMonitor.prototype.received = function() { return this.pingedAt = now(); }; ConnectionMonitor.prototype.reset = function() { return this.reconnectAttempts = 0; }; ConnectionMonitor.prototype.start = function() { this.reset(); delete this.stoppedAt; this.startedAt = now(); return this.poll(); }; ConnectionMonitor.prototype.stop = function() { return this.stoppedAt = now(); }; ConnectionMonitor.prototype.poll = function() { return setTimeout((function(_this) { return function() { if (!_this.stoppedAt) { _this.reconnectIfStale(); return _this.poll(); } }; })(this), this.getInterval()); }; ConnectionMonitor.prototype.getInterval = function() { var interval, max, min, ref; ref = this.pollInterval, min = ref.min, max = ref.max; interval = 4 * Math.log(this.reconnectAttempts + 1); return clamp(interval, min, max) * 1000; }; ConnectionMonitor.prototype.reconnectIfStale = function() { if (this.connectionIsStale()) { this.reconnectAttempts += 1; return this.consumer.connection.reopen(); } }; ConnectionMonitor.prototype.connectionIsStale = function() { if (this.pingedAt) { return secondsSince(this.pingedAt) > this.staleThreshold.pingedAt; } else { return secondsSince(this.startedAt) > this.staleThreshold.startedAt; } }; ConnectionMonitor.prototype.toJSON = function() { var connectionIsStale, interval; interval = this.getInterval(); connectionIsStale = this.connectionIsStale(); return { startedAt: this.startedAt, stoppedAt: this.stoppedAt, pingedAt: this.pingedAt, reconnectAttempts: this.reconnectAttempts, connectionIsStale: connectionIsStale, interval: interval }; }; now = function() { return new Date().getTime(); }; secondsSince = function(time) { return (now() - time) / 1000; }; clamp = function(number, min, max) { return Math.max(min, Math.min(max, number)); }; return ConnectionMonitor; })(); }).call(this); (function() { var slice = [].slice; Cable.Subscriptions = (function() { function Subscriptions(consumer) { this.consumer = consumer; this.subscriptions = []; } Subscriptions.prototype.create = function(channelName, mixin) { var channel, params; channel = channelName; params = typeof channel === "object" ? channel : { channel: channel }; return new Cable.Subscription(this, params, mixin); }; Subscriptions.prototype.add = function(subscription) { this.subscriptions.push(subscription); this.notify(subscription, "initialized"); if (this.sendCommand(subscription, "subscribe")) { return this.notify(subscription, "connected"); } }; Subscriptions.prototype.reload = function() { var i, len, ref, results, subscription; ref = this.subscriptions; results = []; for (i = 0, len = ref.length; i < len; i++) { subscription = ref[i]; if (this.sendCommand(subscription, "subscribe")) { results.push(this.notify(subscription, "connected")); } else { results.push(void 0); } } return results; }; Subscriptions.prototype.remove = function(subscription) { var s; this.subscriptions = (function() { var i, len, ref, results; ref = this.subscriptions; results = []; for (i = 0, len = ref.length; i < len; i++) { s = ref[i]; if (s !== subscription) { results.push(s); } } return results; }).call(this); if (!this.findAll(subscription.identifier).length) { return this.sendCommand(subscription, "unsubscribe"); } }; Subscriptions.prototype.findAll = function(identifier) { var i, len, ref, results, s; ref = this.subscriptions; results = []; for (i = 0, len = ref.length; i < len; i++) { s = ref[i]; if (s.identifier === identifier) { results.push(s); } } return results; }; Subscriptions.prototype.notifyAll = function() { var args, callbackName, i, len, ref, results, subscription; callbackName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; ref = this.subscriptions; results = []; for (i = 0, len = ref.length; i < len; i++) { subscription = ref[i]; results.push(this.notify.apply(this, [subscription, callbackName].concat(slice.call(args)))); } return results; }; Subscriptions.prototype.notify = function() { var args, callbackName, i, len, results, subscription, subscriptions; subscription = arguments[0], callbackName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : []; if (typeof subscription === "string") { subscriptions = this.findAll(subscription); } else { subscriptions = [subscription]; } results = []; for (i = 0, len = subscriptions.length; i < len; i++) { subscription = subscriptions[i]; results.push(typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : void 0); } return results; }; Subscriptions.prototype.sendCommand = function(subscription, command) { var identifier; identifier = subscription.identifier; if (identifier === Cable.PING_IDENTIFIER) { return this.consumer.connection.isOpen(); } else { return this.consumer.send({ command: command, identifier: identifier }); } }; Subscriptions.prototype.toJSON = function() { var i, len, ref, results, subscription; ref = this.subscriptions; results = []; for (i = 0, len = ref.length; i < len; i++) { subscription = ref[i]; results.push(subscription.identifier); } return results; }; return Subscriptions; })(); }).call(this); (function() { Cable.Subscription = (function() { var extend; function Subscription(subscriptions, params, mixin) { this.subscriptions = subscriptions; if (params == null) { params = {}; } this.identifier = JSON.stringify(params); extend(this, mixin); this.subscriptions.add(this); this.consumer = this.subscriptions.consumer; } Subscription.prototype.perform = function(action, data) { if (data == null) { data = {}; } data.action = action; return this.send(data); }; Subscription.prototype.send = function(data) { return this.consumer.send({ command: "message", identifier: this.identifier, data: JSON.stringify(data) }); }; Subscription.prototype.unsubscribe = function() { return this.subscriptions.remove(this); }; extend = function(object, properties) { var key, value; if (properties != null) { for (key in properties) { value = properties[key]; object[key] = value; } } return object; }; return Subscription; })(); }).call(this); (function() { Cable.Consumer = (function() { function Consumer(url) { this.url = url; this.subscriptions = new Cable.Subscriptions(this); this.connection = new Cable.Connection(this); this.connectionMonitor = new Cable.ConnectionMonitor(this); } Consumer.prototype.send = function(data) { return this.connection.send(data); }; Consumer.prototype.inspect = function() { return JSON.stringify(this, null, 2); }; Consumer.prototype.toJSON = function() { return { url: this.url, subscriptions: this.subscriptions, connection: this.connection, connectionMonitor: this.connectionMonitor }; }; return Consumer; })(); }).call(this);