diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/codeq/comms.js | 130 | ||||
-rw-r--r-- | js/codeq/core.js | 17 | ||||
-rw-r--r-- | js/codeq/login.js | 6 | ||||
-rw-r--r-- | js/codeq/problem_list.js | 2 |
4 files changed, 116 insertions, 39 deletions
diff --git a/js/codeq/comms.js b/js/codeq/comms.js index 54a912f..54f5d24 100644 --- a/js/codeq/comms.js +++ b/js/codeq/comms.js @@ -69,7 +69,8 @@ } }, onMessage = function (data) { - var m, waiter, event, handlers, i, N; + var m, waiter, event, handlers, i, N, + tid, sortedWaiters = [], s; try { m = parseData(data); @@ -79,6 +80,68 @@ return; } + if ('type' in m) { + // system (meta) protocol + switch (m.type) { + case 'sid': + // the peer created or changed our session ID + sid = m.sid; + break; + + case 'reset': + // the server forced us to reset to the login page + // * could not re-register the connection with an existing session ID, username, and password + // * the user logged in elsewhere + s = 'The server forced us to disconnect'; + if (typeof m.message === 'string') s += ': ' + m.message; + codeq.reset(s); + return; + + default: + codeq.log.warn('Unknown system message type: ' + m.type); + } + if (!connected) { + if (sid !== null) { + // our connection registration was successful + connected = true; + if (connectPromise) { + // manual connect + connectPromise.resolve(); + connectPromise = null; + } + else { + // auto re-connect + } + // resend anything not already sent + for (tid in waiters) { + if (waiters.hasOwnProperty(tid)) { + sortedWaiters.push(waiters[tid]); + } + } + // sort by ascending transaction ID before sending + sortedWaiters.sort(function (a, b) {return a.tid - b.tid;}); + for (i = 0; i < sortedWaiters.length; i++) { + (function (waiter) { + if (!waiter.sent) { + socket.send(JSON.stringify(waiter.packet), function () { + waiter.sent = true; + }); + } + })(sortedWaiters[i]); + } + } + else { + codeq.log.error('Operating outside a session!'); + codeq.reset('Protocol error: server sent a bogus message instead of acknowledging our connection'); + } + } + return; + } + + if (!connected) { + codeq.log.warn('Not in the connected state, but receiving a user message anyway'); + } + if ('tid' in m) { waiter = waiters[m.tid]; if (waiter) { @@ -112,33 +175,9 @@ } }, onConnect = function () { - var tid, sortedWaiters = [], i; - connected = true; - if (connectPromise) { - // manual connect - connectPromise.resolve(); - connectPromise = null; - } - else { - // auto re-connect - } - // resend anything not already sent - for (tid in waiters) { - if (waiters.hasOwnProperty(tid)) { - sortedWaiters.push(waiters[tid]); - } - } - // sort by ascending transaction ID before sending - sortedWaiters.sort(function (a, b) {return a.tid - b.tid;}); - for (i = 0; i < sortedWaiters.length; i++) { - (function (waiter) { - if (!waiter.sent) { - socket.send(JSON.stringify(waiter.packet), function () { - waiter.sent = true; - }); - } - })(sortedWaiters[i]); - } + // register our connection + if (sid === null) socket.send(JSON.stringify({'type': 'create_session'})); + else socket.send(JSON.stringify({'type': 'connect_session', 'sid': sid, 'username': login_username, 'password': login_password})); }, onClose = function () { connected = false; @@ -209,6 +248,21 @@ ajaxResPrefix = ajaxPrefix + 'res/'; // ================================================================================ + // Try to be polite and logout on page close/reload + // ================================================================================ + + window.onbeforeunload = function () { + if (sid) { + // we need to perform an action inside the current iteration of the event loop + // so no fancy promises etc., just the raw AJAX request + $.ajax({ + type: 'GET', + url: '/ws/logout?sid=' + sid + '&t=' + Date.now() + }); + } + }; + + // ================================================================================ // This module's API methods // ================================================================================ @@ -245,7 +299,8 @@ return deferred.promise; }, - 'disconnect': function () { + 'disconnect': function (reason) { // reason is optional + var tid; if (socket) { socket.off('open', onConnect); socket.off('close', onClose); @@ -264,6 +319,13 @@ } login_password = null; // manual disconnect will prevent auto-login sid = null; + // reject any outstanding promises + for (tid in waiters) { + if (waiters.hasOwnProperty(tid)) { + waiters[id].promise.reject(new Error(reason || 'Forced disconnect')); + delete waiters[tid]; + } + } }, 'send': function (packet) { return Q.Promise(function (resolve, reject, notify) { @@ -321,7 +383,7 @@ }, 'updateSettings': function (new_settings){ - return this.send({'action': 'settings', 'sid': sid, 'settings': new_settings}); + return this.send({'action': 'update_settings', 'settings': new_settings}); }, sendActivity: function commsSendActivity (trace, solution, problem_id) { @@ -358,12 +420,10 @@ return this.send(json); }, - getProblem: function commsGetProblem (language, problem_group, problem) { + getCurrentSolution: function commsGetCurrentSolution (problem_id) { return this.send({ - 'action': 'get_problem', - 'language': language, - 'problem_group': problem_group, - 'problem': problem + 'action': 'get_current_solution', + 'problem_id': problem_id }); }, diff --git a/js/codeq/core.js b/js/codeq/core.js index 02bc2f0..d60c7e4 100644 --- a/js/codeq/core.js +++ b/js/codeq/core.js @@ -569,6 +569,23 @@ if (listeners[i] === callback) listeners.splice(i, 1); } } + }, + + // reset the app + 'reset': function (reason) { + codeq.log.info('App reset: ' + (reason || 'no reason given')); + codeq.globalStateMachine.transition('login'); + codeq.wait( + codeq.comms.logout() + .finally(codeq.comms.disconnect) + .fail(function () {}) // ignore errors + ) + .then(function () { + if (reason) { + alert(reason); + } + }) + .done(); } }; })(); diff --git a/js/codeq/login.js b/js/codeq/login.js index d5bcd90..538cd3d 100644 --- a/js/codeq/login.js +++ b/js/codeq/login.js @@ -21,9 +21,9 @@ //merge these settings into the already existing default settings var sett = data.settings; $.merge(true, codeq.settings, sett); - if('lan' in sett && sett['lan'] in codeq.supportedLangs){ - codeq.setLang(sett['lan']); - $("#gui_lang_select").val(sett['lan']); + if('lang' in sett && sett['lang'] in codeq.supportedLangs){ + codeq.setLang(sett['lang']); + $("#gui_lang_select").val(sett['lang']); } codeq.globalStateMachine.transition('language'); diff --git a/js/codeq/problem_list.js b/js/codeq/problem_list.js index 1b20fe2..eae86ed 100644 --- a/js/codeq/problem_list.js +++ b/js/codeq/problem_list.js @@ -260,7 +260,7 @@ } codeq.wait( Q.all([ - codeq.comms.getProblem(language, ref.g, ref.p), // TODO: use ref.id instead // the current solution + codeq.comms.getCurrentSolution(ref.id), // the current user's solution to the selected problem getProblemData(language, ref.g, ref.p) // the (cached) result of processProblemData() ]) .spread(function (userProblemData, generalProblemData) { |