diff --git a/app/assets/javascripts/AAA_Log.js b/app/assets/javascripts/AAA_Log.js new file mode 100644 index 000000000..4341bb9b0 --- /dev/null +++ b/app/assets/javascripts/AAA_Log.js @@ -0,0 +1,25 @@ +(function(context) { + + /* + internal logger with no-ops when console is missing. + */ + context.JK = context.JK || {}; + + var console_methods = [ + 'log', 'debug', 'info', 'warn', 'error', 'assert', + 'clear', 'dir', 'dirxml', 'trace', 'group', + 'groupCollapsed', 'groupEnd', 'time', 'timeEnd', + 'timeStamp', 'profile', 'profileEnd', 'count', + 'exception', 'table' + ]; + + if ('undefined' === typeof(context.console)) { + context.console = {}; + $.each(console_methods, function(index, value) { + context.console[value] = $.noop; + }); + } + + context.JK.logger = context.console; + +}(window)); \ No newline at end of file diff --git a/app/assets/javascripts/AAB_message_factory.js b/app/assets/javascripts/AAB_message_factory.js new file mode 100644 index 000000000..b3aaaeefe --- /dev/null +++ b/app/assets/javascripts/AAB_message_factory.js @@ -0,0 +1,83 @@ +/* + Message builder for communicating over the websocket + */ +(function(context, $) { + + context.JK = context.JK || {}; + + var msg = context.JK.MessageType = { + LOGIN : "LOGIN", + LOGIN_ACK : "LOGIN_ACK", + LOGIN_MUSIC_SESSION : "LOGIN_MUSIC_SESSION", + LOGIN_MUSIC_SESSION_ACK : "LOGIN_MUSIC_SESSION_ACK", + USER_JOINED_MUSIC_SESSION : "USER_JOINED_MUSIC_SESSION", + LEAVE_MUSIC_SESSION : "LEAVE_MUSIC_SESSION", + LEAVE_MUSIC_SESSION_ACK : "LEAVE_MUSIC_SESSION_ACK", + HEARTBEAT : "HEARTBEAT", + TEST_SESSION_MESSAGE : "TEST_SESSION_MESSAGE", + PING_REQUEST : "PING_REQUEST", + PING_ACK : "PING_ACK", + PEER_MESSAGE : "PEER_MESSAGE", + SERVER_GENERIC_ERROR : "SERVER_GENERIC_ERROR", + SERVER_REJECTION_ERROR : "SERVER_REJECTION_ERROR" + }; + + var route_to = context.JK.RouteToPrefix = { + CLIENT : "client", + SESSION : "session", + SERVER : "server", + USER : "user" + }; + + var factory = {}; + + function client_container(type, target, inner) { + var type_field = type.toLowerCase(); + var body = { "type" : type, "route_to" : target}; + body[type_field] = inner; + return body; + } + + function route_to_client(client_id) { + return route_to.CLIENT + ":" + client_id; + } + + function route_to_session(session_id) { + return route_to.SESSION + ":" + session_id; + } + + // ping the provided client_id + factory.ping = function(client_id) { + var data = {}; + return client_container(msg.PING_REQUEST, route_to_client(client_id), data); + }; + + // create a login message using user/pass + factory.login_with_user_pass = function(username, password) { + var login = { username : username , password : password }; + return client_container(msg.LOGIN, route_to.SERVER, login); + }; + + // create a login message using token (a cookie or similiar) + factory.login_with_token = function(token) { + var login = { token : token }; + return client_container(msg.LOGIN, route_to.SERVER, login); + }; + + // create a music session login message + factory.login_music_session = function(music_session) { + var login_music_session = { music_session : music_session }; + return client_container(msg.LOGIN_MUSIC_SESSION, route_to.SERVER, login_music_session); + }; + + // client-to-client message + factory.client_p2p_message = function(sender_client_id, receiver_client_id, message) { + var peer_message = { "message" : message }; + var result = client_container(msg.PEER_MESSAGE, route_to_client(receiver_client_id), peer_message); + result.from = sender_client_id; + return result; + }; + + context.JK.MessageFactory = factory; + +})(window, jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/JamServer.js b/app/assets/javascripts/JamServer.js index c17d9c462..c5f5889a3 100644 --- a/app/assets/javascripts/JamServer.js +++ b/app/assets/javascripts/JamServer.js @@ -1,25 +1,26 @@ // The wrapper around the web-socket connection to the server -(function(global, $) { - var server = {}; - +(function(context, $) { + context.JK = context.JK || {}; + + var logger = context.JK.logger; + var msg_factory = context.JK.MessageFactory; + // Let socket.io know where WebSocketMain.swf is WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf"; + var server = {}; server.socket = {}; server.singedIn = false; server.dispatchTable = {}; - // TODO: Create the message factory here - //server.messageFactory = global.message_factory; - server.registerMessageCallback = function(messageType, callback) { if (server.dispatchTable[messageType] === undefined) { server.dispatchTable[messageType] = []; } - + server.dispatchTable[messageType].push(callback); }; - + server.unregisterMessageCallback = function(messageType, callback) { if (server.dispatchTable[messageType] !== undefined) { for(var i = server.dispatchTable.length; i--;) { @@ -30,67 +31,120 @@ } } } - - if (server.dispatchTable[messageType].length == 0) { + + if (server.dispatchTable[messageType].length === 0) { delete server.dispatchTable[messageType]; } }; server.connect = function() { - server.registerMessageCallback(LOGIN_ACK, function() { server.signedIn = true; }); - + logger.log("server.connect"); server.socket = new WebSocket(gon.websocket_gateway_uri); server.socket.onopen = server.onOpen; server.socket.onmessage = server.onMessage; server.socket.onclose = server.onClose; }; - + server.onOpen = function() { + logger.log("server.onOpen"); var token, loginMessage; - token = $.cookie("remember_token"); - loginMessage = global.message_factory.login_with_token(token); - + loginMessage = msg_factory.login_with_token(token); server.send(loginMessage); }; server.onMessage = function(e) { + logger.log("server.onMessage"); var message = JSON.parse(e.data), - payload = message[message.type.toLowerCase()], + messageType = message.type.toLowerCase(), + payload = message[messageType], callbacks = server.dispatchTable[message.type]; - + if (callbacks !== undefined) { - for(var i = callbacks.length; i--;) { - callbacks[i](payload); + var len = callbacks.length; + for(var i = 0; i < len; i++) { + callbacks[i](messageType, payload); } } else { - console.log("Unexpected message type %s.", message.type); + logger.log("Unexpected message type %s.", message.type); } }; server.onClose = function() { - console.log("Socket to server closed."); + logger.log("Socket to server closed."); + + if (context.jamClient !== undefined) + { + context.jamClient.connected = false; + } + // TODO: reconnect }; - + server.send = function(message) { - server.socket.send(JSON.stringify(message)); + var jsMessage = JSON.stringify(message); + logger.log("server.send(" + jsMessage + ")"); + if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) { + server.socket.send(jsMessage); + } else { + logger.log("Dropped message because server connection is closed."); + } + }; - + server.loginSession = function(sessionId) { var loginMessage; - + if (!signedIn) { - console.log("Not signed in!"); + logger.log("Not signed in!"); // TODO: surface the error return; } - - loginMessage = global.message_factory.login_jam_session(sessionId); + + loginMessage = msg_factory.login_jam_session(sessionId); server.send(loginMessage); }; - global.JamServer = server; + server.sendP2PMessage = function(receiver_id, message) { + logger.log("P2P message from [" + server.clientID + "] to [" + receiver_id + "]: " + message); + var outgoing_msg = msg_factory.client_p2p_message(server.clientID, receiver_id, message); + server.send(outgoing_msg); + }; + + context.JK.JamServer = server; -})(window, jQuery); + // Message callbacks + server.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, function(type, payload) { + server.signedIn = true; + server.clientID = payload.client_id; + + if (context.jamClient !== undefined) + { + context.jamClient.connected = true; + context.jamClient.clientID = server.clientID; + } + }); + + server.registerMessageCallback(context.JK.MessageType.PEER_MESSAGE, function(type, payload) { + if (context.jamClient !== undefined) + { + context.jamClient.P2PMessageReceived(payload.sender_id, payload.message); + } + }); + + server.registerMessageCallback(context.JK.MessageType.LOGIN_MUSIC_SESSION_ACK, function(type, payload) { + if (context.jamClient !== undefined) + { + // TODO: modify the LOGIN_MUSIC_SESSION_ACK message to include session_id + context.jamClient.JoinSession({ sessionID : payload.session_id}); + } + }); + + // Callbacks from jamClient + if (context.jamClient !== undefined) + { + context.jamClient.SendP2PMessage.connect(server.sendP2PMessage); + } + +})(window, jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/createSession.js b/app/assets/javascripts/createSession.js index 5c5d4ee93..175813407 100644 --- a/app/assets/javascripts/createSession.js +++ b/app/assets/javascripts/createSession.js @@ -2,6 +2,7 @@ context.JK = context.JK || {}; context.JK.CreateSessionScreen = function(app) { + var logger = context.JK.logger; function afterShow(data) {} @@ -27,9 +28,11 @@ $('#create-session-form').submit(submitForm); } - events(); - screenBindings = { 'afterShow': afterShow }; - app.bindScreen('session', screenBindings); + this.initialize = function() { + events(); + screenBindings = { 'afterShow': afterShow }; + app.bindScreen('session', screenBindings); + }; }; diff --git a/app/assets/javascripts/findSession.js b/app/assets/javascripts/findSession.js index b4f672deb..b811f4c5f 100644 --- a/app/assets/javascripts/findSession.js +++ b/app/assets/javascripts/findSession.js @@ -2,6 +2,7 @@ context.JK = context.JK || {}; context.JK.FindSessionScreen = function(app) { + var logger = context.JK.logger; function loadSessions() { $.ajax({ @@ -43,11 +44,13 @@ $('#findSession-tableBody').on("click", '[action="delete"]', deleteSession); } - screenBindings = { - 'afterShow': afterShow + this.initialize = function() { + screenBindings = { + 'afterShow': afterShow + }; + app.bindScreen('findSession', screenBindings); + events(); }; - app.bindScreen('findSession', screenBindings); - events(); }; })(window,jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/jamkazam.js b/app/assets/javascripts/jamkazam.js index e4b843ff5..4b60fa0be 100644 --- a/app/assets/javascripts/jamkazam.js +++ b/app/assets/javascripts/jamkazam.js @@ -5,6 +5,7 @@ var JamKazam = context.JK.JamKazam = function() { var app; + var logger = context.JK.logger; function routing() { var routes = window.RouteMap, rules, rule; diff --git a/app/assets/javascripts/message_factory.js b/app/assets/javascripts/message_factory.js deleted file mode 100644 index 046fb0141..000000000 --- a/app/assets/javascripts/message_factory.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - Message builder for communicating over the websocket - */ -(function() { - - CLIENT_TARGET = "client" - SERVER_TARGET = "server" - SESSION_TARGET_PREFIX = "session:" - USER_TARGET_PREFIX = "user:" - - LOGIN = "LOGIN" - LOGIN_ACK = "LOGIN_ACK" - LOGIN_MUSIC_SESSION = "LOGIN_MUSIC_SESSION" - LOGIN_MUSIC_SESSION_ACK = "LOGIN_MUSIC_SESSION_ACK" - USER_JOINED_MUSIC_SESSION = "USER_JOINED_MUSIC_SESSION" - LEAVE_MUSIC_SESSION = "LEAVE_MUSIC_SESSION" - LEAVE_MUSIC_SESSION_ACK = "LEAVE_MUSIC_SESSION_ACK" - HEARTBEAT = "HEARTBEAT" - - TEST_SESSION_MESSAGE = "TEST_SESSION_MESSAGE" - SERVER_GENERIC_ERROR = "SERVER_GENERIC_ERROR" - SERVER_REJECTION_ERROR = "SERVER_REJECTION_ERROR" - - var message_factory = {} - - function client_container(type, target, inner) { - var type_field = type.toLowerCase() - var body = { "type" : type, "target" : target} - body[type_field] = inner - return body - } - - // create a login message using user/pass - message_factory.login_with_user_pass = function(username, password) { - login = { username : username , password : password} - return client_container(LOGIN, SERVER_TARGET, login) - } - - // create a login message using token (a cookie or similiar) - message_factory.login_with_token = function(token) { - login = { token : token} - return client_container(LOGIN, SERVER_TARGET, login) - } - - // create a music session login message - message_factory.login_music_session = function(music_session) { - login_music_session = { music_session : music_session } - return client_container(LOGIN_MUSIC_SESSION, SERVER_TARGET, login_music_session) - } - - window.message_factory = message_factory -})(); \ No newline at end of file diff --git a/app/assets/javascripts/messaging.js b/app/assets/javascripts/messaging.js new file mode 100644 index 000000000..3e3a6f220 --- /dev/null +++ b/app/assets/javascripts/messaging.js @@ -0,0 +1,66 @@ +/** +* A messaging class for handling websocket messages and taking the +* proper web-ui actions. Anything more generic related to message +* definitions, etc. should be in other places not tied to our UI. +*/ +(function(context) { + + context.JK = context.JK || {}; + context.JK.Messaging = function(app) { + + if ("undefined" === typeof(context.JK.JamServer)) + return; + + // Alias some of the globals for less typing. + var logger = context.JK.logger; + var server = context.JK.JamServer; + var messages = context.JK.MessageType; + var msg_factory = context.JK.MessageFactory; + + var myClientId = null; + var myPingTimer = null; + var pingCount = 0; + var maxPings = 5; + + function logMessage(messageType, payload) { + logger.debug(messageType + ": " + JSON.stringify(payload)); + } + + function pingMyself() { + if (!myClientId) + return; + pingCount++; + message = msg_factory.ping(myClientId); + server.send(message); + if (pingCount > maxPings) { + context.clearInterval(myPingTimer); + myPingTimer = null; + } + } + + function loggedIn(messageType, payload) { + logger.debug('Logged In handler: ' + messageType + ':' + JSON.stringify(payload)); + myClientId = payload.client_id; + myPingTimer = context.setInterval(pingMyself, 1000); + } + + function registerLoginPinger() { + logger.debug("registering login -> pinger"); + server.registerMessageCallback(messages.LOGIN_ACK, loggedIn); + } + + /** + * Register a simple console logger for all known message types. + */ + this.register = function() { + for (var message in messages) { + logger.debug("registering " + message); + server.registerMessageCallback(message, logMessage); + } + + registerLoginPinger(); + }; + + }; + + })(window); \ No newline at end of file diff --git a/app/assets/javascripts/session.js b/app/assets/javascripts/session.js index 10a02611b..6e0b7c39d 100644 --- a/app/assets/javascripts/session.js +++ b/app/assets/javascripts/session.js @@ -2,6 +2,7 @@ context.JK = context.JK || {}; context.JK.SessionScreen = function(app) { + var logger = context.JK.logger; function afterShow(data) { var sessionId = data.id; @@ -35,11 +36,13 @@ $('#session-contents').on("click", '[action="delete"]', deleteSession); } - events(); - screenBindings = { - 'afterShow': afterShow + this.initialize = function() { + events(); + screenBindings = { + 'afterShow': afterShow + }; + app.bindScreen('session', screenBindings); }; - app.bindScreen('session', screenBindings); }; diff --git a/app/views/clients/index.html.erb b/app/views/clients/index.html.erb index 0493d746d..7e9086a9f 100644 --- a/app/views/clients/index.html.erb +++ b/app/views/clients/index.html.erb @@ -106,7 +106,8 @@
- Unstyled (obviously) + Unstyled and Lots of things omitted +
+