(function () { var send = function (service, json) { if (json instanceof Object) json = codeq.jsonize(json); return Q.Promise(function (resolve, reject, notify) { $.ajax({ 'type': 'POST', 'url': codeq.urlPrefix + service, 'accepts': 'application/json', 'contentType': 'application/json; charset=UTF-8', // type of our request 'data': json, 'processData': false, // don't process outgoing data 'dataType': 'json', // expected type of the response 'timeout': 60000, // one minute 'error': function sendErrorHandler(jqXHR, textStatus, errorThrown) { reject(new Error(errorThrown || textStatus)); }, 'success': function sendSuccessHandler(data, textStatus, jqXHR) { resolve(data); } }); }); }; var activities = {}, // keyed by problem_id: activity info waiting to be sent to the server activityKeys = [], // the keys to activities dictionary resolveActivity = function (promiseResolvers) { var i; // signal everyone that the queue is flushed for (i = 0; i < promiseResolvers.length; i++) { try { promiseResolvers[i][0](); } catch (e) {} } promiseResolvers.length = 0; }, sendActivityInternal = function () { var activityKey, activityDescriptor, trace, sendCount; // send max. 100 activities, do not be excessive if (activityKeys.length == 0) return; activityKey = activityKeys[0]; activityDescriptor = activities[activityKey]; trace = activityDescriptor.trace; if (trace.length > 100) { sendCount = 100; trace = trace.slice(0, 100); } else { sendCount = trace.length; } send('activity', {'trace': trace, 'solution': activityDescriptor.solution, 'problem_id': activityDescriptor.problem_id, 'sid': codeq.sid}).then( function sendActivitySuccess() { activityDescriptor.trace.splice(0, sendCount); if (activityDescriptor.trace.length > 0) sendActivityInternal(); else { delete activities[activityKey]; activityKeys.shift(); resolveActivity(activityDescriptor.promiseResolvers); } }, function sendActivityFailure() { Q.delay(500).then(sendActivityInternal).done(); } ).done(); }; codeq.comms = { sendActivity: function commsSendActivity (trace, solution, problem_id) { return Q.Promise(function (resolve, reject, notify) { var triggerSending = activityKeys.length == 0, activityDescriptor = activities[problem_id], problemTrace, promiseResolvers, i; if (!activityDescriptor) { problemTrace = []; promiseResolvers = []; activityDescriptor = { 'problem_id': problem_id, 'trace': problemTrace, 'solution': solution, 'promiseResolvers': promiseResolvers }; activities[problem_id] = activityDescriptor; activityKeys.push(problem_id); } else { problemTrace = activityDescriptor.trace; promiseResolvers = activityDescriptor.promiseResolvers; if (solution) activityDescriptor.solution = solution; } if (trace instanceof Array) { for (i = 0; i < trace.length; i++) { problemTrace.push(trace[i]); } } else { problemTrace.push(trace); } promiseResolvers.push([resolve, reject]); if (triggerSending) { setTimeout(sendActivityInternal, 0); // async trigger: see if you can collect some more payload } }); }, sendQuery: function commsSendQuery (query, problem_id) { var existingActivity = activities[problem_id], tmp; query['sid'] = codeq.sid; if (existingActivity) { tmp = query['trace']; if (tmp) { // the trace will be sent separately, so it will be in the proper order tmp.splice(0, 0, existingActivity.trace.length, 0); // add two parameters in front, they will form the first two parameters to the following splice() invocation existingActivity.trace.splice.apply(existingActivity.trace, tmp); delete query['trace']; } tmp = query['program']; if (tmp) existingActivity.solution = tmp; } return send('query', query); }, sendHint: function commsSendHint (json) { json['sid'] = codeq.sid; return send('hint', json); }, sendTest: function commsSendTest (json) { json['sid'] = codeq.sid; return send('test', json); }, getProblem: function commsGetProblem (language, problem_group, problem) { return send('get_problem', { 'sid': codeq.sid, 'language': language, 'problem_group': problem_group, 'problem': problem }); } }; })();