/** * Created by robert on 9/17/15. */ (function() { var problems,//this will the actual (sub)state machine stateNameTag = 'stateName'; var divs = {}; divs['description'] = $('div.col-lg-3.col-md-6.col-sm-12:has(#description)');//lets actually find the needed divs for later use divs['code'] = $('div.col-lg-3.col-md-6.col-sm-12:has(#code_editor)'); divs['console'] = $('div.col-lg-3.col-md-6.col-sm-12:has(#console)');//named 'consoleDiv', because 'console' is already in use divs['info'] = $('div.col-lg-3.col-md-6.col-sm-12:has(#info)'); divs['description'].data(stateNameTag, 'description'); divs['code'].data(stateNameTag, 'code'); divs['console'].data(stateNameTag, 'console'); divs['info'].data(stateNameTag, 'info'); var eventName = 'mousedown',//event name of the event which will trigger the transition between these substates mouseDownEventFunction = function () { problems.transition($(this).data(stateNameTag)); }, removeListenersFromDivs = function () { $.each(divs, function (i, value) { value.off(eventName, mouseDownEventFunction); }); }, removeBlockClassesFromDivs = function () { $.each(divs, function (i, value) { value.removeClass('block'); }); }, removeChangedClassesFromDivs = function () { $.each(divs, function (i, value) { value.removeClass('block-focus block-less-width block-less-height block-less-everything').addClass('block');//these class names were chosen for the view where the screen is partitioned in 4 quarters (2 by 2) }); }, setTransitionsBetweenDivs = function (current) { $.each(divs, function (key, value) { if (current !== key) value.on(eventName, mouseDownEventFunction); }); }, addClassesToDivs = function (divsWithClasses) { $.each(divsWithClasses, function (divName, className) { divs[divName].addClass(className); }); }, substates = { 'description': { 'enter': function () { removeBlockClassesFromDivs(); addClassesToDivs({ 'description': 'block-focus', 'code': 'block-less-width', 'console': 'block-less-height', 'info': 'block-less-everything' }); setTransitionsBetweenDivs('description'); }, 'exit': function () { removeChangedClassesFromDivs(); removeListenersFromDivs(); } }, 'code': { 'enter': function () { removeBlockClassesFromDivs(); addClassesToDivs({ 'description': 'block-less-width', 'code': 'block-focus', 'console': 'block-less-everything', 'info': 'block-less-height' }); setTransitionsBetweenDivs('code'); }, 'exit': function () { removeChangedClassesFromDivs(); removeListenersFromDivs(); } }, 'info': { 'enter': function () { removeBlockClassesFromDivs(); addClassesToDivs({ 'description': 'block-less-everything', 'code': 'block-less-height', 'console': 'block-less-width', 'info': 'block-focus' }); setTransitionsBetweenDivs('info'); }, 'exit': function () { removeChangedClassesFromDivs(); removeListenersFromDivs(); } }, 'console': { 'enter': function () { removeBlockClassesFromDivs(); addClassesToDivs({ 'description': 'block-less-height', 'code': 'block-less-everything', 'console': 'block-focus', 'info': 'block-less-width' }); setTransitionsBetweenDivs('console'); }, 'exit': function () { removeChangedClassesFromDivs(); removeListenersFromDivs(); } } }; var prologHandler; codeq.globalStateMachine.register('prolog', { 'enter': function (data) { $('#disabled').css('display', ''); $('#screen_prolog').css('display', '');//we have to show the screen now so the code editor shopws its initial values correctly prologHandler = createPrologHandler(data.data)//codeq.createProgrammingLanguageHandler('prolog',data.data,codeq.makePrologTerminalHandler); problems = codeq.makeStateMachine(substates); problems.transition(divs['description'].data(stateNameTag)); Q.delay(100).then(function(){ $('div.col-lg-3.col-md-6.col-sm-12').addClass('transition'); }).done(); $('#disabled').css('display', 'none'); }, 'exit': function () { $('#screen_prolog').css('display', 'none'); $('div.col-lg-3.col-md-6.col-sm-12').removeClass('transition'); prologHandler.destroy(); problems.destroy(); } }); // a constant var firstCharacterPos = {'line': 0, 'ch': 0}; //codeq.makePrologTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) { var makePrologTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) { var promptMode = true, // default: query composition; alternative: query result browsing terminal = codeq.makeConsole(jqConsole, { 'greeting': 'CodeQ Prolog terminal proxy' }), tcs = function terminalCommandSuccess (data) { var t, lines, i; if (data.code === 0) { t = data.terminal; terminal.append(t.messages.join('\n'), 'output'); promptMode = !t.have_more; } else { terminal.append(data.message, 'error'); promptMode = true; } if (promptMode) { terminal.setLineBuffered(); terminal.append('.\n?- ', 'output'); } }, tcf = function terminalCommandFailed (error) { promptMode = true; terminal.setLineBuffered(); terminal.append(error + '\n', 'error'); terminal.append('?- ', 'output'); }; terminal.onKeypress = function (c) { if (promptMode) return c; // query composition: return the character unchanged switch (c) { case ' ': case ';': case '\n': return ';'; // show next answer on space, semicolon or enter default: return '.'; // everything else: stop searching for answers } }; terminal.onInput = function (command) { if (promptMode) { promptMode = false; terminal.setNotBuffered(); return codeq.comms.sendQuery({ 'problem_id': problem_id, 'step': 'run', 'program': editor.getDoc().getValue(), 'query': command, 'trace': activityHandler.addAndPurge({'typ': 'slv', 'qry': command}) }, problem_id).then(tcs, tcf); } else { terminal.append('\n', 'input'); if (command == ';') { // show next answer return codeq.comms.sendQuery({ 'problem_id': problem_id, 'step': 'next', 'trace': activityHandler.addAndPurge({'typ': 'nxt'}) }, problem_id).then(tcs, tcf); } else { // stop searching for answers return codeq.comms.sendQuery({ 'problem_id': problem_id, 'step': 'end', 'trace': activityHandler.addAndPurge({'typ': 'stp'}) }, problem_id).then(tcs, tcf); } } }; terminal.leftmostCol = 3; terminal.append('?- ', 'output'); return terminal; }; var makeActivityHandler = function (editor, problem_id) { var lastActivityMillis = Date.now(), deltaActivityMillis = function deltaActivityMillisFunc () { var now = Date.now(), dt = now - lastActivityMillis; lastActivityMillis = now; return dt; }, queue = [], ts = null, timer = function () { var promise; ts = null; if (queue.length === 0) return Q(true); promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue(), problem_id); queue.length = 0; return promise; }, flush = function () { clearTimeout(ts); return timer(); }; return { 'queueTrace': function (trace) { trace['dt'] = deltaActivityMillis(); queue.push(trace); if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds return this; }, 'flush': flush, 'addAndPurge': function (trace) { var accumulatedTrace = queue; queue = []; trace['dt'] = deltaActivityMillis(); accumulatedTrace.push(trace); if (ts !== null) { clearTimeout(ts); ts = null; } return accumulatedTrace; } }; }; /** * Creates a new handler for the given Prolog assignment definition. * * @param {PrologTaskDef} info * @returns {{destroy: Function, processServerHints: Function}} */ var createPrologHandler = function (info) { var problem = info.problem, jqDescription = $('#description'), jqEditor = $('#code_editor'), jqConsole = $('#console'), jqHints = $('#info'), editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }), activityHandler = makeActivityHandler(editor, problem.id), terminal = makePrologTerminalHandler(jqConsole, editor, problem.id, activityHandler), hintDefs = problem.hint, hintCounter = 0, // for generating unique class-names hintCleaners = [], clearHints = function () { var i; for (i = hintCleaners.length - 1; i >= 0; i--) { hintCleaners[i](); } hintCleaners.length = 0; hintCounter = 0; }, addMark = function (start, end) { var posStart = editor.posFromIndex(start), posEnd = editor.posFromIndex(end), doc = editor.getDoc(), mark = doc.markText(posStart, posEnd, {className: 'editor-mark _emark_' + hintCounter}), result = {start: posStart, end: posEnd, mark: mark, className: '_emark_' + hintCounter}; hintCleaners.push(function () { mark.clear(); mark = null; doc = null; result.mark = null; }); hintCounter++; return result; }, processTemplate = function (template, args) { if (!args) return template; return template.replace(/\[%=(\w+)%\]/g, function(match, name) { return args[name]; }); }, hintHandlers = { 'static': function (type, template, serverHint) { codeq.log.debug('Processing static hint'); var message = processTemplate(template, serverHint.args); jqHints.append('