openct-tasks/_common/modules/bundles/bebras-interface.js

3432 lines
138 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(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';
/*
* Implementation of the displayHelper API.
*
* Copyright (c) 2012 Association France-ioi, MIT License http://opensource.org/licenses/MIT
*
* See documentation for more information.
*/
window.displayHelper = {
loaded: false,
timeLoaded: 0,
checkAnswerInterval: null,
prevAnswer: '',
readOnly: false,
savedAnswer: '',
submittedAnswer: '',
submittedScore: 0,
hasAnswerChanged: true,
taskSelector: '#task',
hideValidateButton: false,
hideRestartButton: false,
confirmRestartAll: true,
showScore: false,
refreshMessages: true,
stoppedShowingResult: false,
previousMessages: {},
popupMessageShown: false,
thresholds: {},
// Legacy settings for old tasks ; new ones are expected to use thresholds
thresholdEasy: 60,
thresholdMedium: 120,
timeoutMinutes: 5,
avatarType: "beaver",
bUseFullWidth: false,
hasLevels: false,
pointsAsStars: true, // TODO: false as default
unlockedLevels: 4,
neverHadHard: false,
showMultiversionNotice: false,
taskLevel: '',
// Defaults
levels: ['easy', 'medium', 'hard'],
levelsIdx: { easy: 0, medium: 1, hard: 2 },
maxStars: 4,
popupMessageHandler: null,
formatTranslation: function(s, args) { return s.replace(/\{([^}]+)\}/g, function(_, match){ return args[match]; }); },
languageStrings: {
fr: {
version: "Version",
levelVersionName_easy: "version facile",
levelVersionName_medium: "version moyenne",
levelVersionName_hard: "version difficile",
levelVersionName_easy_stars: "version à 2 étoiles",
levelVersionName_medium_stars: "version à 3 étoiles",
levelVersionName_hard_stars: "version à 4 étoiles",
levelName_easy: "Facile",
levelName_medium: "Moyen",
levelName_hard: "Difficile",
warningTimeout: "<p>Attention, cela fait plus de {0} minutes que vous êtes sur cette question.</p><p>Vous devriez sans doute changer de sujet, en cliquant sur le bouton tout en haut à droite.</p>",
alright: "D'accord",
moveOn: "Passer à la suite",
solvedMoveOn: "Vous avez entièrement résolu cette question, passez à une autre question.",
confirmRestart: "Êtes-vous certain de vouloir recommencer cette version ?",
yes: "Oui",
no: "Non",
tryHardLevel: "Nous vous proposons d'essayer la version 4 étoiles.",
tryMediumLevel: "Nous vous proposons d'essayer la version 3 étoiles.",
tryNextTask: "Nous vous proposons de passer au sujet suivant. S'il vous reste du temps, vous reviendrez plus tard essayer la version suivante.",
yourScoreIsNow: "Votre score est maintenant :",
worseScoreStays: "C'est moins bien qu'avant ; votre score reste :",
scoreStays: "Votre score reste le même :",
score: "Score :",
noPointsForLevel: "Vous n'avez pas encore de points sur cette version.",
outOf: " sur ",
tryToDoBetterOrChangeTask: "Essayez de faire encore mieux, ou passez à une autre question.",
tryToDoBetterOrMoveToNextLevel: "Essayez de faire encore mieux, ou passez à une version plus difficile.",
bestPossibleScoreCongrats: "C'est le meilleur score possible sur ce sujet ; félicitations !",
forMorePointsMoveToNextLevel: "Pour obtenir plus de points, passez à une version plus difficile.",
youDidBetterBefore: "Vous aviez fait mieux avant.",
scoreStays2: "Votre score reste le même.",
reloadBestAnswer: "Rechargez votre meilleure réponse.",
noAnswerSaved: "Aucune réponse actuellement enregistrée pour cette version.",
validate: "Valider",
restart: "Recommencer",
harderLevelSolved: "Attention : vous avez déjà résolu une version plus difficile. Vous ne pourrez pas gagner de points supplémentaires avec cette version.",
showLevelAnyway: "Voir quand même",
scoreObtained: "Score obtenu :",
hardVersionTakesTime: "Résoudre une {0} peut vous prendre beaucoup de temps ; songez en priorité à répondre aux questions en {1} pour gagner des points rapidement.",
illKeepThatInMind: "J'y prendrai garde",
harderLevelAvailable: "Notez que pour cette question, vous pouvez résoudre directement une version plus difficile que celle-ci.",
lockedLevel: "Cette version est verrouillée. Résolvez la précédente pour l'afficher !",
gradeThisAnswer: "Évaluer cette réponse",
// The following messages are used for tasks with no feedback
saveAnswer: "Enregistrer votre réponse",
answerSavedModifyOrCancelIt: "Votre réponse a été enregistrée. Vous pouvez la modifier, ou bien {0} et recommencer.",
cancelIt: "l'annuler",
warningDifferentAnswerSaved: "Attention : une réponse différente est enregistrée.",
youMay: "Vous pouvez {0}.",
reloadIt: "la recharger",
saveThisNewAnswer: "Enregistrer cette nouvelle réponse",
gradingInProgress: "Évaluation en cours",
scoreIs: "Votre score est de :",
point: "point",
points: "points",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "Le concours étant terminé, votre réponse n'est pas enregistrée et votre score reste de :",
scoreWouldBecome: "Avec cette réponse, votre score serait :",
reloadValidAnswer: "Rechargez la réponse validée.",
contestOverAnswerNotSaved: "Le concours est terminé : votre réponse n'est pas enregistrée.",
scoreWouldStay: "Avec cette réponse, votre score resterait le même :",
answerNotSavedContestOver: "Le concours étant terminé, votre réponse n'a pas été enregistrée. Vous pouvez {0}.",
reloadSubmittedAnswer: "recharger la réponse que vous avez soumise",
difficultyWarning: "<strong>Attention :</strong> résoudre cette version prend du temps.<br/>Vous pourrez résoudre bien plus rapidement les versions 2 et 3 étoiles d'autres sujets.",
enemyWarning: "<strong>Attention :</strong> dans ce défi, l'ordinateur vous empêchera de trouver la solution par hasard."
},
en: {
version: "版本",
levelVersionName_easy: "easy version",
levelVersionName_medium: "medium version",
levelVersionName_hard: "hard version",
levelVersionName_easy_stars: "2 stars version",
levelVersionName_medium_stars: "3 stars version",
levelVersionName_hard_stars: "4 stars version",
levelName_easy: "Easy",
levelName_medium: "Medium",
levelName_hard: "Hard",
warningTimeout: "<p>Warning, it has been more than {0} minutes since you started working on this task.</p><p>You should probably switch to a diffrent task, by clicking on the button on the top-right.</p>",
alright: "Alright",
moveOn: "Move on",
solvedMoveOn: "You solved this task completely, move on to another task.",
confirmRestart: "Are you sure you want to restart this version?",
yes: "Yes",
no: "No",
tryHardLevel: "We suggest you try the 4 stars version.",
tryMediumLevel: "We suggest you try the 3 stars version.",
tryNextTask: "We suggest you try the next task. If you still have time, come back later and try the next version of this task.",
yourScoreIsNow: "Your score is now:",
worseScoreStays: "This is not as good as before. Your score stays:",
scoreStays: "Your score stays the same:",
score: "Score:",
noPointsForLevel: "You have not received any points yet on this version.",
outOf: " out of ",
tryToDoBetterOrChangeTask: "Try to do even better, or move on to another task.",
tryToDoBetterOrMoveToNextLevel: "Try to do even better, or move on to a more difficult version.",
bestPossibleScoreCongrats: "This is the best possible score on this task, congratulations!",
forMorePointsMoveToNextLevel: "要想获得更多的分数,请进入更难的版本。",
youDidBetterBefore: "You did better before.",
scoreStays2: "你的得分保持不变",
reloadBestAnswer: "重新加载你的最佳答案。",
noAnswerSaved: "到目前为止,这个版本还没有答案。",
validate: "Validate",
restart: "重新开始",
harderLevelSolved: "Warning: you already solved a harder version of this task. You won't be able to obtain extra points with this version.",
showLevelAnyway: "Show it to me anyways.",
scoreObtained: "得分:",
hardVersionTakesTime: "Solving a {0} can take a lot of time. Consider working on the {1} to gain points quickly.",
illKeepThatInMind: "I'll consider it.",
harderLevelAvailable: "Note that for this task, you may try to directly work on a harder version than this one.",
lockedLevel: "This version is locked. Solve the previous version to display it!",
gradeThisAnswer: "验证一下",
// The following messages are used for tasks with no feedback
saveAnswer: "Save this answer",
answerSavedModifyOrCancelIt: "Your answer has been saved. You can modify it, or {0} and restart.",
cancelIt: "cancel it",
warningDifferentAnswerSaved: "Warning: a different answer was saved before.",
youMay: "You may {0}.",
reloadIt: "reload it",
saveThisNewAnswer: "Save this new answer",
gradingInProgress: "Grading in process",
scoreIs: "Your score is:",
point: "point",
points: "points",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "The contest being over, your new answer was not saved and your score stays:",
scoreWouldBecome: "With this answer, your score would be:",
reloadValidAnswer: "Reload the validated answer.",
contestOverAnswerNotSaved: "The contest being over, your new answer was not saved.",
scoreWouldStay: "With this answer, your score would stay the same:",
answerNotSavedContestOver: "The contest being over, your answer was not saved. You may {0}.",
reloadSubmittedAnswer: "reload the validated answer",
difficultyWarning: "<strong>提醒:</strong> 解决这个版本很耗费时间。<br/>解决2星或3星版本的任务可以更快哦。",
enemyWarning: "<strong>Warning:</strong> in this challenge, the computer will make sure you don't find the solution by chance."
},
sv: {
version: "Version",
levelVersionName_easy: "lätt version",
levelVersionName_medium: "medelsvår version",
levelVersionName_hard: "svår version",
levelVersionName_easy_stars: "2-stjärnig version",
levelVersionName_medium_stars: "3-stjärnig version",
levelVersionName_hard_stars: "4-stjärnig version",
levelName_easy: "Lätt",
levelName_medium: "Medelsvår",
levelName_hard: "Svår",
warningTimeout: "<p>Varning: det har gått mer än {0} minuter sedan du började med den här uppgiften. </p><p>Du borde kanske byta till en annan uppgift, genom att klicka på knappen uppe till höger.</p>",
alright: "Okej",
moveOn: "Gå vidare",
solvedMoveOn: "Du löste uppgiften helt! Gå nu vidare till en annan uppgift.",
confirmRestart: "Är du säker på att du vill börja om med den här versionen?",
yes: "Ja",
no: "Nej",
tryHardLevel: "Vi föreslår att du provar den 4-stjärniga versionen.",
tryMediumLevel: "Vi föreslår att du provar den 3-stjärniga versionen.",
tryNextTask: "Vi föreslår att du provar nästa uppgift. Kom tillbaka senare och prova en svårare version av den här uppgiften.",
yourScoreIsNow: "Din poäng är nu:",
worseScoreStays: "Det är inte lika bra som tidigare. Poängen fortfarande:",
scoreStays: "Din poäng är fortfarande:",
score: "Poäng:",
noPointsForLevel: "Du har inte fått några poäng än på den här versionen.",
outOf: " utav ",
tryToDoBetterOrChangeTask: "Försök klara det ännu bättre, eller gå vidare till en annan uppgift.",
tryToDoBetterOrMoveToNextLevel: "Försök klara det ännu bättre, eller gå vidare till en svårare version.",
bestPossibleScoreCongrats: "Detta är högsta möjliga poäng på den här uppgiften. Grattis!",
forMorePointsMoveToNextLevel: "För att få mer poäng, gå vidare till en svårare version av den här uppgiften.",
youDidBetterBefore: "Det gick bättre tidigare.",
scoreStays2: "Din poäng ändras inte.",
reloadBestAnswer: "Ladda in ditt bästa svar.",
noAnswerSaved: "No answer saved so far for this version.",
validate: "Kontrollera svaret",
restart: "Börja om",
harderLevelSolved: "Varning: du har redan löst en svårare version av den här uppgiften. Du kommer inte kunna få mer poäng med den här versionen.",
showLevelAnyway: "Visa den ändå.",
scoreObtained: "Uppnådd poäng:",
hardVersionTakesTime: "Att lösa en {0} kan ta lång tid. Fundera på om du ska jobba med en {1} för att tjäna poäng snabbare.",
illKeepThatInMind: "Jag ska tänka på det.",
harderLevelAvailable: "Notera att på den här uppgiften kan du direkt försöka med en svårare version än denna.",
lockedLevel: "Den här versionen är låst. Lös den föregående nivån för att visa den!",
gradeThisAnswer: "Bedöm svaret",
// The following messages are used for tasks with no feedback
saveAnswer: "Spara svaret",
answerSavedModifyOrCancelIt: "Ditt svar har sparats. Du kan ändra det, eller {0} och börja om.",
cancelIt: "avbryta det",
warningDifferentAnswerSaved: "Varning: ett annat svar finns redan sparat.",
youMay: "Du kan {0}.",
reloadIt: "ladda in det på nytt",
saveThisNewAnswer: "Spara det här nya svaret",
gradingInProgress: "Rättning pågår",
scoreIs: "Din poäng är:",
point: "poäng",
points: "poäng",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "Eftersom tävlingen är över sparas inte ditt svar och din poäng ändras inte.",
scoreWouldBecome: "Med det här svaret, skulle din poäng vara:",
reloadValidAnswer: "Ladda in det kontrollerade svaret.",
contestOverAnswerNotSaved: "Eftersom tävlingen är över sparas inte ditt svar.",
scoreWouldStay: "Med det här svaret, skulle din poäng inte ändras:",
answerNotSavedContestOver: "Eftersom tävlingen är över sparas inte ditt svar. Du kan {0}.",
reloadSubmittedAnswer: "ladda in det kontrollerade svaret på nytt",
difficultyWarning: "<strong>Varning:</strong> att lösa den här versionen tar lång tid.<br/>Det kan gå snabbare att lösa 2- eller 3-stjärniga versioner av andra uppgifter.",
enemyWarning: "<strong>Varning:</strong> i den här utmaningen kommer datorn se till att du inte hittar lösningen av en slump."
},
fi: {
version: "Versio",
levelVersionName_easy: "helppo versio",
levelVersionName_medium: "hieman vaikeampi versio",
levelVersionName_hard: "vaikea versio",
levelVersionName_easy_stars: "2 tähden versio",
levelVersionName_medium_stars: "3 tähden versio",
levelVersionName_hard_stars: "4 tähden versio",
levelName_easy: "Helppo",
levelName_medium: "Hieman vaikeampi",
levelName_hard: "Vaikea",
warningTimeout: "<p>Varoitus: on kulunut jo yli {0} minuuttia siitä, kun aloit tekemään tätä tehtävää.</p><p>Sinun mahdollisesti kannattaisi siirtyä yrittämään jotain toista tehtävää, klikkaamalla oikean yläkulman nappia.</p>",
alright: "Ok",
moveOn: "Siirry eteenpäin",
solvedMoveOn: "Ratkaisit tämän tehtävän kokonaan, siirry nyt seuraavaan tehtävään.",
confirmRestart: "Oletko varma, että haluat aloittaa tämän version alusta?",
yes: "Kyllä",
no: "Ei",
tryHardLevel: "Ehdotamme, että kokeilet 4 tähden versiota.",
tryMediumLevel: "Ehdotamme, että kokeilet 3 tähden versiota.",
tryNextTask: "Ehdotamme, että kokeilet seuraavaa tehtävää. Jos sinulle jää vielä aikaa, voit myöhemmin palata takaisin tämän tehtävän pariin.",
yourScoreIsNow: "Pisteesi nyt:",
worseScoreStays: "Tämä on aiempaa alhaisempi. Pistemääränäsi säilyy:",
scoreStays: "Pistemääränäsi sailyy:",
score: "Pisteet:",
noPointsForLevel: "Et ole vielä saanut pisteitä tästä versiosta.",
outOf: " / ",
tryToDoBetterOrChangeTask: "Yritä saada vielä paremmat pisteet, tai siirry toiseen tehtävään.",
tryToDoBetterOrMoveToNextLevel: "Yritä saada vielä paremmat pisteet, tai siirry saman tehtävän vaikeampaan versioon.",
bestPossibleScoreCongrats: "Onnittelut: saavutit tehtävän maksimipistemäärän!",
forMorePointsMoveToNextLevel: "Siirry tehtävän vaikeampaan versioon saadaksesi enemmän pisteitä.",
youDidBetterBefore: "Sait aiemmin enemmän pisteitä.",
scoreStays2: "Pistemääräsi säilyy samana.",
reloadBestAnswer: "Palauta paras aiempi vastauksesi.",
noAnswerSaved: "No answer saved so far for this version.",
validate: "Tarkista vastaus",
restart: "Aloita alusta",
harderLevelSolved: "Varoitus: olet jo ratkaissut vaikeamman version tästä tehtävästä. Tämän helpomman version ratkaiseminen ei voi korottaa pistemäärääsi.",
showLevelAnyway: "Siirry joka tapauksessa.",
scoreObtained: "Saatu pistemäärä:",
hardVersionTakesTime: "{0} voi viedä runsaasti aikaa. {1} voi tuottaa pisteitä nopeammin.",
illKeepThatInMind: "Huomioin tämän.",
harderLevelAvailable: "Huomaa, että voit myös suoraan koittaa ratkaista vaikeampaa versiota tästä tehtävästä.",
lockedLevel: "Tämä versio on vielä lukittu: ratkaise ensin helpompi versio!",
gradeThisAnswer: "Pisteytä tämä vastaus",
// The following messages are used for tasks with no feedback
saveAnswer: "Tallenna vastaus",
answerSavedModifyOrCancelIt: "Vastauksesi on tallennettu. Voit muokata sitä, tai {0} ja aloittaa uudelleen alusta.",
cancelIt: "perua sen",
warningDifferentAnswerSaved: "Varoitus: toisenlainen vastaus on tallennettu jo aiemmin.",
youMay: "Voit {0}.",
reloadIt: "ladata sen uudelleen",
saveThisNewAnswer: "tallentaa tämän uuden vastauksen",
gradingInProgress: "Pisteytystä suoritetaan",
scoreIs: "Pistemääräsi on:",
point: "piste",
points: "pisteet",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "Kilpailu on jo päättynyt, joten uutta vastaustasi ei enää tallennettu ja pistemääränäsi säilyy:",
scoreWouldBecome: "Jos tämäkin vastaus huomioitaisiin, pistemääräsi olisi:",
reloadValidAnswer: "Palauta aiemmin hyväksytty vastaus.",
contestOverAnswerNotSaved: "Kilpailu on jo päättynyt, joten uutta vastaustasi ei enää tallennettu.",
scoreWouldStay: "Jos tämäkin vastaus huomioitaisiin, pistemääräsi olisi yhä:",
answerNotSavedContestOver: "Kilpailu on jo päättynyt, joten uutta vastaustasi ei enää tallennettu. Voit {0}.",
reloadSubmittedAnswer: "palauttaa aiemmin lähetetyn vastauksen",
difficultyWarning: "<strong>Varoitus:</strong> tämän version ratkaiseminen vie aikaa.<br/>Saat luultavasti ratkaistua 2 tai 3 tähden version nopeammin.",
enemyWarning: "<strong>Varoitus:</strong> tässä tehtävässä tietokone pyrkii varmistamaan, ettet voi löytää ratkaisua sattumalta."
},
de: {
version: "Version",
levelVersionName_easy: "leichte Version",
levelVersionName_medium: "mittlere Version",
levelVersionName_hard: "schwere Version",
levelVersionName_easy_stars: "2-Sterne-Version",
levelVersionName_medium_stars: "3-Sterne-Version",
levelVersionName_hard_stars: "4-Sterne-Version",
levelName_easy: "Leicht",
levelName_medium: "Mittel",
levelName_hard: "Schwer",
warningTimeout: "<p>Achtung, du bist schon seit {0} Minuten bei dieser Frage.</p><p>Du solltest jetzt zu einer anderen Aufgabe wechseln.</p>",
alright: "OK",
moveOn: "Fortfahren",
solvedMoveOn: "Du hast diese Frage bereits vollständig gelöst. Wähle eine andere Frage zum Bearbeiten aus.",
confirmRestart: "Bist du sicher, dass du diese Version neustarten möchtest?",
yes: "Ja",
no: "Nein",
tryHardLevel: "Wir schlagen dir vor, die 4-Sterne-Version zu bearbeiten.",
tryMediumLevel: "Wir schlagen dir vor, die 3-Sterne-Version zu bearbeiten",
tryNextTask: "Wir schlagen dir vor, die nächste Aufgabe zu bearbeiten. Wenn du am Ende noch Zeit hast, kannst du hierher zurückkehren und die schwerere Version bearbeiten.",
yourScoreIsNow: "Dein Punktestand ist jetzt:",
worseScoreStays: "Das ist weniger als vorher; dein Punktestand bleibt:",
scoreStays: "Dein Punktestand bleibt gleich:",
score: "Punktestand:",
noPointsForLevel: "Du hast noch keine Punkte für diese Version erhalten.",
outOf: " von ",
tryToDoBetterOrChangeTask: "Versuche, dich zu verbessern oder wähle eine andere Frage.",
tryToDoBetterOrMoveToNextLevel: "Versuche, dich zu verbessern oder wähle schwierigere Version.",
bestPossibleScoreCongrats: "Das ist die bestmögliche Punktzahl für diese Aufgabe. Glückwunsch!",
forMorePointsMoveToNextLevel: "Wähle eine schwerere Version aus, um noch mehr Punkte zu bekommen.",
youDidBetterBefore: "Du hast dich verbessern.",
scoreStays2: "Dein Punktestand bleibt gleich.",
reloadBestAnswer: "Deine beste Antwort wieder laden.",
noAnswerSaved: "Bisher noch keine Antwort für diese Version gespeichert.",
validate: "Erstellen",
restart: "Neustarten",
harderLevelSolved: "Achtung: Du hast schon eine schwerere Version gelöst. Du kannst mit dieser Version keine zusätzlichen Punkte bekommen.",
showLevelAnyway: "Trotzdem anzeigen",
scoreObtained: "Erhaltene Punkte:",
hardVersionTakesTime: "Eine {0} zu lösen kann dich viel Zeit kosten; Denke zunächst daran, die Fragen in {1} zu beantworten, um schnell Punkte zu bekommen.",
illKeepThatInMind: "Ich hab das verstanden",
harderLevelAvailable: "Beachte, dass du bei dieser Frage direkt zu einer schwereren Version gehen kannst.",
lockedLevel: "Diese Version ist noch gesperrt. Löse die vorherige um diese freizuschalten.",
gradeThisAnswer: "Diese Antwort auswerten",
// The following messages are used for tasks with no feedback
saveAnswer: "Antwort speichern",
answerSavedModifyOrCancelIt: "Deine Antwort wurde eingereicht. Du kannst sie noch bearbeiten, oder {0} und neu beginnen.",
cancelIt: "Abbrechen",
warningDifferentAnswerSaved: "Achtung: Eine andere Antwort ist bereits gespeichert!",
youMay: "Du kannst {0}.",
reloadIt: "Neu laden",
saveThisNewAnswer: "Diese Antwort speichern",
gradingInProgress: "Das Ergebnis wird ausgewertet …",
scoreIs: "Dein Punktestand beträgt:",
point: "Punkt",
points: "Punkte",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "Der Contest ist vorbei, deine Antwort wurde nicht eingereicht und deine Punktestand bleibt:",
scoreWouldBecome: "Mit dieser Antwort wäre dein Punktestand:",
reloadValidAnswer: "Die gültige Antwort neu laden.",
contestOverAnswerNotSaved: "Der Contest ist vorbei, deine Antwort wurde nicht eingereicht.",
scoreWouldStay: "Mit dieser Antwort bliebe dein Punktestand gleich:",
answerNotSavedContestOver: "Der Contest ist vorbei, deine Antwort wurde nicht eingereicht. Du kannst {0}.",
reloadSubmittedAnswer: "Lade die Lösung, die du eingereicht hast",
difficultyWarning: "<strong>Achtung:</strong> diese Version zu lösen kann einige Zeit in Anspruch nehmen.<br/>Die 2- und 3-Stern Version von anderen Aufgaben lassen sich schneller lösen.",
enemyWarning: "<strong>Attention :</strong> dans ce défi, l'ordinateur vous empêchera de trouver la solution par hasard."
},
ar: {
version: "المستوى",
levelVersionName_easy: "المستوى السهل",
levelVersionName_medium: "المستوى المتوسط",
levelVersionName_hard: "المستوى الصعب",
levelVersionName_easy_stars: "المستوى الأول",
levelVersionName_medium_stars: "المستوى الثاني",
levelVersionName_hard_stars: "المستوى الثالث",
levelName_easy: "سهل",
levelName_medium: "متوسط",
levelName_hard: "صعب",
warningTimeout: "<p>لقد مر وقت طويل منذ أن بدأت في هذه المسألة, من الأفضل أن تبدأ في مسألة أخرى حتى لا يضيع الوقت</p>",
alright: "حسناً",
moveOn: "استمر",
solvedMoveOn: "لقد أجبت على هذا السؤال بالكامل. ابدأ في سؤال أخر",
confirmRestart: "هل ترغب في بدء هذا سؤال من جديد؟",
yes: "نعم",
no: "لا",
tryHardLevel: "نقترح أن تبدأ في المستوى الثالث للسؤال",
tryMediumLevel: "نقترح أن تبدأ في المستوى الثاني للسؤال",
tryNextTask: "نقترح أن تبدأ في المسألة التالية, وإذا تبقى عندك وقت يمكنك حل المستوى الأصعب في هذه المسألة لاحقاً",
yourScoreIsNow: "مجموع نقاطك:",
worseScoreStays: "هذا ليس جيداً. ما زالت نقاطك:",
scoreStays: "نقاطك ما زالت كما هي:",
score: "النقاط",
noPointsForLevel: "لم تحقق أي نقاط في هذا المستوى",
outOf: "من",
tryToDoBetterOrChangeTask: "حاول في مسألة أخرى",
tryToDoBetterOrMoveToNextLevel: "حاول في المستوى الأصعب",
bestPossibleScoreCongrats: "مبروك ... لقد حصلت على أعلى درجة في هذا السؤال",
forMorePointsMoveToNextLevel: "للحصول على المزيد من النقاط جاوب على المستوى الأصعب",
youDidBetterBefore: "لقد قمت بها أفضل من هذا في وقت سابق",
scoreStays2: "ما زالت نقاطك كما هي",
reloadBestAnswer: "اعد تحميل إجابتك الأفضل",
noAnswerSaved: "No answer saved so far for this version.",
validate: "تحقق",
restart: "ابدأ من جديد",
harderLevelSolved: "لقد قمت بحل المستوى الأصعب في هذا السؤال, لن تتمكن من الحصول على درجات أعلى في هذا السؤال",
showLevelAnyway: "اظهرها لي على أي حال",
scoreObtained: "النقاط المكتسبة:",
hardVersionTakesTime: "Solving a {0} can take a lot of time. Consider working on the {1} to gain points quickly.",
illKeepThatInMind: "I'll consider it.",
harderLevelAvailable: "تنبيه: يمكنك حل المستوى الأصعب في هذه المسألة مباشرة",
lockedLevel: "هذا المستوى مغلق. يجب عليك حل المستوى السابق أولا",
gradeThisAnswer: "قيم هذه الإجابة",
// The following messages are used for tasks with no feedback
saveAnswer: "احفظ هذه الإجابة",
answerSavedModifyOrCancelIt: "تم حفظ إجابتك, يمكنك تعديلها أو بدأها من جديد",
cancelIt: "احذفها",
warningDifferentAnswerSaved: "تنبيه: يوجد اجابة أخرى محفوظة سابقاً",
youMay: "You may {0}.",
reloadIt: "حملها من جديد",
saveThisNewAnswer: "احفظ الإجابة الجديدة",
gradingInProgress: "نقوم بالتقييم",
scoreIs: "مجموع نقاطك:",
point: "نقطة",
points: "نقاط",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "المسابقة انتهت. إجابتك الجديدة لم تحفظ ومجموع نقاطك ما زال:",
scoreWouldBecome: " مع تلك الإجابة، مجموع نقاطك أصبح:",
reloadValidAnswer: "اعد تحميل الإجابة المحققة",
contestOverAnswerNotSaved: "المسابقة إنتهت ولم يتم حفظ إجاباتك الجديدة",
scoreWouldStay: "بهذه الإجابة سوف يظل مجموع نقاطك كما هو",
answerNotSavedContestOver: "The contest being over, your answer was not saved. You may {0}.",
reloadSubmittedAnswer: "اعد تحميل الإجابة المحققة",
difficultyWarning: "تنبيه: حل هذه النسخة سوف يستغرق وقت كثير. الإفضل أن تبدأ في حل مسائل أخرى",
enemyWarning: "تحذير: في هذه المسألة سيمنعك الكمبيوتر من إيجاد الحل عن طريق الصدفة. فكر جيداً"
},
es: {
version: "Versión",
levelVersionName_easy: "versión fácil",
levelVersionName_medium: "versión moderada",
levelVersionName_hard: "versión difícil",
levelVersionName_easy_stars: "versión de 2 estrellas",
levelVersionName_medium_stars: "versión de 3 estrellas",
levelVersionName_hard_stars: "versión de 4 estrellas",
levelName_easy: "Fácil",
levelName_medium: "Moderado",
levelName_hard: "Difícil",
warningTimeout: "<p>Atención, ya lleva {0} minutos en esta pregunta.</p><p>Le recomendamos cambiar de tema haciendo click sobre el botón de arriba a la derecha.</p>",
alright: "De acuerdo",
moveOn: "Pasar a la siguiente",
solvedMoveOn: "Ha resuelto completamente esta pregunta. Pase a otra pregunta.",
confirmRestart: "¿Está seguro que desea volver a iniciar esta versión?",
yes: "Sí",
no: "No",
tryHardLevel: "Le recomendamos intentar la versión de 4 estrellas.",
tryMediumLevel: "Le recomendamos intentar la versión de 3 estrellas.",
tryNextTask: "Nous vous proposons de passer au sujet suivant. S'il vous reste du temps, vous reviendrez plus tard essayer la version suivante.",
yourScoreIsNow: "Su puntuación es ahora :",
worseScoreStays: "Esto no está tan bien como antes; su puntuación se mantiene en :",
scoreStays: "Su puntuación se mantiene igual :",
score: "Puntuación :",
noPointsForLevel: "Aún no ha recibido puntos en esta versión.",
outOf: " de ",
tryToDoBetterOrChangeTask: "Intente nuevamente para obtener una mejor puntuación, o pase a la siguiente pregunta.",
tryToDoBetterOrMoveToNextLevel: "Intente nuevamente para obtener una mejor puntuación, o pase una versión más difícil.",
bestPossibleScoreCongrats: "Esta es la mejor puntuación posible en este problema, ¡felicitaciones!",
forMorePointsMoveToNextLevel: "Para obtener más puntos, pase a una versión más difícil.",
youDidBetterBefore: "Realizó un mejor trabajo antes.",
scoreStays2: "Su puntuación se mantiene igual.",
reloadBestAnswer: "Recargar su mejor respuesta.",
noAnswerSaved: "Aún no hay respuesta guardada para esta versión.",
validate: "Validar",
restart: "Reiniciar",
harderLevelSolved: "Atención: ya ha resuelto una versión más difícil. No puede ganar puntos extra con esta versión.",
showLevelAnyway: "Mostrar el nivel de igual manera",
scoreObtained: "Puntuación obtenida:",
hardVersionTakesTime: "Resolver una {0} puede tomar mucho tiempo; le aconsejamos priorizar resolver las preguntas en {1} para ganar puntos rápidamente.",
illKeepThatInMind: "Lo tendré en mente",
harderLevelAvailable: "Note que para esta pregunta, puede resolver directamente una versión más difícil que esta.",
lockedLevel: "Esta versión está bloqueada. Resuelva la version anterior para verla.",
gradeThisAnswer: "Evaluar esta respuesta",
// The following messages are used for tasks with no feedback
saveAnswer: "Guardar su respuesta",
answerSavedModifyOrCancelIt: "Su respuesta fue guardada. Puede modificarla, o bien {0} y reiniciar.",
cancelIt: "cancelarla",
warningDifferentAnswerSaved: "Atención: una respuesta diferente ha sido guardada.",
youMay: "Usted puede {0}.",
reloadIt: "recargarla",
saveThisNewAnswer: "Guardar esta nueva respuesta",
gradingInProgress: "Evaluación en curso",
scoreIs: "Su puntuación es:",
point: "punto",
points: "puntos",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "El concurso está terminando, su respuesta no ha sido guardada y su puntuación se mantiene en:",
scoreWouldBecome: "Con esta respuesta, su puntuación será :",
reloadValidAnswer: "Volver a cargar la respuesta válida.",
contestOverAnswerNotSaved: "El concurso ha terminado: su respuesta no fue guardada.",
scoreWouldStay: "Con esta respuesta, su puntuación será la misma:",
answerNotSavedContestOver: "El concurso está terminando y su respuesta no ha sido guardada. Usted puede {0}.",
reloadSubmittedAnswer: "recargar la respuesta que ha enviado",
difficultyWarning: "<strong>Advertencia:</strong> resolver esta versión toma tiempo.<br/>Usted puede resolver más rápidamente las versiones de 2 y 3 estrellas de otros problemas.",
enemyWarning: "<strong>Advertencia:</strong> en este desafío, la computadora se asegurará que no encuentre la respuesta por casualidad."
},
sl: {
version: "Stopnja",
levelVersionName_easy: "enostavna stopnja",
levelVersionName_medium: "srednja stopnja",
levelVersionName_hard: "težka stopnja",
levelVersionName_easy_stars: "stopnja 2 zvezdici",
levelVersionName_medium_stars: "stopnja 3 zvezdice",
levelVersionName_hard_stars: "stopnja 4 zvezdice",
levelName_easy: "Enostavno",
levelName_medium: "Srednje",
levelName_hard: "Težko",
warningTimeout: "<p>Opozorilo: Odkar rešuješ to nalogo, je minilo že več kot {0} minut.</p><p>Najbolje, da izbereš drugo nalogo, tako da klikneš gumb v zgornjem desnem kotu.</p>",
alright: "V redu",
moveOn: "Nadaljuj",
solvedMoveOn: "Ta naloga je dokončana, nadaljuj z naslednjo nalogo.",
confirmRestart: "Ali res želiš znova začeti to stopnjo?",
yes: "Da",
no: "Ne",
tryHardLevel: "Predlagamo, da poizkusiš stopnjo s 4 zvezdicami.",
tryMediumLevel: "Predlagamo, da poizkusiš stopnjo s 3 zvezdicami.",
tryNextTask: "Prdlagamo, da poizkusiš naslednjo nalogo. Če bo ostalo dovolj časa, se vrni in poizkusi naslednjo stopnjo te naloge.",
yourScoreIsNow: "Tvoj rezultat je:",
worseScoreStays: "Rešitev ni tako dobra, kot prejšnja. Tvoj rezultat ostaja:",
scoreStays: "Tvoj rezultat ostaja enak:",
score: "Rezultat:",
noPointsForLevel: "Na tej stopnji nisi dobil(-a) še nobene točke.",
outOf: " od ",
tryToDoBetterOrChangeTask: "Poskusi nalogo rešiti še bolje ali se premakni na naslednjo nalogo.",
tryToDoBetterOrMoveToNextLevel: "Poskusi nalogo rešiti še bolje ali se premakni na težjo stopnjo.",
bestPossibleScoreCongrats: "Čestitamo, to je najboljši možni rezultat te naloge!",
forMorePointsMoveToNextLevel: "Če želiš dobiti še več točk, se premakni na težjo stopnjo te naloge.",
youDidBetterBefore: "Rešitev je boljša od prejšnje.",
scoreStays2: "Tvoj rezultat ostaja enak.",
reloadBestAnswer: "Znova naloži najboljšo rešitev.",
noAnswerSaved: "No answer saved so far for this version.",
validate: "Preveri",
restart: "Začni znova",
harderLevelSolved: "Opozorilo: Rešil(-a) si že težjo stopnjo te naloge. S to stopnjo ne boš dobil(-a) dodatnih točk.",
showLevelAnyway: "Vseeno mi pokaži stopnjo.",
scoreObtained: "Dobljeni rezultat:",
hardVersionTakesTime: "Reševanje {0} lahko traja veliko časa. Razmisli o reševanju {1}, da boš hitro dobil(-a) točke.",
illKeepThatInMind: "Razmislil(-a) bom.",
harderLevelAvailable: "Ne pozabi, da lahko to nalogo rešuješ na težji stopnji kot je ta.",
lockedLevel: "Ta stopnja je zaklenjena! Za pikaz moraš najprej rešiti prejšnjo stopnjo!",
gradeThisAnswer: "Oceni ta odgovor",
// The following messages are used for tasks with no feedback
saveAnswer: "Shrani ta odgovor",
answerSavedModifyOrCancelIt: "Tvoj odgovor je bil shranjen. Lahko ga spremeniš ali {0} in začneš znova.",
cancelIt: "prekličeš",
warningDifferentAnswerSaved: "Opozorilo: Prej je bil shranjen drugačen odgovor.",
youMay: "Lahko ga {0}.",
reloadIt: "naložiš znova",
saveThisNewAnswer: "Shrani ta nov odgovor",
gradingInProgress: "Ocenjevanje poteka",
scoreIs: "Tvoj rezultat je:",
point: "točka",
points: "točk",
// The following messages are used when viewing tasks after contest is over
contestOverScoreStays: "Ker je tekmovanja konec, tvoj novi odgovor ni bil shranjen in rezultat bo ostal:",
scoreWouldBecome: "S to rešitvijo bi bil tvoj rezultat:",
reloadValidAnswer: "Znova naloži preverjeno rešitev.",
contestOverAnswerNotSaved: "Ker je tekmovanja konec, tvoj novi odgovor ni bil shranjen.",
scoreWouldStay: "S to rešitvijo bi tvoj rezultat ostal enak:",
answerNotSavedContestOver: "Ker je tekmovanja konec, tvoj novi odgovor ni bil shranjen. Lahko {0}.",
reloadSubmittedAnswer: "znova naložiš preverjeno rešitev",
difficultyWarning: "<strong>Opozorilo:</strong> Reševanje te stopnje lahko traja veliko časa. <br/>Hitreje lahko rešiš stopnje z 2 ali s 3 zvezdicami drugih nalog.",
enemyWarning: "<strong>Opozorilo:</strong> Pri tej nalogi ti bo računalnik preprečil naključno rešitev."
},
},
initLanguage: function() {
if (window.stringsLanguage == undefined) {
window.stringsLanguage = 'fr';
}
this.strings = this.languageStrings[window.stringsLanguage];
},
/***********************************************
* Initialization functions called by the task *
***********************************************/
load: function(views) {
this.initLanguage();
var self = this;
this.showScore = (typeof views.grader !== 'undefined' && views.grader === true);
window.platform.getTaskParams(null, null, function(taskParams) {
self.taskParams = taskParams;
self.readOnly = (self.taskParams.readonly === true || self.taskParams.readOnly == 'true');
self.graderScore = +self.taskParams.noScore;
self.savedAnswer = '';
$("#difficultyWarning").html(self.strings.difficultyWarning).addClass("warningHeader");
$("#enemyWarning").html(self.strings.enemyWarning).addClass("warningHeader");
var addTaskHTML = '<div id="displayHelperAnswering" class="contentCentered">';
// Place button placements at the end of HTML if they don't already exist
var placementNames = ['graderMessage', 'validate', 'cancel', 'saved'];
for (var iPlacement = 0; iPlacement < placementNames.length; iPlacement++) {
var placement = 'displayHelper_' + placementNames[iPlacement];
if ($('#' + placement).length === 0) {
addTaskHTML += '<div id="' + placement + '"></div>';
}
}
addTaskHTML += '</div>';
if (!document.getElementById('displayHelperAnswering')) {
$(self.taskSelector).append(addTaskHTML);
}
self.loaded = true;
self.timeLoaded = new Date().getTime();
if (self.popupMessageShown) {
$('#displayHelperAnswering').hide();
}
var taskDelayWarning = function() {
if (self.popupMessageShown) {
self.taskDelayWarningTimeout = setTimeout(taskDelayWarning, 5000);
} else {
self.showPopupMessage(self.formatTranslation(self.strings.warningTimeout, [self.timeoutMinutes]), 'blanket', self.strings.alright, null, null, "warning");
self.taskDelayWarningTimeout = null;
}
};
if (self.timeoutMinutes > 0) {
self.taskDelayWarningTimeout = setTimeout(taskDelayWarning, self.timeoutMinutes * 60 * 1000);
}
});
},
unload: function() {
if (this.taskDelayWarningTimeout) {
this.taskDelayWarningTimeout = clearTimeout(this.taskDelayWarningTimeout);
}
clearInterval(this.checkAnswerInterval);
this.checkAnswerInterval = null;
this.loaded = false;
this.prevAnswer = '';
this.readOnly = false;
this.savedAnswer = '';
this.submittedAnswer = '';
this.submittedScore = 0;
this.hasAnswerChanged = true;
this.hideValidateButton = false;
this.hideRestartButton = false;
this.showScore = false;
this.refreshMessages = true;
this.stoppedShowingResult = false;
this.previousMessages = {};
this.popupMessageShown = false;
this.hasLevels = false;
this.pointsAsStars = true; // TODO: false as default
this.unlockedLevels = 4;
this.neverHadHard = false;
this.showMultiversionNotice = false;
this.taskLevel = '';
this.initLevelVars();
return true;
},
initLevelVars: function() {
var defaultLevelsRanks = { basic: 1, easy: 2, medium: 3, hard: 4 };
this.levelsRanks = {};
this.levelsScores = {};
this.prevLevelsScores = {};
for(var i=0; i < this.levels.length; i++) {
var levelName = this.levels[i];
if(typeof this.levelsRanks[levelName] == 'undefined') {
this.levelsRanks[levelName] = defaultLevelsRanks[levelName];
}
this.levelsScores[levelName] = 0;
this.prevLevelsScores[levelName] = 0;
}
},
setupLevels: function(initLevel, reloadWithCallbacks, levels) {
this.reloadWithCallbacks = reloadWithCallbacks;
this.initLanguage();
if(levels) {
this.levels = levels;
this.levelsIdx = {};
for(var i = 0; i < this.levels.length; i++) {
this.levelsIdx[this.levels[i]] = i;
}
}
this.initLevelVars();
var self = this;
function callSetupLevels() {
if(!initLevel) {
initLevel = self.taskParams.options.difficulty ? self.taskParams.options.difficulty : "easy";
}
self.doSetupLevels(initLevel);
};
if (!this.taskParams) {
window.platform.getTaskParams(null, null, function(taskParams) {
self.taskParams = taskParams;
callSetupLevels();
});
} else {
callSetupLevels();
}
},
doSetupLevels: function(initLevel) {
// TODO To fix: levelWrapper-1 does not work correctly without this part,
// so the level is loaded twice initially (once here, and once below).
if(!this.reloadWithCallbacks) {
task.reloadStateObject(task.getDefaultStateObject(), true);
task.reloadAnswerObject(task.getDefaultAnswerObject());
}
this.setupParams();
if (!document.getElementById('popupMessage')) {
this.setupLevelsTabs();
$('#tabsMenu .li').on('click', function(event) {
event.preventDefault();
var newLevel = $(this).children().attr('href').split('#')[1];
displayHelper.setLevel(newLevel);
});
}
this.setLevel(initLevel);
if (this.unlockedLevels > 1 && this.showMultiversionNotice) {
this.showPopupMessage(this.strings.harderLevelAvailable, 'blanket', this.strings.alright,
function() {
this.showMultiversionNotice = false;
}
);
}
},
setupParams: function() {
var taskParams = this.taskParams;
this.hasLevels = true;
var paramNames = ['pointsAsStars', 'unlockedLevels', 'neverHadHard', 'showMultiversionNotice'];
for (var iParam = 0; iParam < paramNames.length; iParam++) {
var param = paramNames[iParam];
if (taskParams[param] !== undefined) {
this[param] = taskParams[param];
}
}
var maxScore = taskParams.maxScore !== undefined ? taskParams.maxScore : 40;
this.levelsMaxScores = {};
for(var i=0; i < this.levels.length; i++) {
var levelName = this.levels[i];
var levelMaxScore = maxScore * this.levelsRanks[levelName] / this.maxStars;
this.levelsMaxScores[levelName] = this.pointsAsStars ? levelMaxScore : Math.round(levelMaxScore);
}
},
setupLevelsTabs: function() {
var scoreHTML;
var maxScores = this.levelsMaxScores;
if (this.pointsAsStars) {
var titleStarContainers = [];
scoreHTML = '<span></span><span id="titleStars"></span>';
$('#task > h1').append(scoreHTML);
drawStars('titleStars', this.maxStars, 24, 0, 'normal');
} else {
// Disabled: doesn't work with new tabs layout.
//scoreHTML = '<div class="bestScore">Score retenu : <span id="bestScore">0</span> sur ' + maxScores.hard + '</div>';
//$('#tabsContainer').append(scoreHTML);
}
var tabsStarContainers = [];
var tabsHTML = '<div id="tabsMenu">';
var curLevel;
for (curLevel in this.levelsRanks) {
tabsHTML += '<span class="li" id="tab_' + curLevel + '"><a href="#' + curLevel + '">';
if (this.pointsAsStars) {
tabsHTML += '<span class="levelLabel">' + this.strings.version + '</span><span id="stars_' + this.levelsRanks[curLevel] + '"></span>';
} else {
tabsHTML += this.strings["levelName_" + curLevel] + ' — ' +
'<span id="tabScore_' + curLevel + '">0</span> / ' + maxScores[curLevel];
}
tabsHTML += '</a></span>';
}
tabsHTML += '</div>';
$('#tabsContainer').append(tabsHTML);
var self = this;
setTimeout(function() {
for (var iLevel = 0; iLevel < self.levels.length; iLevel++) {
curLevel = self.levels[iLevel];
if (iLevel >= self.unlockedLevels) {
$('#tab_' + curLevel).addClass('lockedLevel');
}
self.updateStarsAtLevel(curLevel);
}
self.updateLayout();
}, 100);
$('#tabsContainer').after('<div id="popupMessage"></div>');
},
updateStarsAtLevel: function(level) {
var rate = this.levelsScores[level] / this.levelsMaxScores[level];
var iLevel = this.levelsIdx[level];
var starsIdx = this.levelsRanks[level];
var mode = 'normal';
if (iLevel >= this.unlockedLevels) {
mode = 'locked';
}
if (this.graderScore > this.levelsMaxScores[level]) {
mode = 'useless';
}
drawStars('stars_' + starsIdx, starsIdx, 14, rate, mode);
},
updateLayout: function() {
if (!this.bUseFullWidth) {
return
}
$('#valider').appendTo($('#displayHelper_validate'));
if(window.innerWidth >= 1200) {
$('#task').addClass('largeScreen');
$('#displayHelperAnswering').appendTo($('#zone_1'));
}
else {
$('#task').removeClass('largeScreen');
if ($('#showSolutionButton')) {
$('#displayHelperAnswering').insertBefore($('#showSolutionButton'));
}
else {
$('#displayHelperAnswering').appendTo($('#task'));
}
}
},
useFullWidth: function() {
// TODO: find a clean way to do this
try {
$('#question-iframe', window.parent.document).css('width', '100%');
} catch(e) {
}
// This try is probably not needed but avoid breaking just in case
try {
$(document).ready(function () {displayHelper.updateLayout();});
$(window).resize(function () {displayHelper.updateLayout();});
this.bUseFullWidth = true;
} catch(e) {
}
},
// Deprecated: use directly levelsMaxScores instead
getLevelsMaxScores: function() {
return this.levelsMaxScores;
},
displayLevel: function(newLevel) {
// Only displays a level, without requesting a level change to the task
if (this.popupMessageShown) {
$('#popupMessage').hide();
$('#displayHelperAnswering, #taskContent').show();
this.popupMessageShown = false;
}
var allLevels = ['basic', 'easy', 'medium', 'hard'];
if(this.levelsRanks) {
for(var lr in this.levelsRanks) {
allLevels.push(lr);
}
}
for(var i=0; i < allLevels.length; i++) {
var curLevel = allLevels[i];
$('#tab_' + curLevel).removeClass('current');
$('.' + curLevel).hide();
}
$('#tab_' + newLevel).addClass('current');
$('.' + newLevel).show();
// Add prev and next classes to .current direct siblings
$('#tabsMenu .li').removeClass('prev next');
$('#tabsMenu .li.current').prev().addClass('prev');
$('#tabsMenu .li.current').next().addClass('next');
},
setLevel: function(newLevel) {
if (this.taskLevel == newLevel) {
return;
}
this.displayLevel(newLevel);
var answer = task.getAnswerObject();
var state = task.getStateObject();
state.level = newLevel;
this.taskLevel = newLevel;
var self = this;
var afterReload = function() {
self.submittedScore = self.levelsScores[self.taskLevel];
self.refreshMessages = true;
self.checkAnswerChanged();
self.stopShowingResult();
if ($('#tab_' + newLevel).hasClass('lockedLevel')) {
self.showPopupMessage(self.strings.lockedLevel, 'lock');
} else if (!self.hasSolution) {
if ($('#tab_' + newLevel).hasClass('uselessLevel') && self.levelsScores[newLevel] < self.levelsMaxScores[newLevel]) {
self.showPopupMessage(self.strings.harderLevelSolved, 'tab', self.strings.showLevelAnyway, null, null, "warning");
} else if (newLevel == 'hard' && self.neverHadHard) {
var hardVersionKey = "levelVersionName_hard";
var easyVersionKey = "levelVersionName_easy";
if (self.pointsAsStars) {
hardVersionKey += "_stars";
easyVersionKey += "_stars";
}
self.showPopupMessage(self.formatTranslation(self.strings.hardVersionTakesTime, [self.strings[hardVersionKey], self.strings[easyVersionKey]]),
'tab',
self.strings.illKeepThatInMind, function() {
self.neverHadHard = false;
}
);
}
}
};
if(self.reloadWithCallbacks) {
task.reloadStateObject(state, function() {
task.reloadAnswerObject(answer, afterReload);
});
}
else {
task.reloadStateObject(state, true);
task.reloadAnswerObject(answer);
afterReload();
}
},
getImgPath: function() {
if(window.contestsRoot) {
// Hack: when in the context of the platform, we need to change the path
return window.contestsRoot + '/' + window.contestFolder + '/';
} else if(window.modulesPath) {
var modulesPath = window.modulesPath[window.modulesPath.length-1] == '/' ? window.modulesPath : window.modulesPath + '/';
return modulesPath + 'img/';
} else {
return '../../../_common/modules/img/';
}
},
getAvatar: function(mood) {
if (displayHelper.avatarType == "beaver") {
return "castor.png";
} else if (displayHelper.avatarType == "none") {
return "";
} else {
if (mood == "success") {
return "laptop_success.png";
} else if (mood == "warning") {
return "laptop_warning.png";
}{
return "laptop_error.png";
}
}
},
showPopupDialog: function(message) {
if ($('#popupMessage').length == 0) {
$('#task').after('<div id="popupMessage"></div>');
}
$('#popupMessage').addClass('floatingMessage');
var imgPath = displayHelper.getImgPath();
var popupHtml = '<div class="container">' +
'<img class="messageArrow" src="' + imgPath + 'fleche-bulle.png"/>' +
'<div class="message">' + message + '</div></div>';
$('#popupMessage').html(popupHtml).show();
this.popupMessageShown = true;
try {
$(parent.document).scrollTop(0);
} catch (e) {
}
},
errorPopupAvatar: function() {
$('#popupMessage').addClass('noAvatar');
},
showPopupMessage: function(message, mode, yesButtonText, agreeFunc, noButtonText, avatarMood, defaultText, disagreeFunc) {
if(this.popupMessageHandler) {
// A custom popupMessageHandler was defined, call it
// It must return true if it handled the popup, false if displayHelper
// should handle the popup instead
if(this.popupMessageHandler.apply(null, arguments)) {
return;
}
}
if ($('#popupMessage').length == 0) {
$('#task').after('<div id="popupMessage"></div>');
}
if (mode == 'blanket' || mode == 'input') {
$('#popupMessage').addClass('floatingMessage');
} else {
$('#taskContent, #displayHelperAnswering').hide();
$('#popupMessage').removeClass('floatingMessage');
}
$('#popupMessage').removeClass('noAvatar');
var imgPath = displayHelper.getImgPath();
if(mode == 'lock') {
var buttonYes = '';
} else if (mode == 'input') {
var buttonYes = '<button class="buttonYes">' + (yesButtonText || this.strings.validate) + '</button>';
} else {
var buttonYes = '<button class="buttonYes">' + (yesButtonText || this.strings.alright) + '</button>';
}
var buttonNo = '';
if (noButtonText != undefined) {
buttonNo = '<button class="buttonNo" style="margin-left: 10px;">' + noButtonText + '</button>';
}
var popupHtml = '<div class="container">' +
'<img class="beaver" src="' + imgPath + this.getAvatar(avatarMood) + '" onerror="displayHelper.errorPopupAvatar();"/>' +
'<img class="messageArrow" src="' + imgPath + 'fleche-bulle.png"/>' +
'<div class="message">' + message + '</div>';
if(mode == 'input') {
popupHtml += '<input id="popupInput" type="text" value="' + (defaultText ? defaultText : '') + '"></input>';
}
popupHtml += '<div class="buttonsWrapper">' + buttonYes + buttonNo + '</div></div>';
$('#popupMessage').html(popupHtml).show();
if(mode == 'input') {
$('#popupInput').focus();
}
var validateFunc = function() {
$('#popupMessage').hide();
$('#displayHelperAnswering, #taskContent').show();
displayHelper.popupMessageShown = false;
if (agreeFunc) {
if(mode == 'input') {
agreeFunc($('#popupInput').val());
} else {
agreeFunc();
}
}
};
var validateFuncNo = function() {
$('#popupMessage').hide();
$('#displayHelperAnswering, #taskContent').show();
displayHelper.popupMessageShown = false;
if (disagreeFunc) {
if(mode == 'input') {
disagreeFunc($('#popupInput').val());
} else {
disagreeFunc();
}
}
};
$('#popupMessage .buttonYes').click(validateFunc);
$('#popupMessage .buttonNo').click(validateFuncNo);
$('#popupInput').keypress(function (e) {
if(e.which === 13) { validateFunc(); }
});
$('#popupMessage .buttonNo').click(function() {
$('#popupMessage').hide();
$('#displayHelperAnswering, #taskContent').show();
displayHelper.popupMessageShown = false;
});
this.popupMessageShown = true;
try {
$(parent.document).scrollTop(0);
} catch (e) {
}
},
// Function to call at the beginning of task loading, before any html has
// been modified. It places the markers where the buttons will appear, if the
// markers are not present already.
showViews: function(views) {
// Fix for an old version of Firefox in which selection was stuck
try {
if (document.getSelection) {
var selection = document.getSelection();
if (selection !== undefined && selection.removeAllRanges !== undefined) {
selection.removeAllRanges();
}
}
} catch (err) {}
this.views = views;
this.hasSolution = (typeof views.solution !== 'undefined');
if (this.hasSolution && this.graderScore) {
this.prevSavedScore = this.graderScore;
}
var self = this;
this.checkAnswerInterval = setInterval(
function() {
self.checkAnswerChanged();
}, 1000);
task.getAnswer(function(answer) {
self.defaultAnswer = answer;
self.refreshMessages = true;
self.checkAnswerChanged();
});
},
reloadAnswer: function(strAnswer) {
this.savedAnswer = strAnswer;
this.prevAnswer = strAnswer;
this.submittedAnswer = strAnswer;
var that = this;
if (this.showScore) {
// TODO we only know the answer here, and not the state. Possibly problematic?
this.updateScore(strAnswer, true, function() {
that.checkAnswerChanged(); // necessary?
});
} else {
that.checkAnswerChanged(); // necessary?
}
},
reloadState: function() {
this.checkAnswerChanged(); // necessary?
},
stopShowingResult: function() {
this.stoppedShowingResult = true;
this.updateMessages();
},
/**********************
* Internal functions *
**********************/
restartAll: function() {
if(this.confirmRestartAll) {
this.showPopupMessage(this.strings.confirmRestart, 'blanket', this.strings.yes, this.restartAllNoConfirm, this.strings.no);
}
else {
this.restartAllNoConfirm();
}
},
restartAllNoConfirm: function() {
displayHelper.stopShowingResult();
if (!displayHelper.hasLevels) {
// TODO is this the desired behavior for no levels?
task.reloadAnswer('', function() {});
} else {
task.getAnswer(function(strAnswer) {
var answer = $.parseJSON(strAnswer);
var defaultAnswer = task.getDefaultAnswerObject();
var level = displayHelper.taskLevel;
answer[level] = defaultAnswer[level];
task.reloadAnswer(JSON.stringify(answer), function() {});
});
}
},
setValidateString: function(str) {
this.customValidateString = str;
$("#displayHelper_validate > input").val(str);
},
callValidate: function() {
if (this.customValidate != undefined) {
this.customValidate();
} else {
platform.validate("none", function() {});
}
},
validate: function(mode) {
this.stoppedShowingResult = false;
var self = this;
if (mode == 'log') {
// Ignore it? Do something?
} else if (mode == 'cancel') {
this.savedAnswer = '';
task.reloadAnswer('', function() {
self.checkAnswerChanged();
});
} else {
task.getAnswer(function(strAnswer) {
if (!self.hasSolution) {
self.prevSavedScore = self.graderScore;
if (self.hasLevels) {
self.prevLevelsScores[self.taskLevel] = self.levelsScores[self.taskLevel];
}
}
var refresh = function() {
self.refreshMessages = true;
self.checkAnswerChanged();
};
self.submittedAnswer = strAnswer;
if (self.showScore) {
self.updateScore(strAnswer, false, refresh, (mode == "silent"));
} else {
self.savedAnswer = strAnswer;
refresh();
}
});
}
},
updateScore: function(strAnswer, allLevels, callback, silentMode) {
var self = this;
function refresh() {
self.refreshMessages = true;
self.checkAnswerChanged();
callback();
}
if (allLevels) {
// TODO: make sure the grader doesn't evaluate each level at each call (most do right now!)
var levelsToDo = this.levels.slice();
var updateNextScore = null;
updateNextScore = function() {
var nextLevel = levelsToDo.shift();
if(nextLevel) {
self.updateScoreOneLevel(strAnswer, nextLevel, updateNextScore);
} else {
refresh();
}
}
updateNextScore();
} else {
this.updateScoreOneLevel(strAnswer, this.taskLevel, function() {
if (!silentMode) {
if (self.hasLevels) {
self.showValidatePopup(self.taskLevel);
} else {
self.showValidatePopup();
}
}
callback();
}, silentMode);
}
},
updateScoreOneLevel: function(strAnswer, gradedLevel, callback, silentMode) {
var self = this;
this.graderMessage = this.strings.gradingInProgress;
task.getLevelGrade(strAnswer, null, function(score, message) {
score = +score;
self.submittedScore = score;
if (self.hasSolution) {
self.graderScore = score;
self.levelsScores[gradedLevel] = score;
} else {
if (self.hasLevels) {
if (score > self.levelsScores[gradedLevel]) {
self.levelsScores[gradedLevel] = score;
self.graderScore = score;
if (self.savedAnswer === '') {
self.savedAnswer = strAnswer;
} else {
var savedAnswerObj = $.parseJSON(self.savedAnswer);
var answerObj = $.parseJSON(strAnswer);
savedAnswerObj[gradedLevel] = answerObj[gradedLevel];
self.savedAnswer = JSON.stringify(savedAnswerObj);
}
}
} else if (score > self.graderScore) {
self.savedAnswer = strAnswer;
self.graderScore = score;
}
}
if (silentMode) {
message = "";
}
if (message !== undefined) {
self.graderMessage = message;
} else {
self.graderMessage = "";
}
// TODO : should not be called from here, might update the display of a level not currently opened!
if (self.hasLevels) {
self.updateScoreDisplays(gradedLevel);
}
callback();
}, gradedLevel);
},
updateScoreDisplays: function(gradedLevel) {
var scores = this.levelsScores;
var maxScores = this.levelsMaxScores;
if (this.pointsAsStars) {
this.updateStarsAtLevel(gradedLevel);
drawStars('titleStars', this.maxStars, 24, this.graderScore / maxScores.hard, 'normal');
} else {
$('#tabScore_' + gradedLevel).html(scores[gradedLevel]);
$('#bestScore').html(this.graderScore);
}
var gradedLevelNum = $.inArray(gradedLevel, this.levels);
var curLevel;
// Possibly unlocking a level
if (maxScores[gradedLevel] == scores[gradedLevel]) {
var unlockedLevel = gradedLevelNum + 1;
if (unlockedLevel < this.levels.length && unlockedLevel >= this.unlockedLevels) {
curLevel = this.levels[unlockedLevel];
$('#tab_' + curLevel).removeClass('lockedLevel');
this.unlockedLevels++;
this.updateStarsAtLevel(curLevel);
}
}
if (scores[gradedLevel] == this.graderScore) {
// Marks levels that can't earn points as useless
for (curLevel in this.levelsRanks) {
if (maxScores[curLevel] > this.graderScore) {
break;
}
if (this.pointsAsStars) {
this.updateStarsAtLevel(curLevel);
}
$('#tab_' + curLevel).addClass('uselessLevel');
}
}
},
showValidatePopup: function(gradedLevel) {
var curTime = new Date().getTime();
var secondsSinceLoaded = (curTime - this.timeLoaded) / 1000;
var actionNext = "stay";
// Display popup to indicate what to do next
var fullMessage = this.graderMessage;
var maxScores = this.levelsMaxScores;
var buttonText = this.strings.alright;
var avatarMood = "error";
if ((gradedLevel == undefined) && (this.graderScore >= this.taskParams.maxScore - 0.001)) {
avatarMood = "success";
buttonText = this.strings.moveOn;
fullMessage += "<br/><br/>";
actionNext = "nextTask";
fullMessage += this.strings.solvedMoveOn;
} else if (maxScores && (gradedLevel !== undefined) && this.graderScore >= maxScores[gradedLevel] - 0.001) {
avatarMood = "success";
buttonText = this.strings.moveOn;
fullMessage += "<br/><br/>";
var levelIdx = this.levelsIdx[gradedLevel];
var nextLevel = levelIdx !== undefined && levelIdx < this.levels.length-1 ? this.levels[levelIdx+1] : null;
if(nextLevel) {
// Offer to try next task if the user solved this difficulty slowly
var threshold = this.thresholds[gradedLevel];
if(!threshold) {
if(gradedLevel == "medium") { threshold = this.thresholdMedium; }
else if(gradedLevel == "easy") { threshold = this.thresholdEasy; }
}
if(!threshold || (threshold && secondsSinceLoaded < threshold)) {
actionNext = nextLevel;
if(gradedLevel == "easy") { fullMessage += this.strings.tryMediumLevel; }
if(gradedLevel == "medium") { fullMessage += this.strings.tryHardLevel; }
} else {
actionNext = "nextTask";
fullMessage += this.strings.tryNextTask;
}
} else {
// Solved the last level, move on
actionNext = "nextTask";
fullMessage += this.strings.solvedMoveOn;
}
}
var self = this;
// Offer an option to stay on the task instead of forcing nextTask
var noButtonText = actionNext == "nextTask" ? this.strings.no : null;
this.showPopupMessage(fullMessage, 'blanket', buttonText,
function() {
// TODO: replace with something compatible with the API.
try {
$(parent.document).scrollTop(0);
} catch (e) {
}
if (actionNext == "nextTask") {
platform.validate("nextImmediate");
} else if(self.levelsIdx[actionNext] !== undefined) {
self.setLevel(actionNext);
}
},
noButtonText,
avatarMood
);
},
// Does task have unsaved answers?
hasNonSavedAnswer: function(callback) {
if (!task) {
return false;
}
var self = this;
task.getAnswer(function(curAnswer) {
if (curAnswer != self.prevAnswer) {
try {
if (self != top && parent.Tracker) {
var data = {
dataType: 'nonSavedAnswer', teamID: parent.teamID, questionKey: parent.currentQuestionKey, answer: curAnswer
};
// Call TrackData, only when loaded in an iframe
// this is not yet document in the API, but should be soonish
parent.Tracker.trackData(data);
}
} catch (e) {}
self.prevAnswer = curAnswer;
}
if (curAnswer != self.submittedAnswer) {
self.submittedAnswer = '';
self.refreshMessages = true;
}
if (curAnswer == self.defaultAnswer && self.savedAnswer === '') {
callback(false);
} else {
callback(curAnswer != self.submittedAnswer);
}
});
},
// Checks task.getAnswer() against previously recorded result, and calls
// displayHelper.updateMessages() accordingly.
checkAnswerChanged: function() {
if (!this.loaded) {
this.checkAnswerInterval = clearInterval(this.checkAnswerInterval);
return;
}
var self = this;
this.hasNonSavedAnswer(function(hasNonSavedAnswer) {
if (hasNonSavedAnswer && !self.hasAnswerChanged) {
self.refreshMessages = true;
self.hasAnswerChanged = true;
} else if (!hasNonSavedAnswer && self.hasAnswerChanged) {
self.refreshMessages = true;
self.hasAnswerChanged = false;
}
if (self.refreshMessages) {
self.updateMessages();
}
});
},
getFullFeedbackSavedMessage: function(taskMode) {
var scoreDiffMsg = this.strings.score;
var showRetrieveAnswer = false;
if (this.submittedAnswer !== '' && this.prevSavedScore !== undefined) {
if (!this.hasSolution) {
if (this.prevSavedScore < this.submittedScore) {
scoreDiffMsg = this.strings.yourScoreIsNow;
} else if (this.prevSavedScore > this.submittedScore) {
scoreDiffMsg = this.strings.worseScoreStays;
showRetrieveAnswer = true;
}
else {
scoreDiffMsg = this.strings.scoreStays;
}
} else {
if (this.prevSavedScore != this.submittedScore) {
scoreDiffMsg = this.strings.contestOverScoreStays + " " + this.prevSavedScore + ". " + this.strings.scoreWouldBecome;
} else if (this.submittedAnswer != this.savedAnswer) {
scoreDiffMsg = this.strings.contestOverScoreStays + " " + this.prevSavedScore + ". " + this.strings.scoreWouldStay;
} else {
scoreDiffMsg = this.strings.scoreIs;
}
}
}
scoreDiffMsg += " " + this.graderScore + this.strings.outOf + this.taskParams.maxScore + ".";
if ((this.hasSolution && this.savedAnswer != this.prevAnswer) ||
(this.graderScore > 0 && (taskMode == 'saved_changed' || showRetrieveAnswer))) {
scoreDiffMsg += ' <a href="#" onclick="displayHelper.retrieveAnswer(); return false;">' + this.strings.reloadValidAnswer + '</a>';
}
return scoreDiffMsg;
},
getFullFeedbackWithLevelsSavedMessage: function() {
var maxScoreLevel = this.levelsMaxScores[this.taskLevel];
var showRetrieveAnswer = false;
var message = "";
var curAnswer = this.submittedAnswer;
var answerExists = false;
if (curAnswer !== '') {
curAnswer = $.parseJSON(curAnswer);
answerExists = !$.isEmptyObject(curAnswer);
}
if (!answerExists) {
if (this.levelsScores[this.taskLevel] > 0) {
if (this.hideScoreDetails) {
message = this.strings.scoreObtained + ' <span id="answerScore">' + this.levelsScores[this.taskLevel] + " " + strPoint + " " + this.strings.outOf + " " + maxScoreLevel + ".</span><br/>";;
} else {
showRetrieveAnswer = true;
}
} else {
message += this.strings.noPointsForLevel;
}
} else {
var strPoint = this.strings.point;
if (this.submittedScore > 1) {
strPoint = this.strings.points;
}
message = this.strings.scoreObtained + ' <span id="answerScore">' + this.submittedScore + " " + strPoint + " " + this.strings.outOf + " " + maxScoreLevel + ".</span><br/>";
if (this.hideScoreDetails) {
} else if (this.hasSolution) {
message += this.strings.contestOverAnswerNotSaved;
if (this.prevSavedScore !== undefined) {
showRetrieveAnswer = true;
}
} else {
var prevScore = this.prevLevelsScores[this.taskLevel];
if (this.prevSavedScore !== undefined) {
if (this.submittedScore > prevScore) {
if (this.submittedScore < maxScoreLevel) {
if (this.taskLevel == "hard") {
message += this.strings.tryToDoBetterOrChangeTask;
} else {
message += this.strings.tryToDoBetterOrMoveToNextLevel;
}
} else if (this.taskLevel == "hard") {
message += this.strings.bestPossibleScoreCongrats;
} else {
message += this.strings.forMorePointsMoveToNextLevel;
}
} else if (this.submittedScore < prevScore) {
message += this.strings.youDidBetterBefore;
showRetrieveAnswer = true;
}
else {
message += this.strings.scoreStays2;
}
}
}
}
if (showRetrieveAnswer) {
message += ' <a href="#" onclick="displayHelper.retrieveAnswer(); return false;">' + this.strings.reloadBestAnswer + '</a>';
}
return message;
},
getFullFeedbackGraderMessage: function(taskMode) {
switch (taskMode) {
case 'saved_unchanged':
var color = 'red';
if (this.submittedScore == this.taskParams.maxScore) {
color = 'green';
} else if (this.submittedScore > 0) {
color = '#ff8c00';
}
if (this.graderMessage !== "") {
if (!this.stoppedShowingResult) {
return '<div style="margin: .2em 0; color: ' + color + '; font-weight: bold;">' + this.graderMessage + '</div>';
}
}
break;
}
return '';
},
// TODO: rename function below to getFullFeedbackValidate, assuming it is not called from outside this file
getFullFeedbackValidateMessage: function(taskMode, disabledStr) {
var strValidate = this.strings.validate;
if (this.customValidateString != undefined) {
strValidate = this.customValidateString;
}
switch (taskMode) {
case 'saved_unchanged':
if (this.graderMessage !== "") {
if (!this.hideValidateButton && !this.hasSolution) {
return '<input type="button" value="' + strValidate + '" onclick="displayHelper.callValidate();" ' +
disabledStr + '/>';
}
}
break;
case 'unsaved_unchanged':
case 'unsaved_changed':
if (!this.hideValidateButton) {
if (this.hasSolution) {
return '<input type="button" value="' + this.strings.gradeThisAnswer + '" onclick="displayHelper.validate(\'test\');" ' +
disabledStr + '/>';
} else {
return '<input type="button" value="' + strValidate + '" onclick="displayHelper.callValidate();" ' +
disabledStr + '/>';
}
}
break;
case 'saved_changed':
if (!this.hideValidateButton) {
if (this.hasSolution) {
return '<input type="button" value="' + this.strings.gradeThisAnswer + '" onclick="displayHelper.validate(\'test\');" ' +
disabledStr + '/>';
} else {
// was: Valider votre nouvelle réponse
return '<input type="button" value="' + strValidate + '" onclick="displayHelper.callValidate();" ' +
disabledStr + '/>';
}
}
break;
}
return '';
},
lastSentHeight: null,
updateMessages: function() {
this.initLanguage();
var self = this;
this.refreshMessages = false;
var suffix, prefix;
if (this.hasAnswerChanged) {
suffix = 'changed';
} else {
suffix = 'unchanged';
}
if (this.savedAnswer !== '' && this.savedAnswer != this.defaultAnswer) {
prefix = 'saved';
} else {
prefix = 'unsaved';
}
if (this.submittedAnswer !== '' && this.submittedAnswer != this.savedAnswer) {
prefix = 'saved'; // equivalent, should be named differently
suffix = 'unchanged';
}
var taskMode = prefix + '_' + suffix;
var messages = { graderMessage: '', validate: '', cancel: '', saved: '' };
var disabledStr = this.readOnly ? ' disabled' : '';
if (this.showScore) {
if (!this.hideRestartButton) {
messages.cancel = '<input type="button" value="' + this.strings.restart + '" onclick="displayHelper.restartAll();"' +
disabledStr + '/></div>';
}
messages.graderMessage = this.getFullFeedbackGraderMessage(taskMode);
messages.validate = this.getFullFeedbackValidateMessage(taskMode, disabledStr);
if (this.hasLevels) {
messages.saved = this.getFullFeedbackWithLevelsSavedMessage(taskMode);
} else {
messages.saved = this.getFullFeedbackSavedMessage(taskMode);
}
} else {
switch (taskMode) {
case 'unsaved_unchanged':
case 'unsaved_changed':
if (!this.hasSolution) {
messages.validate = '<input type="button" value="' + this.strings.saveAnswer + '" ' +
'onclick="platform.validate(\'done\', function(){})" ' + disabledStr + '/>';
}
break;
case 'saved_unchanged':
if (!this.hasSolution) {
messages.saved = this.formatTranslation(this.strings.answerSavedModifyOrCancelIt,
["<a href='#' onclick=\"platform.validate('cancel', function(){}); return false;\" " + disabledStr + ">" + this.strings.cancelIt + "</a>"]);
} else {
messages.saved = this.formatTranslation(this.strings.answerNotSavedContestOver,
["<a href='#' onclick=\"displayHelper.validate('cancel'); return false;\" " + disabledStr + ">" + this.strings.reloadSubmittedAnswer + "</a>"]);
}
break;
case 'saved_changed':
messages.saved = "<br/><b style='color: red;'>" + this.strings.warningDifferentAnswerSaved + "</b> " +
this.formatTranslation(this.strings.youMay, ["<a href='#' onclick='displayHelper.retrieveAnswer(); return false;'>" + this.strings.reloadIt + "</a>"]);
if (!this.hideValidateButton) {
messages.validate = "<input type='button' value='" + this.strings.saveThisNewAnswer + "' onclick=\"platform.validate('done', function(){})\" " + disabledStr + "/>";
}
break;
}
}
for (var type in messages) {
if (this.loaded && (typeof this.previousMessages[type] === 'undefined' || this.previousMessages[type] !== messages[type])) {
$('#displayHelper_' + type).html(messages[type]);
this.previousMessages[type] = messages[type];
}
}
if (this.pointsAsStars && $('#answerScore').length) {
drawStars('answerScore', this.levelsRanks[this.taskLevel], 20,
this.levelsScores[this.taskLevel] / this.levelsMaxScores[this.taskLevel], 'normal');
}
window.task.getHeight(function(height) {
if (height != self.lastSentHeight) {
self.lastSentHeight = height;
window.platform.updateDisplay({height: height}, function(){});
}
});
},
getSavedAnswer: function() {
// Gets the previously saved answer
var retrievedAnswer;
if (this.hasLevels) {
var savedAnswerObj = this.savedAnswer && $.parseJSON(this.savedAnswer);
if(savedAnswerObj) {
var retrievedAnswerObj = task.getAnswerObject();
retrievedAnswerObj[this.taskLevel] = savedAnswerObj[this.taskLevel];
retrievedAnswer = retrievedAnswerObj[this.taskLevel] && JSON.stringify(retrievedAnswerObj);
} else {
retrievedAnswer = null;
}
} else {
retrievedAnswer = this.savedAnswer;
}
return retrievedAnswer;
},
retrieveAnswer: function() {
// Loads previously saved answer
var retrievedAnswer = this.getSavedAnswer();
if(!retrievedAnswer) {
this.showPopupMessage(this.strings.noAnswerSaved, 'blanket', this.strings.alright, null, null, "warning");
return;
}
var self = displayHelper;
task.reloadAnswer(retrievedAnswer, function() {
self.submittedAnswer = self.savedAnswer;
self.updateScore(self.savedAnswer, false, function() {});
});
},
hasSavedAnswer: function() {
// Returns whether a saved answer exists
if (this.hasLevels) {
var savedAnswerObj = this.savedAnswer && $.parseJSON(this.savedAnswer);
if(savedAnswerObj) {
return !!savedAnswerObj[this.taskLevel];
}
} else {
return !!this.savedAnswer;
}
return false;
},
sendBestScore: function(callback, scores, messages) {
var bestLevel = 'easy';
for (var curLevel in scores) {
if (scores[bestLevel] <= scores[curLevel]) {
bestLevel = curLevel;
}
}
callback(scores[bestLevel], messages[bestLevel] + " (" + this.strings["levelVersionName_" + bestLevel] + ")");
}
};
/*
draw nbStars stars of width starWidth in element of id id
fills rate% of them in yellow from the left
mode is "norma", "locked" or "useless"
*/
function drawStars(id, nbStars, starWidth, rate, mode) {
$('#' + id).addClass('stars');
function clipPath(coords, xClip) {
var result = [[coords[0][0], coords[0][1]]];
var clipped = false;
for (var iCoord = 1; iCoord <= coords.length; iCoord++) {
var x1 = coords[iCoord - 1][0];
var y1 = coords[iCoord - 1][1];
var x2 = coords[iCoord % coords.length][0];
var y2 = coords[iCoord % coords.length][1];
if (x2 > xClip) {
if (!clipped) {
result.push([xClip, y1 + (y2 - y1) * (xClip - x1) / (x2 - x1)]);
clipped = true;
}
} else {
if (clipped) {
result.push([xClip, y1 + (y2 - y1) * (xClip - x1) / (x2 - x1)]);
clipped = false;
}
result.push([x2, y2]);
}
}
result.pop();
return result;
}
function pathFromCoords(coords) {
var result = 'm' + coords[0][0] + ',' + coords[0][1];
for (var iCoord = 1; iCoord < coords.length; iCoord++) {
var x1 = coords[iCoord - 1][0];
var y1 = coords[iCoord - 1][1];
var x2 = coords[iCoord][0];
var y2 = coords[iCoord][1];
result += ' ' + (x2 - x1) + ',' + (y2 - y1);
}
result += 'z';
return result;
}
var fillColors = { normal: 'white', locked: '#ddd', useless: '#ced' };
var strokeColors = { normal: 'black', locked: '#ddd', useless: '#444' };
var starCoords = [[25, 60], [5, 37], [35, 30], [50, 5], [65, 30], [95, 37], [75, 60], [78, 90], [50, 77], [22, 90]];
var fullStarCoords = [
[[5, 37], [35, 30], [50, 5], [65, 30], [95, 37], [75, 60], [25, 60]],
[[22, 90], [50, 77], [78, 90], [75, 60], [25, 60]]
];
if ($('#' + id).length == 0) {
return;
}
$('#' + id).html('');
var paper = new Raphael(id, starWidth * nbStars, starWidth * 0.95);
for (var iStar = 0; iStar < nbStars; iStar++) {
var scaleFactor = starWidth / 100;
var deltaX = iStar * starWidth;
var coordsStr = pathFromCoords(starCoords, iStar * 100);
paper.path(coordsStr).attr({
fill: fillColors[mode],
stroke: 'none'
}).transform('s' + scaleFactor + ',' + scaleFactor + ' 0,0 t' + (deltaX / scaleFactor) + ',0');
var ratio = Math.min(1, Math.max(0, rate * nbStars - iStar));
var xClip = ratio * 100;
if (xClip > 0) {
for (var iPiece = 0; iPiece < fullStarCoords.length; iPiece++) {
var coords = clipPath(fullStarCoords[iPiece], xClip);
var star = paper.path(pathFromCoords(coords)).attr({
fill: '#ffc90e',
stroke: 'none'
}).transform('s' + scaleFactor + ',' + scaleFactor + ' 0,0 t' + (deltaX / scaleFactor) + ",0");
}
}
paper.path(coordsStr).attr({
fill: 'none',
stroke: strokeColors[mode],
'stroke-width': 5 * scaleFactor
}).transform('s' + scaleFactor + ',' + scaleFactor + ' 0,0 t' + (deltaX / scaleFactor) + ',0');
}
}
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<k; i++) {
if (compFct(t[k], t[i]) < 0) {
var v = t[i];
t[i] = t[k];
insert(k, j, v);
}
}
};
var msort = function msort(i, j) {
var size = j - i;
if (size < 2)
return;
var k = i + Math.floor(size/2);
msort(i, k);
msort(k, j);
merge(i, k, j);
};
msort(0, t.length);
};
Beav.Array.shuffle = function(t, randomSeed) {
var nbValues = t.length;
for (var iValue = 0; iValue < nbValues; iValue++) {
// TODO: we should pick the next random number at every step
// by calling, e.g., randomSeed = RandomGenerator.next(randomSeed);
var randomShift = randomSeed % (nbValues - iValue);
var pos = iValue + randomShift;
var tmp = t[iValue];
t[iValue] = t[pos];
t[pos] = tmp;
}
};
/**********************************************************************************/
/* Matrix */
Beav.Matrix = new Object();
Beav.Matrix.init = function(nbRows, nbCols, initFct) {
var m = [];
for (var x = 0; x < nbRows; x++) {
var t = [];
for (var y = 0; y < nbCols; y++) {
t.push(initFct(x, y));
}
m.push(t);
}
return m;
};
Beav.Matrix.map = function(m, mapFct) {
var r = [];
for (var x = 0; x < m.length; x++) {
r[x] = [];
for (var y = 0; y < m[x].length; y++) {
r[x][y] = mapFct(m[x][y], x, y, m);
}
}
return r;
};
Beav.Matrix.copy = function(m) {
return Beav.Matrix.map(m, function(v) { return v; });
};
Beav.Matrix.make = function(nbRows, nbCols, v) {
return Beav.Matrix.init(nbRows, nbCols, function() { return v; });
};
Beav.Matrix.forEach = function(m, iterFct) {
for (var x = 0; x < m.length; x++) {
for (var y = 0; y < m[x].length; y++) {
iterFct(m[x][y], x, y, m);
}
}
};
Beav.Matrix.filterCount = function(m, selectFct) {
var count = 0;
for (var x = 0; x < m.length; x++) {
for (var y = 0; y < m[x].length; y++) {
if (selectFct(m[x][y], x, y)) {
count++;
}
}
}
return count;
};
/**********************************************************************************/
/* Matrix3D */
Beav.Matrix3D = new Object();
Beav.Matrix3D.init = function(nbX, nbY, nbZ, initFct) {
var m = [];
for (var x = 0; x < nbX; x++) {
var t = [];
for (var y = 0; y < nbY; y++) {
var r = [];
for (var z = 0; z < nbZ; z++) {
r.push(initFct(x, y, z));
}
t.push(r);
}
m.push(t);
}
return m;
};
Beav.Matrix3D.map = function(m, mapFct) {
var r = [];
for (var x = 0; x < m.length; x++) {
r[x] = [];
for (var y = 0; y < m[x].length; y++) {
r[x][y] = [];
for (var z = 0; z < m[x][y].length; z++) {
r[x][y][z] = mapFct(m[x][y][z], x, y, z, m);
}
}
}
return r;
};
Beav.Matrix3D.copy = function(m) {
return Beav.Matrix3D.map(m, function(v) { return v; });
};
Beav.Matrix3D.make = function(nbX, nbY, nbZ, v) {
return Beav.Matrix3D.init(nbX, nbY, nbZ, function() { return v; });
};
Beav.Matrix3D.forEach = function(m, iterFct) {
for (var x = 0; x < m.length; x++) {
for (var y = 0; y < m[x].length; y++) {
for (var z = 0; z < m[x][y].length; z++) {
iterFct(m[x][y][z], x, y, z, m);
}
}
}
};
Beav.Matrix3D.filterCount = function(m, selectFct) {
var count = 0;
for (var x = 0; x < m.length; x++) {
for (var y = 0; y < m[x].length; y++) {
for (var z = 0; z < m[x][y].length; z++) {
if (selectFct(m[x][y][z], x, y, z)) {
count++;
}
}
}
}
return count;
};
/**********************************************************************************/
/* Exception */
/* Mechanism for having user exceptions that cannot be confused
with JavaScript builtin exceptions.
To throw the exception myExn, do:
Beav.Exception.throw(myExn);
To catch only user exceptions, do:
try {
...
} catch (exn) {
var myExn = Beav.Exception.extract(exn);
...
}
In this case, the exception is automatically re-thrown
if it is not a user exception.
*/
/*
Beav.Exception = {};
Beav.Exception.constructor = function(arg) {
this.contents = arg;
};
Beav.Exception.throw = function(arg) {
throw new Beav.Exception.constructor(arg);
};
Beav.Exception.extract = function(exn) {
if (exn instanceof Beav.Exception.constructor) {
return exn.contents;
} else {
throw exn;
}
};
*/
/**********************************************************************************/
/* Navigator */
Beav.Navigator = new Object();
Beav.Navigator.isIE8 = function() {
return navigator.appVersion.indexOf("MSIE 8.") != -1;
}
/**********************************************************************************/
/* Dom */
Beav.Dom = new Object();
Beav.Dom.showOrHide = function(e, visible) {
if (visible)
e.show();
else
e.hide();
};
/**********************************************************************************/
/* HTML */
Beav.Html = new Object();
// Escape the html characters in a string
Beav.Html.escape = function(stringToEncode) {
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;' };
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<fileAttributes.length; i++) {
var curAttr = $(this).attr(fileAttributes[i]);
curAttr = curAttr ? curAttr.split(';') : [];
for(var a=0; a<curAttr.length; a++) {
var curAttrFile = curAttr[a];
if(curAttrFile && curAttrFile != 'animation' && curAttrFile != 'none') {
res.files.push({ type: fileAttributes[i], url: curAttrFile });
}
}
}
});
taskResourcesLoaded = true;
if(window.taskGetResourcesPost) {
window.taskGetResourcesPost(res, callback);
} else {
callback(res);
}
};
}
function declareResource(type, resource) {
if (!res[type]) {
res[type] = [];
}
res[type].push(resource);
}
window.declareTaskResource = declareResource;
var resourcesObjectForRegistration = {};
$(document).ready(function() {
if (typeof json !== 'undefined') {
res = json;
}
if(window.preprocessingFunctions) {
for(var i=0; i<window.preprocessingFunctions.length; i++) {
window.preprocessingFunctions[i]();
}
}
window.preprocessingFunctions = [];
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.task = [{ type: 'html', content: $('#task').html() }];
res.solution = [{ type: 'html', content: $('#solution').html() }];
if (window.task) {
window.implementGetResources(window.task);
// alias for old code, TODO: remove
window.getTaskResources = task.getResources;
}
});
function fillImages(text, images, res) {
var extensions = ["png", "jpg", "gif", "ttf", "woff", "eot", "mp4", "zip"];
for (var iExt = 0; iExt < extensions.length; iExt++) {
var ext = extensions[iExt];
var regexp = new RegExp("[\'\"]([^;\"\']*." + ext + ")[\'\"]", "g");
while (true) {
var match = regexp.exec(text);
if (!match) {
break;
}
var image = match[1];
if (image.length <= ext.length + 1) {
continue;
}
if ($.inArray(image, images) === -1) {
res.push({ type: 'image', url: image });
images.push(image);
}
}
}
}
})();
(function() {
'use strict';
/*
* Implementation of a small platform for standalone tasks, mostly for
* development, demo and testing purposes.
*
* Requirements:
* - jQuery
* - a Platform class creating a simple platform (present in the standard
* implementation of the integration API
* - task.getMetaData(), as documented in the PEM
*/
// demo platform key
var demo_key = 'buddy'
var languageStrings = {
ar: {
'task': 'Task',
'submission': 'Submission',
'solution': 'Solution',
'editor': 'Edit',
'hints': 'Hints',
'showSolution': '显示答案',
'yourScore': "Your score:",
'canReadSolution': "You can now read the solution at the bottom of this page.",
'gradeAnswer': 'Test grader'
},
fr: {
'task': 'Exercice',
'submission': 'Soumission',
'solution': 'Solution',
'editor': 'Résoudre',
'hints': 'Conseils',
'showSolution': 'Voir la solution',
'yourScore': "Votre score :",
'canReadSolution': "Vous pouvez maintenant lire la solution en bas de la page.",
'gradeAnswer': "Tester le grader"
},
en: {
'task': 'Task',
'submission': 'Submission',
'solution': 'Solution',
'editor': 'Edit',
'hints': 'Hints',
'showSolution': 'Show solution',
'yourScore': "Your score:",
'canReadSolution': "You can now read the solution at the bottom of this page.",
'gradeAnswer': 'Test grader'
},
fi: {
'task': 'Task',
'submission': 'Submission',
'solution': 'Solution',
'editor': 'Edit',
'hints': 'Hints',
'showSolution': 'Show solution',
'yourScore': "Your score:",
'canReadSolution': "You can now read the solution at the bottom of this page.",
'gradeAnswer': 'Test grader'
},
sv: {
'task': 'Task',
'submission': 'Submission',
'solution': 'Solution',
'editor': 'Edit',
'hints': 'Hints',
'showSolution': 'Show solution',
'yourScore': "Your score:",
'canReadSolution': "You can now read the solution at the bottom of this page.",
'gradeAnswer': 'Test grader'
},
de: {
'task': 'Aufgabe',
'submission': 'Abgabe',
'solution': 'Lösung',
'editor': 'Bearbeiten',
'hints': 'Hinweise',
'showSolution': 'Lösung anzeigen',
'yourScore': "Dein Punktestand:",
'canReadSolution': "Du kannst dir jetzt die Lösung unten auf der Seite anschauen.",
'gradeAnswer': 'Test grader'
},
es: {
'task': 'Problema',
'submission': 'Sumisión',
'solution': 'Solución',
'editor': 'Editar',
'hints': 'Pistas',
'showSolution': 'Mostrar solución',
'yourScore': 'Su puntuación:',
'canReadSolution': 'Puede leer la solución al final de esta página.',
'gradeAnswer': 'Test grader'
}
};
function getLanguageString(key) {
// Default to english strings
var ls = languageStrings[window.stringsLanguage] ? languageStrings[window.stringsLanguage] : languageStrings['en'];
var str = ls[key];
return str ? str : '';
}
/*
* Create custom elements for platformless implementation
*/
var miniPlatformWrapping = {
beaver: {
'header' : '\
<div id="miniPlatformHeader">\
<table>\
<td><img src="' + (window.modulesPath?window.modulesPath:'../../../_common/modules') + '/img/castor.png" width="60px" style="display:inline-block;margin-right:20px;vertical-align:middle"/></td>\
<td><span class="platform">高阶思维能力测试</span></td>\
<td><a href="http://concours.castor-informatique.fr/" style="display:inline-block;text-align:right;">Le concours Castor</a></td>\
</table>\
</div>'
},
laptop: {
'header' : '\
<div style="width:100%; border-bottom:1px solid #B47238;overflow:hidden">\
<table style="width:770px;margin: 10px auto;">\
<td><img src="' + (window.modulesPath?window.modulesPath:'../../../_common/modules') + '/img/laptop.png" width="60px" style="display:inline-block;margin-right:20px;vertical-align:middle"/></td>\
<td><span class="platform">Concours Alkindi</span></td>\
<td><a href="http://concours-alkindi.fr/home.html#/" style="display:inline-block;text-align:right;">Le concours Alkindi</a></td>\
</table>\
</div>'
},
none: {
'header' : '<span></span>'
}
};
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) {
$("<div id='previewScorePopup'><div style=\"background-color:#111;opacity: 0.65;filter:alpha(opacity=65);position:absolute;z-index:10;top:0px;left:0px;width:100%;height:2000px\"></div>" +
"<div style='position:fixed;top:100px;left:100px;width:400px;height:200px;background-color:#E0E0FF;color:black;border: solid black 3px;text-align:center;z-index:1000'>" +
"<div style='padding:50px'><span id='previewScoreMessage'></span><br/><br/><input type='button' onclick='$(\"#previewScorePopup\").remove()' value='OK' /></div></div></div>").insertBefore("#solution");
}
$("#previewScorePopup").show();
$("#previewScoreMessage").html("<b>" + getLanguageString('showSolution') + " " + score + "/" + maxScore + "</b><br/>" + 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();
}
} 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('<div id="choose-view" style="margin-top:6em"></div>');
$("#choose-view").html("");
// Display buttons to select task view or solution view
/*
for(var viewName in views) {
if (!views[viewName].requires) {
var btn = $('<button id="choose-view-'+viewName+'" class="btn btn-default choose-view-button">' + getLanguageString(viewName) + '</button>')
$("#choose-view").append(btn);
btn.click(this.selectFactory(viewName));
}
}
*/
$("#grade").remove();
var btnGradeAnswer = $('<center id="grade"><button class="btn btn-default">' + getLanguageString('gradeAnswer') + '</button></center>');
// 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<views[viewName].includes.length; i++) {
if(this.lastShownViews[views[viewName].includes[i]]) {
newShownViews[viewName] = true;
}
}
}
}
this.update(newShownViews);
},
selectFactory: function(viewName) {
var that = this;
return function () {
that.select(viewName);
};
},
select: function(viewName) {
var that = this;
var shownViews = {};
shownViews[viewName] = true;
task.showViews(shownViews, function () {
that.update(shownViews);
});
},
update: function(shownViews) {
this.lastShownViews = shownViews;
$('.choose-view-button').removeClass('btn-info');
for(var viewName in shownViews) {
if(shownViews[viewName]) {
$('#choose-view-'+viewName).addClass('btn-info');
}
};
}
};
})();
window.task_token = new TaskToken({
itemUrl: window.location.href,
randomSeed: Math.floor(Math.random() * 10)
}, demo_key);
$(document).ready(function() {
var hasPlatform = false;
try {
hasPlatform = (inIframe() && (typeof parent.TaskProxyManager !== 'undefined') && (typeof parent.generating == 'undefined' || parent.generating === true));
var testEdge = parent.TaskProxyManager; // generates an exception on edge when in a platform (parent not available)
} catch(ex) {
// iframe from files:// url are considered cross-domain by Chrome
if(location.protocol !== 'file:') {
hasPlatform = true;
}
}
if (!hasPlatform) {
$('head').append('<link rel="stylesheet"type="text/css" href="' + (window.modulesPath?window.modulesPath:'../../../_common/modules') + '/integrationAPI.01/official/miniPlatform.css">');
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("<center id='showSolutionButton'><button type='button' class='btn btn-default' onclick='miniPlatformShowSolution()'>" + getLanguageString('showSolution') + "</button></center>");
}
// 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);
};
}
}
});
})();