mirror of
https://github.com/moodle/moodle.git
synced 2025-03-10 02:40:10 +01:00
405 lines
11 KiB
Plaintext
405 lines
11 KiB
Plaintext
//CORE CODE FOR CHECKING SHORT ANSWER GUESSES AGAINST ANSWER ARRAYS
|
|
|
|
var CaseSensitive = [boolCaseSensitive];
|
|
var ShowAlsoCorrect = [boolShowAlsoCorrect];
|
|
var PleaseEnter = '[strPleaseEnter]';
|
|
var HybridTries = [intHybridTries];
|
|
var PartlyIncorrect = '[strPartlyIncorrect]';
|
|
var CorrectList = '[strCorrectList]';
|
|
var NextCorrect = '[strNextCorrect]';
|
|
var CurrBox = null;
|
|
|
|
function TrackFocus(BoxID){
|
|
InTextBox = true;
|
|
CurrBox = document.getElementById(BoxID);
|
|
}
|
|
|
|
function LeaveGap(){
|
|
InTextBox = false;
|
|
}
|
|
|
|
function TypeChars(Chars){
|
|
if (CurrBox != null){
|
|
//Following check added for 6.0.4.4 to avoid error message in IE6
|
|
if (CurrBox.style.display != 'none'){
|
|
CurrBox.value += Chars;
|
|
CurrBox.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
function CheckGuess(Guess, Answer, CaseSensitive, PercentCorrect, Feedback){
|
|
this.Guess = Guess;
|
|
this.Answer = Answer;
|
|
this.PercentCorrect = PercentCorrect;
|
|
this.Feedback = Feedback;
|
|
if (CaseSensitive == false){
|
|
this.WorkingGuess = Guess.toLowerCase();
|
|
this.WorkingAnswer = Answer.toLowerCase();
|
|
}
|
|
else{
|
|
this.WorkingGuess = Guess;
|
|
this.WorkingAnswer = Answer;
|
|
}
|
|
this.Hint = '';
|
|
this.HintPenalty = 1/Answer.length;
|
|
this.CorrectStart = '';
|
|
this.WrongMiddle = '';
|
|
this.CorrectEnd = '';
|
|
this.PercentMatch = 0;
|
|
this.DoCheck();
|
|
}
|
|
|
|
function CheckGuess_DoCheck(){
|
|
//Check if it's an exact match
|
|
if (this.WorkingAnswer == this.WorkingGuess){
|
|
this.PercentMatch = 100;
|
|
this.CorrectStart = this.Guess;
|
|
return;
|
|
}
|
|
//Figure out how much of the beginning is correct
|
|
var i = 0;
|
|
var CorrectChars = 0;
|
|
while (this.WorkingAnswer.charAt(i) == this.WorkingGuess.charAt(i)){
|
|
i++;
|
|
CorrectChars++;
|
|
}
|
|
//Stash the hint
|
|
this.Hint = this.Answer.charAt(i);
|
|
|
|
this.CorrectStart = this.Guess.substring(0, i);
|
|
|
|
//If there's more to the answer, look at the rest of it
|
|
if (i<this.Guess.length){
|
|
|
|
//Figure out how much of the end is correct
|
|
var j = this.WorkingGuess.length-1;
|
|
var k = this.WorkingAnswer.length-1;
|
|
while ((j>=i)&&((this.WorkingAnswer.charAt(k) == this.WorkingGuess.charAt(j))&&(CorrectChars < this.Answer.length))){
|
|
CorrectChars++;
|
|
j--;
|
|
k--;
|
|
}
|
|
this.CorrectEnd = this.Guess.substring(j+1, this.Guess.length);
|
|
this.WrongMiddle = this.Guess.substring(i, j+1);
|
|
}
|
|
if (TrimString(this.WrongMiddle).length < 1){this.WrongMiddle = '_';}
|
|
//Calculate match score based on how much of the guess is correct
|
|
if (CorrectChars < this.Answer.length){
|
|
this.PercentMatch = Math.floor(100*CorrectChars)/this.Answer.length;
|
|
}
|
|
else{
|
|
this.PercentMatch = Math.floor((100 * CorrectChars)/this.Guess.length);
|
|
}
|
|
}
|
|
|
|
CheckGuess.prototype.DoCheck = CheckGuess_DoCheck;
|
|
|
|
function CheckAnswerArray(CaseSensitive){
|
|
this.CaseSensitive = CaseSensitive;
|
|
this.Answers = new Array();
|
|
this.Score = 0;
|
|
this.Feedback = '';
|
|
this.Hint = '';
|
|
this.HintPenalty = 0;
|
|
this.MatchedAnswerLength = 1;
|
|
this.CompleteMatch = false;
|
|
this.MatchNum = -1;
|
|
}
|
|
|
|
function CheckAnswerArray_AddAnswer(Guess, Answer, PercentCorrect, Feedback){
|
|
this.Answers.push(new CheckGuess(Guess, Answer, this.CaseSensitive, PercentCorrect, Feedback));
|
|
}
|
|
|
|
CheckAnswerArray.prototype.AddAnswer = CheckAnswerArray_AddAnswer;
|
|
|
|
function CheckAnswerArray_ClearAll(){
|
|
this.Answers.length = 0;
|
|
}
|
|
|
|
CheckAnswerArray.prototype.ClearAll = CheckAnswerArray_ClearAll;
|
|
|
|
function CheckAnswerArray_GetBestMatch(){
|
|
//First check for a 100% match
|
|
for (var i=0; i<this.Answers.length; i++){
|
|
if (this.Answers[i].PercentMatch == 100){
|
|
this.Feedback = this.Answers[i].Feedback;
|
|
this.Score = this.Answers[i].PercentCorrect;
|
|
this.CompleteMatch = true;
|
|
this.MatchNum = i;
|
|
return;
|
|
}
|
|
}
|
|
//Now check for the best alternative match
|
|
var PercentMatch = 0;
|
|
var BestMatch = -1;
|
|
for (i=0; i<this.Answers.length; i++){
|
|
if ((this.Answers[i].PercentMatch > PercentMatch)&&(this.Answers[i].PercentCorrect == 100)){
|
|
BestMatch = i;
|
|
PercentMatch = this.Answers[i].PercentMatch;
|
|
}
|
|
}
|
|
if (BestMatch > -1){
|
|
this.Score = this.Answers[BestMatch].PercentMatch;
|
|
this.Feedback = PartlyIncorrect + ' ';
|
|
this.Feedback += '<span class="PartialAnswer">' + this.Answers[BestMatch].CorrectStart;
|
|
this.Feedback += '<span class="Highlight">' + this.Answers[BestMatch].WrongMiddle + '</span>';
|
|
this.Feedback += this.Answers[BestMatch].CorrectEnd + '</span>';
|
|
this.Hint = '<span class="PartialAnswer">' + this.Answers[BestMatch].CorrectStart;
|
|
this.Hint += '<span class="Highlight">' + this.Answers[BestMatch].Hint + '</span></span>';
|
|
this.HintPenalty = this.Answers[BestMatch].HintPenalty;
|
|
}
|
|
else{
|
|
this.Score = 0;
|
|
this.Feedback = '';
|
|
}
|
|
}
|
|
|
|
CheckAnswerArray.prototype.GetBestMatch = CheckAnswerArray_GetBestMatch;
|
|
|
|
function CheckShortAnswer(QNum){
|
|
//bail if question doesn't exist or exercise finished
|
|
if ((State[QNum].length < 1)||(Finished == true)){return;}
|
|
|
|
//bail if question already complete
|
|
if (State[QNum][0] > -1){return;}
|
|
|
|
//Get the guess (TrimString added to fix bug for 6.0.4.3)
|
|
var G = TrimString(document.getElementById('Q_' + QNum + '_Guess').value);
|
|
|
|
//If no guess, bail with message; no penalty
|
|
if (G.length < 1){
|
|
ShowMessage(PleaseEnter);
|
|
return;
|
|
}
|
|
|
|
//Increment tries
|
|
State[QNum][2]++;
|
|
|
|
//Create a check object
|
|
var CA = new CheckAnswerArray(CaseSensitive);
|
|
|
|
CA.ClearAll();
|
|
for (var ANum=0; ANum<I[QNum][3].length; ANum++){
|
|
CA.AddAnswer(G, I[QNum][3][ANum][0], I[QNum][3][ANum][3], I[QNum][3][ANum][1]);
|
|
}
|
|
CA.GetBestMatch();
|
|
|
|
//Store any match in the state tracking field
|
|
if (State[QNum][5].length > 0){State[QNum][5] += ' | ';}
|
|
if (CA.MatchNum > -1){
|
|
State[QNum][5] += String.fromCharCode(65+CA.MatchNum);
|
|
}
|
|
//Else store the student's answer
|
|
else{
|
|
State[QNum][5] += G;
|
|
}
|
|
|
|
//Add the percent correct value for this answer to the Q State (works for all
|
|
//situations, wrong or right)
|
|
State[QNum][3] += CA.Score;
|
|
|
|
//Now branch, based on the nature of the match
|
|
//Is it a complete match?
|
|
if (CA.CompleteMatch == true){
|
|
|
|
//Is it with a wrong answer, or a right answer?
|
|
if (CA.Score == 100){
|
|
//It's right
|
|
CalculateShortAnsQuestionScore(QNum);
|
|
//Get correct answer list if required, assuming there are any other correct alternatives
|
|
if (ShowAlsoCorrect == true){
|
|
var AlsoCorrectList = GetCorrectList(QNum, G, false);
|
|
if (AlsoCorrectList.length > 0){
|
|
CA.Feedback += '<br />' + CorrectList + '<br />' + AlsoCorrectList;
|
|
}
|
|
}
|
|
|
|
//Get the overall score and add it to the feedback
|
|
if (ContinuousScoring == true){
|
|
CalculateOverallScore();
|
|
CA.Feedback += '<br />' + YourScoreIs + ' ' + Score + '%.';
|
|
WriteToInstructions(YourScoreIs + ' ' + Score + '%.');
|
|
}
|
|
ShowMessage(CA.Feedback);
|
|
//Put the answer in
|
|
ReplaceGuessBox(QNum, G);
|
|
CheckFinished();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Otherwise, it's a match to a predicted wrong/partially correct, or a partial
|
|
//match to a right answer
|
|
if (CA.Feedback.length < 1){CA.Feedback = DefaultWrong;}
|
|
//Remove any previous score unless exercise is finished (6.0.3.8+)
|
|
if (Finished == false){
|
|
WriteToInstructions(strInstructions);
|
|
}
|
|
ShowMessage(CA.Feedback);
|
|
|
|
//If necessary, switch a hybrid question to m/c
|
|
if (State[QNum][2] >= HybridTries){
|
|
SwitchHybridDisplay(QNum);
|
|
}
|
|
}
|
|
|
|
function CalculateShortAnsQuestionScore(QNum){
|
|
var Tries = State[QNum][2] + State[QNum][4]; //include tries and hint penalties;
|
|
var PercentCorrect = State[QNum][3];
|
|
var HintPenalties = State[QNum][4];
|
|
|
|
//Make sure it's not already complete
|
|
if (State[QNum][0] < 0){
|
|
if (HintPenalties >= 1){
|
|
State[QNum][0] = 0;
|
|
}
|
|
else{
|
|
State[QNum][0] = (PercentCorrect/(100*Tries));
|
|
}
|
|
if (State[QNum][0] < 0){
|
|
State[QNum][0] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
function SwitchHybridDisplay(QNum){
|
|
if (document.getElementById('Q_' + QNum + '_Hybrid_MC') != null){
|
|
document.getElementById('Q_' + QNum + '_Hybrid_MC').style.display = '';
|
|
if (document.getElementById('Q_' + QNum + '_SA') != null){
|
|
document.getElementById('Q_' + QNum + '_SA').style.display = 'none';
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetCorrectArray(QNum){
|
|
var Result = new Array();
|
|
for (var ANum=0; ANum<I[QNum][3].length; ANum++){
|
|
if (I[QNum][3][ANum][2] == 1){ //This is an acceptable correct answer
|
|
Result.push(I[QNum][3][ANum][0]);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
function GetCorrectList(QNum, Answer, IncludeAnswer){
|
|
var As = GetCorrectArray(QNum);
|
|
var Result = '';
|
|
for (var ANum=0; ANum<As.length; ANum++){
|
|
if ((IncludeAnswer == true)||(As[ANum] != Answer)){
|
|
Result += As[ANum] + '<br />';
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
function GetFirstCorrectAnswer(QNum){
|
|
var As = GetCorrectArray(QNum);
|
|
if (As.length > 0){
|
|
return As[0];
|
|
}
|
|
else{
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function ReplaceGuessBox(QNum, Ans){
|
|
if (document.getElementById('Q_' + QNum + '_SA') != null){
|
|
var El = document.getElementById('Q_' + QNum + '_SA');
|
|
while (El.childNodes.length > 0){
|
|
El.removeChild(El.childNodes[0]);
|
|
}
|
|
var A = document.createElement('span');
|
|
A.setAttribute('class', 'Answer');
|
|
var T = document.createTextNode(Ans);
|
|
A.appendChild(T);
|
|
El.appendChild(A);
|
|
}
|
|
}
|
|
|
|
[inclShowAnswer]
|
|
|
|
function ShowAnswers(QNum){
|
|
//bail if question doesn't exist or exercise finished
|
|
if ((State[QNum].length < 1)||(Finished == true)){return;}
|
|
|
|
//Get the answer list to display
|
|
var Ans = GetCorrectList(QNum, '', false);
|
|
Ans = CorrectList + '<br />' + Ans;
|
|
|
|
//Display feedback
|
|
ShowMessage(Ans);
|
|
|
|
//Set the score for this question to 0 if no
|
|
if (State[QNum][0] < 1){
|
|
State[QNum][0] = 0;
|
|
}
|
|
|
|
//Get the first correct answer
|
|
var FirstAns = GetFirstCorrectAnswer(QNum);
|
|
|
|
//Replace the textbox
|
|
ReplaceGuessBox(QNum, FirstAns);
|
|
|
|
//Remove any current score
|
|
WriteToInstructions(strInstructions);
|
|
|
|
//This may be the last, so check finished status
|
|
CheckFinished();
|
|
}
|
|
|
|
[/inclShowAnswer]
|
|
|
|
[inclHint]
|
|
|
|
function ShowHint(QNum){
|
|
//bail if question doesn't exist or exercise finished
|
|
if ((State[QNum].length < 1)||(Finished == true)){return;}
|
|
|
|
//bail if question already complete
|
|
if (State[QNum][0] > -1){return;}
|
|
|
|
//Get the guess
|
|
var G = document.getElementById('Q_' + QNum + '_Guess').value;
|
|
|
|
//If no guess, give the first correct bit
|
|
if (G.length < 1){
|
|
var Ans = GetFirstCorrectAnswer(QNum);
|
|
var Hint = Ans.charAt(0);
|
|
ShowMessage(NextCorrect + '<br />' + Hint);
|
|
//Penalty for hint
|
|
State[QNum][4] += (1/Ans.length);
|
|
return;
|
|
}
|
|
|
|
//Increment tries
|
|
State[QNum][2]++;
|
|
|
|
//Create a check object
|
|
var CA = new CheckAnswerArray(CaseSensitive);
|
|
|
|
CA.ClearAll();
|
|
for (var ANum=0; ANum<I[QNum][3].length; ANum++){
|
|
//Use only correct answers
|
|
if (I[QNum][3][ANum][2] == 1){
|
|
CA.AddAnswer(G, I[QNum][3][ANum][0], I[QNum][3][ANum][3], I[QNum][3][ANum][1]);
|
|
}
|
|
}
|
|
CA.GetBestMatch();
|
|
if (CA.CompleteMatch == true){
|
|
//It's right!
|
|
CheckShortAnswer(QNum);
|
|
return;
|
|
}
|
|
else{
|
|
if (CA.Hint.length > 0){
|
|
ShowMessage(NextCorrect + '<br />' + CA.Hint);
|
|
State[QNum][4] += CA.HintPenalty;
|
|
}
|
|
else{
|
|
ShowMessage(DefaultWrong + '<br />' + NextCorrect + '<br />' + GetFirstCorrectAnswer(QNum).charAt(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
[/inclHint] |