From 5913dfdc2e49db142d4f26158e737e7180e6330b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Tainon?= Date: Wed, 30 Oct 2024 12:35:45 +0100 Subject: [PATCH] Make cleaner code for miniPlatformValidate --- bundles/bebras-interface.js | 3138 +++++++++--------- integrationAPI.01/official/miniPlatform.js | 51 +- integrationAPI.01/official/miniPlatform_M.js | 42 +- 3 files changed, 1606 insertions(+), 1625 deletions(-) diff --git a/bundles/bebras-interface.js b/bundles/bebras-interface.js index 731280493..509e752de 100644 --- a/bundles/bebras-interface.js +++ b/bundles/bebras-interface.js @@ -1,267 +1,267 @@ -(function () { - -'use strict'; - -function getUrlParameterByName(name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.href); - return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -function isCrossDomain() { - function isInIframe() { - try { - return window.self !== window.top; - } catch (e) { - return false; - } - } - function isSameDomain() { - var res = false; - function doNothing(document){} - try{ - res = !! parent.document; - } catch(e){ - res = false; - } - return res; - } - return isInIframe() && !isSameDomain(); -} - -var platform; - -if (!isCrossDomain()) { - - /* Implementation of a platform proxy for the task iframe. This object is always - * available, but is effective only when setPlatform is called with a true - * platform object. - */ - - platform = { - registered_objects: [], - parent_platform: null, - initFailed: false, - setPlatform: function(platformArg) { - platform.parent_platform = platformArg; - }, - trigger: function(event, content) { - for (var i = 0; i < platform.registered_objects.length; i++) { - var object = platform.registered_objects[i]; - if (typeof (object[event]) != "undefined") { - object[event].apply(object, content); - } - } - }, - subscribe: function(object) { - this.registered_objects.push(object); - }, - unsubscribe: function(object) { - var index = this.registered_objects.indexOf(object); - if (index != -1) { - this.registered_objects.splice(index, 1); - } - }, - validate: function(mode, success, error) { - // TODO: this case is a bit blur... - var res = platform.parent_platform.validate(mode, success, error); - this.trigger('validate', [mode]); - return res; - }, - showView: function(views, success, error) { - return platform.parent_platform.showView(views, success, error); - }, - askHint: function(platformToken, success, error) { - return platform.parent_platform.askHint(platformToken, success, error); - }, - updateHeight: function(height, success, error) { - return platform.parent_platform.updateDisplay({height: height}, success, error); - }, - updateDisplay: function(data, success, error) { - return platform.parent_platform.updateDisplay(data, success, error); - }, - getTaskParams: function(key, defaultValue, success, error) { - return platform.parent_platform.getTaskParams(key, defaultValue, success, error); - }, - openUrl: function(url, success, error) { - return platform.parent_platform.openUrl(url, success, error); - }, - initCallback: function(callback) { - this.initCallbackFun = callback; - if (platform.initDone) { - callback(); - } - }, - initWithTask: function(task) { - platform.task = task; - window.task = task; - platform.initDone = true; - if (platform.initCallbackFun) { - platform.initCallbackFun(); - } - } - }; - -} else { - - // cross-domain version, depends on jschannel - platform = {}; - var callAndTrigger = function(fun, triggerName, error, args) { - return function() { - try { - platform.trigger(triggerName, args); - fun(arguments); - } catch(e) { - error(e.toString()+'\n'+e.stack); - } - }; - }; - platform.ready = false; - platform.initFailed = false; - platform.initWithTask = function(task) { - if (typeof Channel === 'undefined') { - platform.initFailed = true; - console.error('cannot init task if jschannel is not present'); - return; - } - - var previousHeights = []; - var getHeightFiltered = function(success, error) { - // If the new height has already been returned just before the current - // height, we're in a loop between two heights, possibly because of a - // scrollbar. - // In that case we want to keep the largest of the two heights. - if(!task.getHeight) { error('task.getHeight not defined yet'); } - task.getHeight(function(h) { - if((previousHeights.length == 2) && - (previousHeights[0] == h) && - (previousHeights[1] >= h)) { - success(previousHeights[1]); - return; - } - previousHeights.push(h); - previousHeights = previousHeights.slice(-2); - success(h); - }, error); - } - - var gradeAnswer = function(params, success, error) { - var newSuccess = function(score, message, scoreToken) { - success([score, message, scoreToken]); - }; - if (typeof task.gradeAnswer === 'function') { - task.gradeAnswer(params[0], params[1], newSuccess, error); - } else { - window.grader.gradeTask(params[0], params[1], newSuccess, error); - } - }; - var channelId = getUrlParameterByName('channelId'); - var chan = Channel.build({window: window.parent, origin: "*", scope: channelId, onReady: function() {platform.ready = true;}}); - platform.chan = chan; - platform.task = task; - platform.channelId = channelId; - chan.bind('task.load', function(trans, views) {task.load(views, callAndTrigger(trans.complete, 'load', trans.error, [views]), trans.error);trans.delayReturn(true);}); - chan.bind('task.unload', function(trans) {task.unload(callAndTrigger(trans.complete, 'unload', trans.error, null), trans.error);trans.delayReturn(true);}); - chan.bind('task.getHeight', function(trans) {getHeightFiltered(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.getMetaData', function(trans) {task.getMetaData(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.getViews', function(trans) {task.getViews(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.showViews', function(trans, views) {task.showViews(views, callAndTrigger(trans.complete, 'showViews', trans.error, [views]), trans.error);trans.delayReturn(true);}); - chan.bind('task.updateToken', function(trans, token) {task.updateToken(token, trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.reloadAnswer', function(trans, answer) {task.reloadAnswer(answer, callAndTrigger(trans.complete, 'reloadAnswer', trans.error, [answer]), trans.error);trans.delayReturn(true);}); - chan.bind('task.getAnswer', function(trans) {task.getAnswer(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.getState', function(trans) {task.getState(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.getResources', function(trans) {task.getResources(trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.reloadState', function(trans, state) {task.reloadState(state, callAndTrigger(trans.complete, 'reloadState', trans.error, [state]), trans.error);trans.delayReturn(true);}); - chan.bind('grader.gradeTask', function(trans, params) {gradeAnswer(params, trans.complete, trans.error);trans.delayReturn(true);}); - chan.bind('task.gradeAnswer', function(trans, params) {gradeAnswer(params, trans.complete, trans.error);trans.delayReturn(true);}); - }; - - platform.registered_objects = []; - platform.trigger = function(event, content) { - for (var i = 0; i < platform.registered_objects.length; i++) { - var object = platform.registered_objects[i]; - if (typeof object[event] !== "undefined") { - object[event].apply(object, content); - } - } - }; - platform.subscribe = function(object) { - platform.registered_objects.push(object); - }; - platform.unsubscribe = function(object) { - var index = platform.registered_objects.indexOf(object); - if (index != -1) { - platform.registered_objects.splice(index, 1); - } - }; - platform.stop = function() { - platform.chan.destroy(); - }; - platform.validate = function (sMode, success, error) { - if (!success) success = function(){}; // not mandatory, as most code doesn't use it - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.validate", - params: sMode, - error: error, - success: callAndTrigger(success, 'validate', error, [sMode]) - }); - }; - platform.getTaskParams = function(key, defaultValue, success, error) { - if (!success) success = function(){}; - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.getTaskParams", - params: [key, defaultValue], - error: error, - success: success - }); - }; - platform.showView = function(views, success, error) { - if (!success) success = function(){}; - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.showView", - params: views, - error: error, - success: success - }); - }; - platform.askHint = function(platformToken, success, error) { - if (!success) success = function(){}; - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.askHint", - params: platformToken, - error: error, - success: success - }); - }; - platform.updateHeight = function(height, success, error) { - // Legacy - platform.updateDisplay({height: height}, success, error); - }; - platform.updateDisplay = function(data, success, error) { - if (!success) success = function(){}; - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.updateDisplay", - params: data, - error: error, - success: success - }); - }; - platform.openUrl = function(url, success, error) { - if (!success) success = function(){}; - if (!error) error = function() {console.error(arguments);}; - platform.chan.call({method: "platform.openUrl", - params: url, - error: error, - success: success - }); - }; -} - -window.platform = platform; - -}()); - +(function () { + +'use strict'; + +function getUrlParameterByName(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.href); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +function isCrossDomain() { + function isInIframe() { + try { + return window.self !== window.top; + } catch (e) { + return false; + } + } + function isSameDomain() { + var res = false; + function doNothing(document){} + try{ + res = !! parent.document; + } catch(e){ + res = false; + } + return res; + } + return isInIframe() && !isSameDomain(); +} + +var platform; + +if (!isCrossDomain()) { + + /* Implementation of a platform proxy for the task iframe. This object is always + * available, but is effective only when setPlatform is called with a true + * platform object. + */ + + platform = { + registered_objects: [], + parent_platform: null, + initFailed: false, + setPlatform: function(platformArg) { + platform.parent_platform = platformArg; + }, + trigger: function(event, content) { + for (var i = 0; i < platform.registered_objects.length; i++) { + var object = platform.registered_objects[i]; + if (typeof (object[event]) != "undefined") { + object[event].apply(object, content); + } + } + }, + subscribe: function(object) { + this.registered_objects.push(object); + }, + unsubscribe: function(object) { + var index = this.registered_objects.indexOf(object); + if (index != -1) { + this.registered_objects.splice(index, 1); + } + }, + validate: function(mode, success, error) { + // TODO: this case is a bit blur... + var res = platform.parent_platform.validate(mode, success, error); + this.trigger('validate', [mode]); + return res; + }, + showView: function(views, success, error) { + return platform.parent_platform.showView(views, success, error); + }, + askHint: function(platformToken, success, error) { + return platform.parent_platform.askHint(platformToken, success, error); + }, + updateHeight: function(height, success, error) { + return platform.parent_platform.updateDisplay({height: height}, success, error); + }, + updateDisplay: function(data, success, error) { + return platform.parent_platform.updateDisplay(data, success, error); + }, + getTaskParams: function(key, defaultValue, success, error) { + return platform.parent_platform.getTaskParams(key, defaultValue, success, error); + }, + openUrl: function(url, success, error) { + return platform.parent_platform.openUrl(url, success, error); + }, + initCallback: function(callback) { + this.initCallbackFun = callback; + if (platform.initDone) { + callback(); + } + }, + initWithTask: function(task) { + platform.task = task; + window.task = task; + platform.initDone = true; + if (platform.initCallbackFun) { + platform.initCallbackFun(); + } + } + }; + +} else { + + // cross-domain version, depends on jschannel + platform = {}; + var callAndTrigger = function(fun, triggerName, error, args) { + return function() { + try { + platform.trigger(triggerName, args); + fun(arguments); + } catch(e) { + error(e.toString()+'\n'+e.stack); + } + }; + }; + platform.ready = false; + platform.initFailed = false; + platform.initWithTask = function(task) { + if (typeof Channel === 'undefined') { + platform.initFailed = true; + console.error('cannot init task if jschannel is not present'); + return; + } + + var previousHeights = []; + var getHeightFiltered = function(success, error) { + // If the new height has already been returned just before the current + // height, we're in a loop between two heights, possibly because of a + // scrollbar. + // In that case we want to keep the largest of the two heights. + if(!task.getHeight) { error('task.getHeight not defined yet'); } + task.getHeight(function(h) { + if((previousHeights.length == 2) && + (previousHeights[0] == h) && + (previousHeights[1] >= h)) { + success(previousHeights[1]); + return; + } + previousHeights.push(h); + previousHeights = previousHeights.slice(-2); + success(h); + }, error); + } + + var gradeAnswer = function(params, success, error) { + var newSuccess = function(score, message, scoreToken) { + success([score, message, scoreToken]); + }; + if (typeof task.gradeAnswer === 'function') { + task.gradeAnswer(params[0], params[1], newSuccess, error); + } else { + window.grader.gradeTask(params[0], params[1], newSuccess, error); + } + }; + var channelId = getUrlParameterByName('channelId'); + var chan = Channel.build({window: window.parent, origin: "*", scope: channelId, onReady: function() {platform.ready = true;}}); + platform.chan = chan; + platform.task = task; + platform.channelId = channelId; + chan.bind('task.load', function(trans, views) {task.load(views, callAndTrigger(trans.complete, 'load', trans.error, [views]), trans.error);trans.delayReturn(true);}); + chan.bind('task.unload', function(trans) {task.unload(callAndTrigger(trans.complete, 'unload', trans.error, null), trans.error);trans.delayReturn(true);}); + chan.bind('task.getHeight', function(trans) {getHeightFiltered(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.getMetaData', function(trans) {task.getMetaData(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.getViews', function(trans) {task.getViews(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.showViews', function(trans, views) {task.showViews(views, callAndTrigger(trans.complete, 'showViews', trans.error, [views]), trans.error);trans.delayReturn(true);}); + chan.bind('task.updateToken', function(trans, token) {task.updateToken(token, trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.reloadAnswer', function(trans, answer) {task.reloadAnswer(answer, callAndTrigger(trans.complete, 'reloadAnswer', trans.error, [answer]), trans.error);trans.delayReturn(true);}); + chan.bind('task.getAnswer', function(trans) {task.getAnswer(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.getState', function(trans) {task.getState(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.getResources', function(trans) {task.getResources(trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.reloadState', function(trans, state) {task.reloadState(state, callAndTrigger(trans.complete, 'reloadState', trans.error, [state]), trans.error);trans.delayReturn(true);}); + chan.bind('grader.gradeTask', function(trans, params) {gradeAnswer(params, trans.complete, trans.error);trans.delayReturn(true);}); + chan.bind('task.gradeAnswer', function(trans, params) {gradeAnswer(params, trans.complete, trans.error);trans.delayReturn(true);}); + }; + + platform.registered_objects = []; + platform.trigger = function(event, content) { + for (var i = 0; i < platform.registered_objects.length; i++) { + var object = platform.registered_objects[i]; + if (typeof object[event] !== "undefined") { + object[event].apply(object, content); + } + } + }; + platform.subscribe = function(object) { + platform.registered_objects.push(object); + }; + platform.unsubscribe = function(object) { + var index = platform.registered_objects.indexOf(object); + if (index != -1) { + platform.registered_objects.splice(index, 1); + } + }; + platform.stop = function() { + platform.chan.destroy(); + }; + platform.validate = function (sMode, success, error) { + if (!success) success = function(){}; // not mandatory, as most code doesn't use it + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.validate", + params: sMode, + error: error, + success: callAndTrigger(success, 'validate', error, [sMode]) + }); + }; + platform.getTaskParams = function(key, defaultValue, success, error) { + if (!success) success = function(){}; + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.getTaskParams", + params: [key, defaultValue], + error: error, + success: success + }); + }; + platform.showView = function(views, success, error) { + if (!success) success = function(){}; + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.showView", + params: views, + error: error, + success: success + }); + }; + platform.askHint = function(platformToken, success, error) { + if (!success) success = function(){}; + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.askHint", + params: platformToken, + error: error, + success: success + }); + }; + platform.updateHeight = function(height, success, error) { + // Legacy + platform.updateDisplay({height: height}, success, error); + }; + platform.updateDisplay = function(data, success, error) { + if (!success) success = function(){}; + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.updateDisplay", + params: data, + error: error, + success: success + }); + }; + platform.openUrl = function(url, success, error) { + if (!success) success = function(){}; + if (!error) error = function() {console.error(arguments);}; + platform.chan.call({method: "platform.openUrl", + params: url, + error: error, + success: success + }); + }; +} + +window.platform = platform; + +}()); + (function() { 'use strict'; @@ -2120,1313 +2120,1303 @@ function drawStars(id, nbStars, starWidth, rate, mode) { window.platform.subscribe(displayHelper); })(); - -var Beav = new Object(); - - -/**********************************************************************************/ -/* Object */ - -Beav.Object = new Object(); - -Beav.Object.eq = function eq(x, y) { - // assumes arguments to be of same type - var tx = typeof(x); - var ty = typeof(y); - if (tx != ty) { - throw "Beav.Object.eq incompatible types"; - } - if (tx == "boolean" || tx == "number" || tx == "string" || tx == "undefined") { - return x == y; - } - if ($.isArray(x)) { - if (! $.isArray(y)) - throw "Beav.Object.eq incompatible types"; - if (x.length != y.length) - return false; - for (var i = 0; i < x.length; i++) - if (! eq(x[i], y[i])) - return false; - return true; - } - if (tx == "object") { - var kx = []; - for (var key in x) { - kx.push(key); - } - var ky = []; - for (var key in y) { - ky.push(key); - } - var sort_keys = function(n1,n2) { return (n1 < n2) ? -1 : ((n1 > n2) ? 1 : 0); }; - kx.sort(sort_keys); - ky.sort(sort_keys); - if (kx.length != ky.length) - return false; - for (var i = 0; i < kx.length; i++) { - var ex = kx[i]; - var ey = ky[i]; - if (ex != ey) - return false; - if (! eq(x[ex], y[ex])) - return false; - } - return true; - } - throw "Beav.Object.eq unsupported types"; -}; - - -/**********************************************************************************/ -/* Array */ - -Beav.Array = new Object(); - -Beav.Array.make = function(nb, initValue) { - var t = []; - for (var i = 0; i < nb; i++) - t[i] = initValue; - return t; -}; - -Beav.Array.init = function(nb, initFct) { - var t = []; - for (var i = 0; i < nb; i++) - t.push(initFct(i)); - return t; -}; - -Beav.Array.indexOf = function(t, v, eq) { - if (eq === undefined) - eq = Beav.Object.eq; - for (var i = 0; i < t.length; i++) - if (eq(t[i], v)) - return i; - return -1; -}; - -Beav.Array.has = function(t, v, eq) { - return Beav.Array.indexOf(t, v, eq) != -1; -}; - -Beav.Array.filterCount = function(t, filterFct) { - var count = 0; - for (var i = 0; i < t.length; i++) - if (filterFct(t[i], i)) - count++; - return count; -}; - -Beav.Array.stableSort = function(t, compFct) { - var swap = function(a, b) { - var v = t[a]; - t[a] = t[b]; - t[b] = v; - }; - var insert = function (i, j, v) { - while(i+1 < j && compFct(t[i+1], v) < 0) { - swap(i, i+1); - i++; - } - t[i] = v; - }; - var merge = function(i, k, j) { - for ( ; i": ">", - '"': '"', - "'": ''', - "/": '/' }; - return String(stringToEncode).replace(/[&<>"'\/]/g, function (s) { - return entityMap[s]; - }); -}; - - -/**********************************************************************************/ -/* Raphael */ - -Beav.Raphael = new Object(); - -Beav.Raphael.line = function(paper, x1, y1, x2, y2) { - return paper.path([ "M", x1, y1, "L", x2, y2 ]); -}; - -Beav.Raphael.lineRelative = function(paper, x1, y1, dx, dy) { - return Beav.Raphael.line(paper, x1, y1, x1+dx, y1+dy); -}; - - -/**********************************************************************************/ -/* Random */ - -Beav.Random = new Object(); - -Beav.Random.bit = function(randomSeed, idBit) { - return (randomSeed & (1 << idBit)) ? 1 : 0; -}; - - -/**********************************************************************************/ -/* Task */ - -Beav.Task = new Object(); - -Beav.Task.scoreInterpolate = function(minScore, maxScore, minResult, maxResult, result) { - // requires minResult <= result <= maxResult and minScore <= maxScore - return Math.round(minScore + (maxScore - minScore) * (result - minResult) / (maxResult - minResult)); -}; - - -/**********************************************************************************/ -/* Geometry */ - -Beav.Geometry = new Object(); - -Beav.Geometry.distance = function(x1,y1,x2,y2) { - return Math.sqrt(Math.pow(x2 - x1,2) + Math.pow(y2 - y1,2)); -}; - -/* - This is used to handle drag on devices that have both a touch screen and a mouse. - Can be tested on chrome by loading a task in desktop mode, then switching to tablet mode. - To call instead of element.drag(onMove, onStart, onEnd); -*/ -Beav.dragWithTouch = function(element, onMove, onStart, onEnd) { - var touchingX = 0; - var touchingY = 0; - var disabled = false; - - function onTouchStart(evt) { - if (disabled) { - return; - } - var touches = evt.changedTouches; - touchingX = touches[0].pageX; - touchingY = touches[0].pageY; - onStart(touches[0].pageX, touches[0].pageY, evt); - } - - function onTouchEnd(evt) { - if (disabled) { - return; - } - onEnd(null); - } - - function onTouchMove(evt) { - if (disabled) { - return; - } - var touches = evt.changedTouches; - var dx = touches[0].pageX - touchingX; - var dy = touches[0].pageY - touchingY; - onMove(dx, dy, touches[0].pageX, touches[0].pageY, evt); - } - - function callOnStart(x,y,event) { - disabled = true; - onStart(x,y,event); - } - - function callOnMove(dx,dy,x,y,event) { - disabled = true; - onMove(dx,dy,x,y,event); - } - - function callOnEnd(event) { - disabled = false; - onEnd(event); - } - - // element.undrag(); - element.drag(callOnMove,callOnStart,callOnEnd); - if (element.touchstart) { - element.touchstart(onTouchStart); - element.touchend(onTouchEnd); - element.touchcancel(onTouchEnd); - element.touchmove(onTouchMove); - } -} -(function() { - -'use strict'; - -// requires jQuery, and a task object in the global scope. - -// this should be called before the task loads, because the task can modify -// its html at load, and we want to return unmodified html in getTaskResources. -var res = {}; - -var taskResourcesLoaded = false; - -window.implementGetResources = function(task) { - task.getResources = function(callback) - { - if (taskResourcesLoaded) { - callback(res); - return; - } - res.task = ('task' in res) ? res.task : [{ type: 'html', content: $('#task').html() }]; - res.solution = ('solution' in res) ? res.solution : [{ type: 'html', content: $('#solution').html() }]; - res.grader = []; - res.task_modules = []; - res.solution_modules = []; - res.grader_modules = []; - if (!res.hints) { - res.hints = []; - $('.hint').each(function(index) { - res.hints[res.hints.length] = [{type: 'html', content: $(this).html() }]; - $(this).attr('hint-Num', res.hints.length-1); - }); - } - res.proxy = []; - res.proxy_modules = []; - res.display = []; - res.display_modules = []; - res.sat = []; - res.sat_modules = []; - res.files = []; - if (!res.title) { - res.title = $('title').text(); - } - - // Resources - var curDest = 'task'; - var curType = 'javascript'; - $('script, style, link').each(function() { - if ($(this).hasClass('remove')) { - return; - } - if ($(this).hasClass('solution') && $(this).hasClass('module')) { - curDest = res.solution_modules; - } - else if ($(this).hasClass('solution')) { - curDest = res.solution; - } - else if ($(this).hasClass('grader') && $(this).hasClass('module')) { - curDest = res.grader_modules; - } - else if ($(this).hasClass('grader')) { - curDest = res.grader; - } - else if ($(this).hasClass('hint')) { - res.hints.push([{ type: 'html', content: $(this).html() }]); - return; - } - else if ($(this).hasClass('proxy') && $(this).hasClass('module')) { - curDest = res.proxy_modules; - } - else if ($(this).hasClass('proxy')) { - curDest = res.proxy; - } - else if ($(this).hasClass('stdButtonsAndMessages') && $(this).hasClass('module')) { - curDest = res.display_modules; - } - else if ($(this).hasClass('stdButtonsAndMessages')) { - curDest = res.display; - } - else if ($(this).hasClass('stdAnswerTypes') && $(this).hasClass('module')) { - curDest = res.sat_modules; - } - else if ($(this).hasClass('stdAnswerTypes')) { - curDest = res.sat; - } - else if ($(this).hasClass('module')) { - curDest = res.task_modules; - } - else { - curDest = res.task; - } - - if ($(this).is('script')) { - curType = 'javascript'; - } - else if ($(this).is('style') || $(this).is('link')) { - curType = 'css'; - } - - if ($(this).attr('src')) { - curDest.push({ type: curType, url: $(this).attr('src'), id: $(this).attr('id') }); - } - else if ($(this).attr('href')) { - curDest.push({ type: curType, url: $(this).attr('href'), id: $(this).attr('id') }); - } - else { - curDest.push({ type: curType, id: $(this).attr('id'), content: $(this).html() }); - } - }); - - // Images - var images = []; - var image = ''; - $('#task img').each(function() { - var src = $(this).attr('src'); - if (src) { - image = src.toString(); - if ($.inArray(image, images) === -1) { - res.task.push({ type: 'image', url: image }); - images.push(image); - } - } - }); - fillImages($('#task').html(), images, res.task); - $('script').each(function() { - if ($(this).hasClass('remove') || $(this).attr('src') || $(this).attr('href')) { - return; - } - fillImages($(this).html(), images, res.task); - }); - $('#solution img').each(function() { - image = $(this).attr('src').toString(); - if ($.inArray(image, images) === -1) { - res.solution.push({ type: 'image', url: image }); - images.push(image); - } - }); - fillImages($('#solution').html(), images, res.solution); - $('.hint').each(function() { - var hintnum = $(this).attr('hint-num'); - $('[hint-num='+hintnum+'] img').each(function() { - image = $(this).attr('src').toString(); - if ($.inArray(image, images) === -1) { - res.hints[hintnum].push({ type: 'image', url: image }); - images.push(image); - } - }); - fillImages($(this).html(), images, res.hints[hintnum]); - }); - - // Links - $('iframe').each(function () { - var curUrl = $(this).attr('src'); - if(curUrl.indexOf('://') == -1 && curUrl.charAt(0) != '/') { - res.files.push({ type: this.tagName, url: $(this).attr('src') }); - } - }); - - // Other resources - $('source, track').each(function() { - res.files.push({ type: this.tagName, url: $(this).attr('src') }); - }); - $('fioi-video-player').each(function() { - var fileAttributes = ["data-source", "data-image", "data-subtitles"]; - for(var i=0; i\ - \ - \ - \ - \ -
Concours castorLe concours Castor
\ - ' - }, - laptop: { - 'header' : '\ -
\ - \ - \ - \ - \ -
Concours AlkindiLe concours Alkindi
\ -
' - }, - none: { - 'header' : '' - } - }; - - function inIframe() { - try { - return window.self !== window.top; - } catch (e) { - return false; - } - } - - - - if(typeof window.jwt == 'undefined') { - window.jwt = { - isDummy: true, - sign: function() { return null; }, - decode: function(token) { return token; } - }; - } - - function TaskToken(data, key) { - - this.data = data - this.data.sHintsRequested = "[]"; - this.key = key - - var query = document.location.search.replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0]; - this.queryToken = query.sToken; - - this.addHintRequest = function(hint_params, callback) { - try { - hint_params = jwt.decode(hint_params).askedHint; - } catch(e) {} - var hintsReq = JSON.parse(this.data.sHintsRequested); - var exists = hintsReq.find(function(h) { - return h == hint_params; - }); - if(!exists) { - hintsReq.push(hint_params); - this.data.sHintsRequested = JSON.stringify(hintsReq); - } - return this.get(callback); - } - - this.update = function(newData, callback) { - for(var key in newData) { - this.data[key] = newData[key]; - } - } - - this.getToken = function(data, callback) { - var res = jwt.sign(data, this.key) - if(callback) { - // imitate async req - setTimeout(function() { - callback(res) - }, 0); - } - return res; - } - - this.get = function(callback) { - if(window.jwt.isDummy && this.queryToken) { - var token = this.queryToken; - if(callback) { - // imitate async req - setTimeout(function() { - callback(token) - }, 0); - } - return token; - } - return this.getToken(this.data, callback); - } - - this.getAnswerToken = function(answer, callback) { - var answerData = {}; - for(var key in this.data) { - answerData[key] = this.data[key]; - } - answerData.sAnswer = answer; - return this.getToken(answerData, callback); - } - } - - - function AnswerToken(key) { - this.key = key - this.get = function(answer, callback) { - var res = jwt.sign(answer, this.key) - if(callback) { - // imitate async req - setTimeout(function() { - callback(res) - }, 0) - } - return res; - } - } - - - -var taskMetaData; - -// important for tracker.js -var compiledTask = true; - -window.miniPlatformShowSolution = function() { - $("#showSolutionButton").hide(); - task.getAnswer(function(answer) { - task.showViews({"task": true, "solution": true}, function() { - // For tasks with no feedback / older tasks - // miniPlatformPreviewGrade(answer); - platform.trigger('showViews', [{"task": true, "solution": true}]); - }); - }); -} - -function miniPlatformPreviewGrade(answer) { - var minScore = -3; - if (taskMetaData.fullFeedback) { - minScore = 0; - } - var maxScore = 40; - var score; - var showGrade = function(score) { - if ($("#previewScorePopup").length === 0) { - $("
" + - "
" + - "


").insertBefore("#solution"); - } - $("#previewScorePopup").show(); - $("#previewScoreMessage").html("" + getLanguageString('showSolution') + " " + score + "/" + maxScore + "
" + getLanguageString('showSolution')); - }; - // acceptedAnswers is not documented, but necessary for old Bebras tasks - if (taskMetaData.acceptedAnswers && taskMetaData.acceptedAnswers[0]) { - if ($.inArray("" + answer, taskMetaData.acceptedAnswers) > -1) { - score = maxScore; - } - else { - score = minScore; - } - showGrade(score); - } else { - score = grader.gradeTask(answer, null, showGrade); - } -} - -var alreadyStayed = false; - -var miniPlatformValidate = function(task) { return function(mode, success, error) { - //$.post('updateTestToken.php', {action: 'showSolution'}, function(){}, 'json'); - if (mode == 'nextImmediate' || mode == 'log') { - return; - } - if (mode == 'stay') { - if (alreadyStayed) { - platform.trigger('validate', [mode]); - if (success) { - success(); - } - return; - } else { - alreadyStayed = true; - } - } - if (mode == 'cancel') { - alreadyStayed = false; - } - if(platform.registered_objects && platform.registered_objects.length > 0) { - platform.trigger('validate', [mode]); - } else { - // Try to validate - task.getAnswer(function(answer) { - task.gradeAnswer(answer, task_token.getAnswerToken(answer), function(score, message) { - if(success) { success(); } - }) - }); - } - if (success) { - success(); - } -}}; - -function getUrlParameter(sParam) -{ - var sPageURL = window.location.search.substring(1); - var sURLVariables = sPageURL.split('&'); - for (var i = 0; i < sURLVariables.length; i++) - { - var sParameterName = sURLVariables[i].split('='); - if (sParameterName[0] == sParam) - { - return decodeURIComponent(sParameterName[1]); - } - } -} - -function getHashParameter(sParam) -{ - var sPageURL = window.location.hash.substring(1); - var sURLVariables = sPageURL.split('&'); - for (var i = 0; i < sURLVariables.length; i++) - { - var sParameterName = sURLVariables[i].split('='); - if (sParameterName[0] == sParam) - { - return decodeURIComponent(sParameterName[1]); - } - } -} - -var chooseView = (function () { - // Manages the buttons to choose the view - return { - doubleEnabled: false, - isDouble: false, - lastShownViews: {}, - - init: function(views) { - if (! $("#choose-view").length) - $(document.body).append('
'); - $("#choose-view").html(""); - // Display buttons to select task view or solution view - /* - for(var viewName in views) { - if (!views[viewName].requires) { - var btn = $('') - $("#choose-view").append(btn); - btn.click(this.selectFactory(viewName)); - } - } - */ - $("#grade").remove(); - var btnGradeAnswer = $('
'); - // display grader button only if dev mode by adding URL hash 'dev' - if (getHashParameter('dev')) { - $(document.body).append(btnGradeAnswer); - } - btnGradeAnswer.click(function() { - task.getAnswer(function(answer) { - answer_token.get(answer, function(answer_token) { - task.gradeAnswer(answer, answer_token, function(score, message, scoreToken) { - alert("Score : " + score + ", message : " + message); - }); - }) - }, function() { - alert("error"); - }); - }) - }, - - reinit: function(views) { - this.init(views); - var newShownViews = {}; - for(var viewName in this.lastShownViews) { - if(!this.lastShownViews[viewName]) { continue; } - if(views[viewName] && !views[viewName].requires) { - newShownViews[viewName] = true; - } - } - for(var viewName in views) { - if(views[viewName].includes) { - for(var i=0; i'); - var platformLoad = function(task) { - window.task_token.update({id: taskMetaData.id}); - window.answer_token = new AnswerToken(demo_key) - - platform.validate = miniPlatformValidate(task); - platform.updateHeight = function(height,success,error) {if (success) {success();}}; - platform.updateDisplay = function(data,success,error) { - if(data.views) { - chooseView.reinit(data.views); - } - if (success) {success();} - }; - var taskOptions = {}; - try { - var strOptions = getUrlParameter("options"); - if (strOptions !== undefined) { - taskOptions = $.parseJSON(strOptions); - } - } catch(exception) { - alert("Error: invalid options"); - } - var minScore = -3; - if (taskMetaData.fullFeedback) { - minScore = 0; - } - platform.getTaskParams = function(key, defaultValue, success, error) { - var res = {'minScore': minScore, 'maxScore': 40, 'noScore': 0, 'readOnly': false, 'randomSeed': "0", 'options': taskOptions}; - if (key) { - if (key !== 'options' && key in res) { - res = res[key]; - } else if (res.options && key in res.options) { - res = res.options[key]; - } else { - res = (typeof defaultValue !== 'undefined') ? defaultValue : null; - } - } - if (success) { - success(res); - } else { - return res; - } - }; - platform.askHint = function(hint_params, success, error) { - /* - $.post('updateTestToken.php', JSON.stringify({action: 'askHint'}), function(postRes){ - if (success) {success();} - }, 'json'); - */ - task_token.addHintRequest(hint_params, function(token) { - task.updateToken(token, function() {}) - success(token) - }) - }; - - - var loadedViews = {'task': true, 'solution': true, 'hints': true, 'editor': true, 'grader': true, 'metadata': true, 'submission': true}; - var shownViews = {'task': true}; - // TODO: modifs ARTHUR à relire - if (taskOptions.showSolutionOnLoad) { - shownViews.solution = true; - } - if (!taskOptions.hideTitle) { - $("#task h1").show(); - } - - if (taskMetaData.fullFeedback) { - loadedViews.grader = true; - } - - task.load( - loadedViews, - function() { - platform.trigger('load', [loadedViews]); - task.getViews(function(views) { - chooseView.init(views); - }); - task.showViews(shownViews, function() { - chooseView.update(shownViews); - platform.trigger('showViews', [{"task": true}]); - }); - if ($("#solution").length) { - $("#task").append("
"); - } - - // add branded header to platformless task depending on avatarType - // defaults to beaver platform branding - if(window.displayHelper) { - if (miniPlatformWrapping[displayHelper.avatarType].header) { - $('body').prepend(miniPlatformWrapping[displayHelper.avatarType].header); - } else { - $('body').prepend(miniPlatformWrapping[beaver].header); - } - } - }, - function(error) { - console.error(error) - } - ); - - - task_token.get(function(token) { - task.updateToken(token, function() {}) - }) - - - /* For the 'resize' event listener below, we use a cross-browser - * compatible version for "addEventListener" (modern) and "attachEvent" (old). - * Source: https://stackoverflow.com/questions/6927637/addeventlistener-in-internet-explorer - */ - function addEvent(evnt, elem, func) { - if (elem.addEventListener) // W3C DOM - elem.addEventListener(evnt,func,false); - else if (elem.attachEvent) { // IE DOM - elem.attachEvent("on"+evnt, func); - } - else { // No much to do - elem[evnt] = func; - } - } - - addEvent('resize', window, function() { - task.getViews(function(views) { - chooseView.reinit(views); - }); - }); - }; - var getMetaDataAndLoad = function(task) { - task.getMetaData(function(metaData) { - taskMetaData = metaData; - platformLoad(task); - }); - }; - if (window.platform.task || platform.initFailed) { - // case everything went fine with task loading, or task loading failed - // (due to missing jschannel and file:// protocol... - getMetaDataAndLoad(window.task ? window.task : window.platform.task); - } else { - // task is not loaded yet - var oldInit = platform.initWithTask; - platform.initWithTask = function(task) { - oldInit(task); - getMetaDataAndLoad(task); - }; - } - } -}); - -})(); + +var Beav = new Object(); + + +/**********************************************************************************/ +/* Object */ + +Beav.Object = new Object(); + +Beav.Object.eq = function eq(x, y) { + // assumes arguments to be of same type + var tx = typeof(x); + var ty = typeof(y); + if (tx != ty) { + throw "Beav.Object.eq incompatible types"; + } + if (tx == "boolean" || tx == "number" || tx == "string" || tx == "undefined") { + return x == y; + } + if ($.isArray(x)) { + if (! $.isArray(y)) + throw "Beav.Object.eq incompatible types"; + if (x.length != y.length) + return false; + for (var i = 0; i < x.length; i++) + if (! eq(x[i], y[i])) + return false; + return true; + } + if (tx == "object") { + var kx = []; + for (var key in x) { + kx.push(key); + } + var ky = []; + for (var key in y) { + ky.push(key); + } + var sort_keys = function(n1,n2) { return (n1 < n2) ? -1 : ((n1 > n2) ? 1 : 0); }; + kx.sort(sort_keys); + ky.sort(sort_keys); + if (kx.length != ky.length) + return false; + for (var i = 0; i < kx.length; i++) { + var ex = kx[i]; + var ey = ky[i]; + if (ex != ey) + return false; + if (! eq(x[ex], y[ex])) + return false; + } + return true; + } + throw "Beav.Object.eq unsupported types"; +}; + + +/**********************************************************************************/ +/* Array */ + +Beav.Array = new Object(); + +Beav.Array.make = function(nb, initValue) { + var t = []; + for (var i = 0; i < nb; i++) + t[i] = initValue; + return t; +}; + +Beav.Array.init = function(nb, initFct) { + var t = []; + for (var i = 0; i < nb; i++) + t.push(initFct(i)); + return t; +}; + +Beav.Array.indexOf = function(t, v, eq) { + if (eq === undefined) + eq = Beav.Object.eq; + for (var i = 0; i < t.length; i++) + if (eq(t[i], v)) + return i; + return -1; +}; + +Beav.Array.has = function(t, v, eq) { + return Beav.Array.indexOf(t, v, eq) != -1; +}; + +Beav.Array.filterCount = function(t, filterFct) { + var count = 0; + for (var i = 0; i < t.length; i++) + if (filterFct(t[i], i)) + count++; + return count; +}; + +Beav.Array.stableSort = function(t, compFct) { + var swap = function(a, b) { + var v = t[a]; + t[a] = t[b]; + t[b] = v; + }; + var insert = function (i, j, v) { + while(i+1 < j && compFct(t[i+1], v) < 0) { + swap(i, i+1); + i++; + } + t[i] = v; + }; + var merge = function(i, k, j) { + for ( ; i": ">", + '"': '"', + "'": ''', + "/": '/' }; + return String(stringToEncode).replace(/[&<>"'\/]/g, function (s) { + return entityMap[s]; + }); +}; + + +/**********************************************************************************/ +/* Raphael */ + +Beav.Raphael = new Object(); + +Beav.Raphael.line = function(paper, x1, y1, x2, y2) { + return paper.path([ "M", x1, y1, "L", x2, y2 ]); +}; + +Beav.Raphael.lineRelative = function(paper, x1, y1, dx, dy) { + return Beav.Raphael.line(paper, x1, y1, x1+dx, y1+dy); +}; + + +/**********************************************************************************/ +/* Random */ + +Beav.Random = new Object(); + +Beav.Random.bit = function(randomSeed, idBit) { + return (randomSeed & (1 << idBit)) ? 1 : 0; +}; + + +/**********************************************************************************/ +/* Task */ + +Beav.Task = new Object(); + +Beav.Task.scoreInterpolate = function(minScore, maxScore, minResult, maxResult, result) { + // requires minResult <= result <= maxResult and minScore <= maxScore + return Math.round(minScore + (maxScore - minScore) * (result - minResult) / (maxResult - minResult)); +}; + + +/**********************************************************************************/ +/* Geometry */ + +Beav.Geometry = new Object(); + +Beav.Geometry.distance = function(x1,y1,x2,y2) { + return Math.sqrt(Math.pow(x2 - x1,2) + Math.pow(y2 - y1,2)); +}; + +/* + This is used to handle drag on devices that have both a touch screen and a mouse. + Can be tested on chrome by loading a task in desktop mode, then switching to tablet mode. + To call instead of element.drag(onMove, onStart, onEnd); +*/ +Beav.dragWithTouch = function(element, onMove, onStart, onEnd) { + var touchingX = 0; + var touchingY = 0; + var disabled = false; + + function onTouchStart(evt) { + if (disabled) { + return; + } + var touches = evt.changedTouches; + touchingX = touches[0].pageX; + touchingY = touches[0].pageY; + onStart(touches[0].pageX, touches[0].pageY, evt); + } + + function onTouchEnd(evt) { + if (disabled) { + return; + } + onEnd(null); + } + + function onTouchMove(evt) { + if (disabled) { + return; + } + var touches = evt.changedTouches; + var dx = touches[0].pageX - touchingX; + var dy = touches[0].pageY - touchingY; + onMove(dx, dy, touches[0].pageX, touches[0].pageY, evt); + } + + function callOnStart(x,y,event) { + disabled = true; + onStart(x,y,event); + } + + function callOnMove(dx,dy,x,y,event) { + disabled = true; + onMove(dx,dy,x,y,event); + } + + function callOnEnd(event) { + disabled = false; + onEnd(event); + } + + // element.undrag(); + element.drag(callOnMove,callOnStart,callOnEnd); + if (element.touchstart) { + element.touchstart(onTouchStart); + element.touchend(onTouchEnd); + element.touchcancel(onTouchEnd); + element.touchmove(onTouchMove); + } +} +(function() { + +'use strict'; + +// requires jQuery, and a task object in the global scope. + +// this should be called before the task loads, because the task can modify +// its html at load, and we want to return unmodified html in getTaskResources. +var res = {}; + +var taskResourcesLoaded = false; + +window.implementGetResources = function(task) { + task.getResources = function(callback) + { + if (taskResourcesLoaded) { + callback(res); + return; + } + res.task = ('task' in res) ? res.task : [{ type: 'html', content: $('#task').html() }]; + res.solution = ('solution' in res) ? res.solution : [{ type: 'html', content: $('#solution').html() }]; + res.grader = []; + res.task_modules = []; + res.solution_modules = []; + res.grader_modules = []; + if (!res.hints) { + res.hints = []; + $('.hint').each(function(index) { + res.hints[res.hints.length] = [{type: 'html', content: $(this).html() }]; + $(this).attr('hint-Num', res.hints.length-1); + }); + } + res.proxy = []; + res.proxy_modules = []; + res.display = []; + res.display_modules = []; + res.sat = []; + res.sat_modules = []; + res.files = []; + if (!res.title) { + res.title = $('title').text(); + } + + // Resources + var curDest = 'task'; + var curType = 'javascript'; + $('script, style, link').each(function() { + if ($(this).hasClass('remove')) { + return; + } + if ($(this).hasClass('solution') && $(this).hasClass('module')) { + curDest = res.solution_modules; + } + else if ($(this).hasClass('solution')) { + curDest = res.solution; + } + else if ($(this).hasClass('grader') && $(this).hasClass('module')) { + curDest = res.grader_modules; + } + else if ($(this).hasClass('grader')) { + curDest = res.grader; + } + else if ($(this).hasClass('hint')) { + res.hints.push([{ type: 'html', content: $(this).html() }]); + return; + } + else if ($(this).hasClass('proxy') && $(this).hasClass('module')) { + curDest = res.proxy_modules; + } + else if ($(this).hasClass('proxy')) { + curDest = res.proxy; + } + else if ($(this).hasClass('stdButtonsAndMessages') && $(this).hasClass('module')) { + curDest = res.display_modules; + } + else if ($(this).hasClass('stdButtonsAndMessages')) { + curDest = res.display; + } + else if ($(this).hasClass('stdAnswerTypes') && $(this).hasClass('module')) { + curDest = res.sat_modules; + } + else if ($(this).hasClass('stdAnswerTypes')) { + curDest = res.sat; + } + else if ($(this).hasClass('module')) { + curDest = res.task_modules; + } + else { + curDest = res.task; + } + + if ($(this).is('script')) { + curType = 'javascript'; + } + else if ($(this).is('style') || $(this).is('link')) { + curType = 'css'; + } + + if ($(this).attr('src')) { + curDest.push({ type: curType, url: $(this).attr('src'), id: $(this).attr('id') }); + } + else if ($(this).attr('href')) { + curDest.push({ type: curType, url: $(this).attr('href'), id: $(this).attr('id') }); + } + else { + curDest.push({ type: curType, id: $(this).attr('id'), content: $(this).html() }); + } + }); + + // Images + var images = []; + var image = ''; + $('#task img').each(function() { + var src = $(this).attr('src'); + if (src) { + image = src.toString(); + if ($.inArray(image, images) === -1) { + res.task.push({ type: 'image', url: image }); + images.push(image); + } + } + }); + fillImages($('#task').html(), images, res.task); + $('script').each(function() { + if ($(this).hasClass('remove') || $(this).attr('src') || $(this).attr('href')) { + return; + } + fillImages($(this).html(), images, res.task); + }); + $('#solution img').each(function() { + image = $(this).attr('src').toString(); + if ($.inArray(image, images) === -1) { + res.solution.push({ type: 'image', url: image }); + images.push(image); + } + }); + fillImages($('#solution').html(), images, res.solution); + $('.hint').each(function() { + var hintnum = $(this).attr('hint-num'); + $('[hint-num='+hintnum+'] img').each(function() { + image = $(this).attr('src').toString(); + if ($.inArray(image, images) === -1) { + res.hints[hintnum].push({ type: 'image', url: image }); + images.push(image); + } + }); + fillImages($(this).html(), images, res.hints[hintnum]); + }); + + // Links + $('iframe').each(function () { + var curUrl = $(this).attr('src'); + if(curUrl.indexOf('://') == -1 && curUrl.charAt(0) != '/') { + res.files.push({ type: this.tagName, url: $(this).attr('src') }); + } + }); + + // Other resources + $('source, track').each(function() { + res.files.push({ type: this.tagName, url: $(this).attr('src') }); + }); + $('fioi-video-player').each(function() { + var fileAttributes = ["data-source", "data-image", "data-subtitles"]; + for(var i=0; i\ + \ + \ + \ + \ +
Concours castorLe concours Castor
\ + ' + }, + laptop: { + 'header' : '\ +
\ + \ + \ + \ + \ +
Concours AlkindiLe concours Alkindi
\ +
' + }, + none: { + 'header' : '' + } + }; + + function inIframe() { + try { + return window.self !== window.top; + } catch (e) { + return false; + } + } + + + + if(typeof window.jwt == 'undefined') { + window.jwt = { + isDummy: true, + sign: function() { return null; }, + decode: function(token) { return token; } + }; + } + + function TaskToken(data, key) { + + this.data = data + this.data.sHintsRequested = "[]"; + this.key = key + + var query = document.location.search.replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0]; + this.queryToken = query.sToken; + + this.addHintRequest = function(hint_params, callback) { + try { + hint_params = jwt.decode(hint_params).askedHint; + } catch(e) {} + var hintsReq = JSON.parse(this.data.sHintsRequested); + var exists = hintsReq.find(function(h) { + return h == hint_params; + }); + if(!exists) { + hintsReq.push(hint_params); + this.data.sHintsRequested = JSON.stringify(hintsReq); + } + return this.get(callback); + } + + this.update = function(newData, callback) { + for(var key in newData) { + this.data[key] = newData[key]; + } + } + + this.getToken = function(data, callback) { + var res = jwt.sign(data, this.key) + if(callback) { + // imitate async req + setTimeout(function() { + callback(res) + }, 0); + } + return res; + } + + this.get = function(callback) { + if(window.jwt.isDummy && this.queryToken) { + var token = this.queryToken; + if(callback) { + // imitate async req + setTimeout(function() { + callback(token) + }, 0); + } + return token; + } + return this.getToken(this.data, callback); + } + + this.getAnswerToken = function(answer, callback) { + var answerData = {}; + for(var key in this.data) { + answerData[key] = this.data[key]; + } + answerData.sAnswer = answer; + return this.getToken(answerData, callback); + } + } + + + function AnswerToken(key) { + this.key = key + this.get = function(answer, callback) { + var res = jwt.sign(answer, this.key) + if(callback) { + // imitate async req + setTimeout(function() { + callback(res) + }, 0) + } + return res; + } + } + + + +var taskMetaData; + +// important for tracker.js +var compiledTask = true; + +window.miniPlatformShowSolution = function() { + $("#showSolutionButton").hide(); + task.getAnswer(function(answer) { + task.showViews({"task": true, "solution": true}, function() { + // For tasks with no feedback / older tasks + // miniPlatformPreviewGrade(answer); + platform.trigger('showViews', [{"task": true, "solution": true}]); + }); + }); +} + +function miniPlatformPreviewGrade(answer) { + var minScore = -3; + if (taskMetaData.fullFeedback) { + minScore = 0; + } + var maxScore = 40; + var score; + var showGrade = function(score) { + if ($("#previewScorePopup").length === 0) { + $("
" + + "
" + + "


").insertBefore("#solution"); + } + $("#previewScorePopup").show(); + $("#previewScoreMessage").html("" + getLanguageString('showSolution') + " " + score + "/" + maxScore + "
" + getLanguageString('showSolution')); + }; + // acceptedAnswers is not documented, but necessary for old Bebras tasks + if (taskMetaData.acceptedAnswers && taskMetaData.acceptedAnswers[0]) { + if ($.inArray("" + answer, taskMetaData.acceptedAnswers) > -1) { + score = maxScore; + } + else { + score = minScore; + } + showGrade(score); + } else { + score = grader.gradeTask(answer, null, showGrade); + } +} + +var alreadyStayed = false; + +var miniPlatformValidate = function(task) { return function(mode, success, error) { + if (!success) { success = function () { }; } + if (mode == 'nextImmediate' || mode == 'top' || mode == 'log') { + return; + } + if (mode == 'cancel') { + alreadyStayed = false; + } + if (alreadyStayed || (platform.registered_objects && platform.registered_objects.length > 0)) { + platform.trigger('validate', [mode]); + success(); + } else { + // Try to validate + task.getAnswer(function (answer) { + task.gradeAnswer(answer, task_token.getAnswerToken(answer), function (score, message) { + success(); + }) + }); + } + if (mode == 'stay') { + alreadyStayed = true; + } +}}; + +function getUrlParameter(sParam) +{ + var sPageURL = window.location.search.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) + { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == sParam) + { + return decodeURIComponent(sParameterName[1]); + } + } +} + +function getHashParameter(sParam) +{ + var sPageURL = window.location.hash.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) + { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == sParam) + { + return decodeURIComponent(sParameterName[1]); + } + } +} + +var chooseView = (function () { + // Manages the buttons to choose the view + return { + doubleEnabled: false, + isDouble: false, + lastShownViews: {}, + + init: function(views) { + if (! $("#choose-view").length) + $(document.body).append('
'); + $("#choose-view").html(""); + // Display buttons to select task view or solution view + /* + for(var viewName in views) { + if (!views[viewName].requires) { + var btn = $('') + $("#choose-view").append(btn); + btn.click(this.selectFactory(viewName)); + } + } + */ + $("#grade").remove(); + var btnGradeAnswer = $('
'); + // display grader button only if dev mode by adding URL hash 'dev' + if (getHashParameter('dev')) { + $(document.body).append(btnGradeAnswer); + } + btnGradeAnswer.click(function() { + task.getAnswer(function(answer) { + answer_token.get(answer, function(answer_token) { + task.gradeAnswer(answer, answer_token, function(score, message, scoreToken) { + alert("Score : " + score + ", message : " + message); + }); + }) + }, function() { + alert("error"); + }); + }) + }, + + reinit: function(views) { + this.init(views); + var newShownViews = {}; + for(var viewName in this.lastShownViews) { + if(!this.lastShownViews[viewName]) { continue; } + if(views[viewName] && !views[viewName].requires) { + newShownViews[viewName] = true; + } + } + for(var viewName in views) { + if(views[viewName].includes) { + for(var i=0; i'); + var platformLoad = function(task) { + window.task_token.update({id: taskMetaData.id}); + window.answer_token = new AnswerToken(demo_key) + + platform.validate = miniPlatformValidate(task); + platform.updateHeight = function(height,success,error) {if (success) {success();}}; + platform.updateDisplay = function(data,success,error) { + if(data.views) { + chooseView.reinit(data.views); + } + if (success) {success();} + }; + var taskOptions = {}; + try { + var strOptions = getUrlParameter("options"); + if (strOptions !== undefined) { + taskOptions = $.parseJSON(strOptions); + } + } catch(exception) { + alert("Error: invalid options"); + } + var minScore = -3; + if (taskMetaData.fullFeedback) { + minScore = 0; + } + platform.getTaskParams = function(key, defaultValue, success, error) { + var res = {'minScore': minScore, 'maxScore': 40, 'noScore': 0, 'readOnly': false, 'randomSeed': "0", 'options': taskOptions}; + if (key) { + if (key !== 'options' && key in res) { + res = res[key]; + } else if (res.options && key in res.options) { + res = res.options[key]; + } else { + res = (typeof defaultValue !== 'undefined') ? defaultValue : null; + } + } + if (success) { + success(res); + } else { + return res; + } + }; + platform.askHint = function(hint_params, success, error) { + /* + $.post('updateTestToken.php', JSON.stringify({action: 'askHint'}), function(postRes){ + if (success) {success();} + }, 'json'); + */ + task_token.addHintRequest(hint_params, function(token) { + task.updateToken(token, function() {}) + success(token) + }) + }; + + + var loadedViews = {'task': true, 'solution': true, 'hints': true, 'editor': true, 'grader': true, 'metadata': true, 'submission': true}; + var shownViews = {'task': true}; + // TODO: modifs ARTHUR à relire + if (taskOptions.showSolutionOnLoad) { + shownViews.solution = true; + } + if (!taskOptions.hideTitle) { + $("#task h1").show(); + } + + if (taskMetaData.fullFeedback) { + loadedViews.grader = true; + } + + task.load( + loadedViews, + function() { + platform.trigger('load', [loadedViews]); + task.getViews(function(views) { + chooseView.init(views); + }); + task.showViews(shownViews, function() { + chooseView.update(shownViews); + platform.trigger('showViews', [{"task": true}]); + }); + if ($("#solution").length) { + $("#task").append("
"); + } + + // add branded header to platformless task depending on avatarType + // defaults to beaver platform branding + if(window.displayHelper) { + if (miniPlatformWrapping[displayHelper.avatarType].header) { + $('body').prepend(miniPlatformWrapping[displayHelper.avatarType].header); + } else { + $('body').prepend(miniPlatformWrapping[beaver].header); + } + } + }, + function(error) { + console.error(error) + } + ); + + + task_token.get(function(token) { + task.updateToken(token, function() {}) + }) + + + /* For the 'resize' event listener below, we use a cross-browser + * compatible version for "addEventListener" (modern) and "attachEvent" (old). + * Source: https://stackoverflow.com/questions/6927637/addeventlistener-in-internet-explorer + */ + function addEvent(evnt, elem, func) { + if (elem.addEventListener) // W3C DOM + elem.addEventListener(evnt,func,false); + else if (elem.attachEvent) { // IE DOM + elem.attachEvent("on"+evnt, func); + } + else { // No much to do + elem[evnt] = func; + } + } + + addEvent('resize', window, function() { + task.getViews(function(views) { + chooseView.reinit(views); + }); + }); + }; + var getMetaDataAndLoad = function(task) { + task.getMetaData(function(metaData) { + taskMetaData = metaData; + platformLoad(task); + }); + }; + if (window.platform.task || platform.initFailed) { + // case everything went fine with task loading, or task loading failed + // (due to missing jschannel and file:// protocol... + getMetaDataAndLoad(window.task ? window.task : window.platform.task); + } else { + // task is not loaded yet + var oldInit = platform.initWithTask; + platform.initWithTask = function(task) { + oldInit(task); + getMetaDataAndLoad(task); + }; + } + } +}); + +})(); diff --git a/integrationAPI.01/official/miniPlatform.js b/integrationAPI.01/official/miniPlatform.js index 0844df27d..be2996d59 100644 --- a/integrationAPI.01/official/miniPlatform.js +++ b/integrationAPI.01/official/miniPlatform.js @@ -287,36 +287,27 @@ function miniPlatformPreviewGrade(answer) { var alreadyStayed = false; var miniPlatformValidate = function(task) { return function(mode, success, error) { - //$.post('updateTestToken.php', {action: 'showSolution'}, function(){}, 'json'); - if (mode == 'nextImmediate' || mode == 'top' || mode == 'log') { - return; - } - if (mode == 'stay') { - if (alreadyStayed) { - platform.trigger('validate', [mode]); - if (success) { - success(); - } - } else { - alreadyStayed = true; - } - } - if (mode == 'cancel') { - alreadyStayed = false; - } - if(platform.registered_objects && platform.registered_objects.length > 0) { - platform.trigger('validate', [mode]); - } else { - // Try to validate - task.getAnswer(function(answer) { - task.gradeAnswer(answer, task_token.getAnswerToken(answer), function(score, message) { - if(success) { success(); } - }) - }); - } - if (success) { - success(); - } + if (!success) { success = function () { }; } + if (mode == 'nextImmediate' || mode == 'top' || mode == 'log') { + return; + } + if (mode == 'cancel') { + alreadyStayed = false; + } + if (alreadyStayed || (platform.registered_objects && platform.registered_objects.length > 0)) { + platform.trigger('validate', [mode]); + success(); + } else { + // Try to validate + task.getAnswer(function (answer) { + task.gradeAnswer(answer, task_token.getAnswerToken(answer), function (score, message) { + success(); + }) + }); + } + if (mode == 'stay') { + alreadyStayed = true; + } }}; function getUrlParameter(sParam) diff --git a/integrationAPI.01/official/miniPlatform_M.js b/integrationAPI.01/official/miniPlatform_M.js index 8166c2be2..35de19dee 100644 --- a/integrationAPI.01/official/miniPlatform_M.js +++ b/integrationAPI.01/official/miniPlatform_M.js @@ -230,27 +230,27 @@ function miniPlatformPreviewGrade(answer) { var alreadyStayed = false; var miniPlatformValidate = function(mode, success, error) { - //$.post('updateTestToken.php', {action: 'showSolution'}, function(){}, 'json'); - if (mode == 'nextImmediate' || mode == 'top' || mode == 'log') { - return; - } - if (mode == 'stay') { - if (alreadyStayed) { - platform.trigger('validate', [mode]); - if (success) { - success(); - } - } else { - alreadyStayed = true; - } - } - if (mode == 'cancel') { - alreadyStayed = false; - } - platform.trigger('validate', [mode]); - if (success) { - success(); - } + if (!success) { success = function () { }; } + if (mode == 'nextImmediate' || mode == 'top' || mode == 'log') { + return; + } + if (mode == 'cancel') { + alreadyStayed = false; + } + if (alreadyStayed || (platform.registered_objects && platform.registered_objects.length > 0)) { + platform.trigger('validate', [mode]); + success(); + } else { + // Try to validate + task.getAnswer(function (answer) { + task.gradeAnswer(answer, task_token.getAnswerToken(answer), function (score, message) { + success(); + }) + }); + } + if (mode == 'stay') { + alreadyStayed = true; + } }; function getUrlParameter(sParam)