(function ($) { "use strict"; function SessionManager(userInfo, client) { this._userInfo = userInfo; this._client = client; this._documentId = null; this._sessionId = null; this.onStateChange = function () {}; this.onContentsChange = function () {}; this._unsubscribeState = function () {}; this._unsubscribeContentsChange = function () {}; this._pendingActions = []; this._connecting = false; this._setupErrorHandlers(); } SessionManager.prototype._setupErrorHandlers = function () { var self = this; var originalOnStompError = this._client.onStompError; this._client.onStompError = function (frame) { if (originalOnStompError) { originalOnStompError.call(self._client, frame); } self._handleConnectionFailure(); }; var originalOnWebSocketError = this._client.onWebSocketError; this._client.onWebSocketError = function (event) { if (originalOnWebSocketError) { originalOnWebSocketError.call(self._client, event); } self._handleConnectionFailure(); }; }; SessionManager.prototype._handleConnectionFailure = function () { this._connecting = false; this._pendingActions = []; }; SessionManager.prototype._createSession = function () { return $.ajax({ url: "/api/sessions/create", type: "POST", contentType: "application/json;charset=utf-8", data: JSON.stringify({ creatorName: this._documentId }) }); }; SessionManager.prototype._setDocumentId = function (content) { return $.ajax({ url: "/api/sessions/" + this._sessionId + "/set-document", type: "POST", contentType: "application/json;charset=utf-8", data: JSON.stringify({ documentId: this._documentId, content: content }) }); }; SessionManager.prototype._ensureConnected = function (action) { if (!this._client) { console.error("SessionManager: client is not initialized"); return; } if (this._client.connected) { action.call(this); return; } this._pendingActions.push(action); if (this._connecting) { return; } this._connecting = true; var self = this; var originalOnConnect = this._client.onConnect; this._client.onConnect = function (frame) { if (originalOnConnect) { originalOnConnect.call(self._client, frame); } self._client.onConnect = originalOnConnect; self._connecting = false; var actions = self._pendingActions; self._pendingActions = []; actions.forEach(function (pendingAction) { try { pendingAction.call(self); } catch (e) { console.error("SessionManager: error executing pending action", e); } }); }; this._client.deactivate(); this._client.activate(); }; SessionManager.prototype._joinRoom = function () { var joinPayload = $.extend( {}, { sessionId: this._sessionId, documentId: this._documentId }, this._userInfo ); this._client.publish({ destination: "/app/join", body: JSON.stringify(joinPayload) }); }; SessionManager.prototype._subscribeState = function () { this._unsubscribeState(); var self = this; var TOPIC_URL = "/topic/sessions/" + this._sessionId + "/state/document/" + this._documentId; var subscription = this._client.subscribe(TOPIC_URL, function (message) { try { var response = JSON.parse(message.body); self.onStateChange(response.participants); } catch (e) { console.error("SessionManager: invalid state message", e); } }); this._unsubscribeState = subscription.unsubscribe.bind(subscription); }; SessionManager.prototype._subscribeContentsChange = function () { this._unsubscribeContentsChange(); var self = this; var TOPIC_URL = "/topic/sessions/" + this._sessionId + "/selections/document/" + this._documentId; var subscription = this._client.subscribe(TOPIC_URL, function (message) { try { var response = JSON.parse(message.body); if (response.userInfo.id !== self._userInfo.userId) { self.onContentsChange(response.userInfo); } } catch (e) { console.error("SessionManager: invalid contents message", e); } }); this._unsubscribeContentsChange = subscription.unsubscribe.bind(subscription); }; SessionManager.prototype._leaveRoom = function () { var leavePayload = $.extend( {}, { sessionId: this._sessionId, documentId: this._documentId }, this._userInfo ); this._client.publish({ destination: "/app/leave", body: JSON.stringify(leavePayload) }); }; SessionManager.prototype.remoteUserUpdate = function (data) { if (!this._sessionId || !this._documentId) { return; } this._ensureConnected(function () { var payload = { documentId: this._documentId, sessionId: this._sessionId, userInfo: { id: this._userInfo.userId, name: this._userInfo.userName.trim(), color: this._userInfo.userColor, selection: { message: data } } }; this._client.publish({ destination: "/app/selection", body: JSON.stringify(payload) }); }); }; SessionManager.prototype.setRoom = function (documentId, content) { this._documentId = documentId; var self = this; return this._createSession() .then(function (sessionInfo) { self._sessionId = sessionInfo.sessionId; return self._setDocumentId(content); }) .fail(function (error) { console.error("SessionManager: failed to set room", error); self._sessionId = null; }); }; SessionManager.prototype.closeRoom = function () { this._unsubscribeState(); this._unsubscribeContentsChange(); if (this._sessionId && this._documentId) { this._ensureConnected(function () { this._leaveRoom(); }); } this._sessionId = null; this._documentId = null; }; SessionManager.prototype.openWebsocket = function () { if (!this._sessionId || !this._documentId) { console.error("SessionManager: sessionId or documentId is not set. Call setRoom first."); return; } this._unsubscribeState(); this._unsubscribeContentsChange(); this._ensureConnected(function () { this._subscribeState(); this._subscribeContentsChange(); this._joinRoom(); }); }; var sessionManager = function (Client, SockJS, userInfo) { var SOCKET_URL = "/ws"; var client = new Client({ brokerURL: SOCKET_URL, webSocketFactory: function () { return new SockJS(SOCKET_URL); }, debug: function (str) { console.debug(str); }, onConnect: function (frame) { console.log("Connected: " + frame); }, onStompError: function (frame) { console.error("Broker reported error: " + frame.headers["message"]); console.error("Additional details: " + frame.body); }, onWebSocketError: function (event) { console.error("Web Socket Error: ", event); }, onDisconnect: function () { console.log("Disconnected"); }, reconnectDelay: 5000 }); client.activate(); return new SessionManager(userInfo, client); }; $.sessionManager = sessionManager; })(window.jQuery);