"use strict"; /** leak object to top level scope **/ var ccLog = undefined; // global code-mirror logger;, to enable run // Logger.get('ConnectionController').setLevel(Logger.DEBUG) ccLog = Logger.get('ConnectionController'); ccLog.setLevel(Logger.OFF); var ConnectionController; var connCtrlr; var registerViewChannels; (function () { ConnectionController = function (webappUrl, serverUrl, urls) { var self = this; var channels = ['broadcast']; this.state = { open: false, webappUrl: webappUrl, serverUrl: serverUrl, connId: null, socket: null, channels: channels, heartbeat: null, channelsInfo: {}, urls: urls }; this.channelNameParsers = []; this.addChannelNameParser = function (fn) { if (this.channelNameParsers.indexOf(fn) === -1) { this.channelNameParsers.push(fn); } }; this.listen = function () { if (window.WebSocket) { ccLog.debug('attempting to create socket'); var socket_url = self.state.serverUrl + "/ws?conn_id=" + self.state.connId; var socket_conf = { url: socket_url, handleAs: 'json', headers: { "Accept": "application/json", "Content-Type": "application/json" } }; self.state.socket = new WebSocket(socket_conf.url); self.state.socket.onopen = function (event) { ccLog.debug('open event', event); if (self.state.heartbeat === null) { self.state.heartbeat = setInterval(function () { if (self.state.socket.readyState === WebSocket.OPEN) { self.state.socket.send('heartbeat'); } }, 10000) } }; self.state.socket.onmessage = function (event) { var data = $.parseJSON(event.data); for (var i = 0; i < data.length; i++) { if (data[i].message.topic) { ccLog.debug('publishing', data[i].message.topic, data[i]); $.Topic(data[i].message.topic).publish(data[i]) } else { ccLog.warn('unhandled message', data); } } }; self.state.socket.onclose = function (event) { ccLog.debug('closed event', event); setTimeout(function () { self.connect(true); }, 5000); }; self.state.socket.onerror = function (event) { ccLog.debug('error event', event); }; } else { ccLog.debug('attempting to create long polling connection'); var poolUrl = self.state.serverUrl + "/listen?conn_id=" + self.state.connId; self.state.socket = $.ajax({ url: poolUrl }).done(function (data) { ccLog.debug('data', data); var data = $.parseJSON(data); for (var i = 0; i < data.length; i++) { if (data[i].message.topic) { ccLog.info('publishing', data[i].message.topic, data[i]); $.Topic(data[i].message.topic).publish(data[i]) } else { ccLog.warn('unhandled message', data); } } self.listen(); }).fail(function () { ccLog.debug('longpoll error'); setTimeout(function () { self.connect(true); }, 5000); }); } }; this.connect = function (create_new_socket) { var connReq = {'channels': self.state.channels}; ccLog.debug('try obtaining connection info', connReq); $.ajax({ url: self.state.urls.connect, type: "POST", contentType: "application/json", data: JSON.stringify(connReq), dataType: "json" }).done(function (data) { ccLog.debug('Got connection:', data.conn_id); self.state.channels = data.channels; self.state.channelsInfo = data.channels_info; self.state.connId = data.conn_id; if (create_new_socket) { self.listen(); } self.update(); }).fail(function () { setTimeout(function () { self.connect(create_new_socket); }, 5000); }); self.update(); }; this.subscribeToChannels = function (channels) { var new_channels = []; for (var i = 0; i < channels.length; i++) { var channel = channels[i]; if (self.state.channels.indexOf(channel)) { self.state.channels.push(channel); new_channels.push(channel) } } /** * only execute the request if socket is present because subscribe * can actually add channels before initial app connection **/ if (new_channels && self.state.socket !== null) { var connReq = { 'channels': self.state.channels, 'conn_id': self.state.connId }; $.ajax({ url: self.state.urls.subscribe, type: "POST", contentType: "application/json", data: JSON.stringify(connReq), dataType: "json" }).done(function (data) { self.state.channels = data.channels; self.state.channelsInfo = data.channels_info; self.update(); }); } self.update(); }; this.update = function () { for (var key in this.state.channelsInfo) { if (this.state.channelsInfo.hasOwnProperty(key)) { // update channels with latest info $.Topic('/connection_controller/channel_update').publish( {channel: key, state: this.state.channelsInfo[key]}); } } /** * checks current channel list in state and if channel is not present * converts them into executable "commands" and pushes them on topics */ for (var i = 0; i < this.state.channels.length; i++) { var channel = this.state.channels[i]; for (var j = 0; j < this.channelNameParsers.length; j++) { this.channelNameParsers[j](channel); } } }; this.run = function () { this.connect(true); }; $.Topic('/connection_controller/subscribe').subscribe( self.subscribeToChannels); }; $.Topic('/plugins/__REGISTER__').subscribe(function (data) { // enable chat controller if (window.CHANNELSTREAM_SETTINGS && window.CHANNELSTREAM_SETTINGS.enabled) { $(document).ready(function () { connCtrlr.run(); }); } }); registerViewChannels = function (){ // subscribe to PR repo channel for PR's' if (templateContext.pull_request_data.pull_request_id) { var channelName = '/repo$' + templateContext.repo_name + '$/pr/' + String(templateContext.pull_request_data.pull_request_id); connCtrlr.state.channels.push(channelName); } } })();