Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / mod / hotpot / template / v6 / hp6checkshortanswer.js_
blob6792ef1981075f3ccaba0607d9b125478bd7a1a2
1 //CORE CODE FOR CHECKING SHORT ANSWER GUESSES AGAINST ANSWER ARRAYS
3 var CaseSensitive = [boolCaseSensitive];
4 var ShowAlsoCorrect = [boolShowAlsoCorrect];
5 var PleaseEnter = '[strPleaseEnter]';
6 var HybridTries = [intHybridTries];
7 var PartlyIncorrect = '[strPartlyIncorrect]';
8 var CorrectList = '[strCorrectList]';
9 var NextCorrect = '[strNextCorrect]';
10 var CurrBox = null;
12 function TrackFocus(BoxID){
13         InTextBox = true;
14         CurrBox = document.getElementById(BoxID);
17 function LeaveGap(){
18         InTextBox = false;
21 function TypeChars(Chars){
22         if (CurrBox != null){
23 //Following check added for 6.0.4.4 to avoid error message in IE6
24                 if (CurrBox.style.display != 'none'){
25                         CurrBox.value += Chars;
26                         CurrBox.focus();
27                 }
28         }
31 function CheckGuess(Guess, Answer, CaseSensitive, PercentCorrect, Feedback){
32         this.Guess = Guess;
33         this.Answer = Answer;
34         this.PercentCorrect = PercentCorrect;
35         this.Feedback = Feedback;
36         if (CaseSensitive == false){
37                 this.WorkingGuess = Guess.toLowerCase();
38                 this.WorkingAnswer = Answer.toLowerCase();
39         }
40         else{
41                 this.WorkingGuess = Guess;
42                 this.WorkingAnswer = Answer;
43         }
44         this.Hint = '';
45         this.HintPenalty = 1/Answer.length;
46         this.CorrectStart = '';
47         this.WrongMiddle = '';
48         this.CorrectEnd = '';
49         this.PercentMatch = 0;
50         this.DoCheck();
53 function CheckGuess_DoCheck(){
54 //Check if it's an exact match
55         if (this.WorkingAnswer == this.WorkingGuess){
56                 this.PercentMatch = 100;
57                 this.CorrectStart = this.Guess;
58         return;
59         }
60 //Figure out how much of the beginning is correct
61         var i = 0;
62         var CorrectChars = 0;
63         while (this.WorkingAnswer.charAt(i) == this.WorkingGuess.charAt(i)){
64                 i++;
65                 CorrectChars++;
66         }
67 //Stash the hint
68         this.Hint = this.Answer.charAt(i);
70         this.CorrectStart = this.Guess.substring(0, i);
72 //If there's more to the answer, look at the rest of it
73         if (i<this.Guess.length){
75 //Figure out how much of the end is correct
76                 var j = this.WorkingGuess.length-1;
77                 var k = this.WorkingAnswer.length-1;
78                 while ((j>=i)&&((this.WorkingAnswer.charAt(k) == this.WorkingGuess.charAt(j))&&(CorrectChars < this.Answer.length))){
79                         CorrectChars++;
80                         j--;
81                         k--;
82                 }
83                 this.CorrectEnd = this.Guess.substring(j+1, this.Guess.length);
84                 this.WrongMiddle = this.Guess.substring(i, j+1);
85         }
86         if (TrimString(this.WrongMiddle).length < 1){this.WrongMiddle = '_';}
87 //Calculate match score based on how much of the guess is correct
88         if (CorrectChars < this.Answer.length){
89                 this.PercentMatch = Math.floor(100*CorrectChars)/this.Answer.length;
90         }
91         else{
92                 this.PercentMatch = Math.floor((100 * CorrectChars)/this.Guess.length);
93         }
96 CheckGuess.prototype.DoCheck = CheckGuess_DoCheck;
98 function CheckAnswerArray(CaseSensitive){
99         this.CaseSensitive = CaseSensitive;
100         this.Answers = new Array();
101         this.Score = 0;
102         this.Feedback = '';
103         this.Hint = '';
104         this.HintPenalty = 0;
105         this.MatchedAnswerLength = 1;
106         this.CompleteMatch = false;
107         this.MatchNum = -1;
110 function CheckAnswerArray_AddAnswer(Guess, Answer, PercentCorrect, Feedback){
111         this.Answers.push(new CheckGuess(Guess, Answer, this.CaseSensitive, PercentCorrect, Feedback));
114 CheckAnswerArray.prototype.AddAnswer = CheckAnswerArray_AddAnswer;
116 function CheckAnswerArray_ClearAll(){
117         this.Answers.length = 0;
120 CheckAnswerArray.prototype.ClearAll = CheckAnswerArray_ClearAll;
122 function CheckAnswerArray_GetBestMatch(){
123 //First check for a 100% match
124         for (var i=0; i<this.Answers.length; i++){
125                 if (this.Answers[i].PercentMatch == 100){
126                         this.Feedback = this.Answers[i].Feedback;
127                         this.Score = this.Answers[i].PercentCorrect;
128                         this.CompleteMatch = true;
129                         this.MatchNum = i;
130                         return;
131                 }
132         }
133 //Now check for the best alternative match
134         var PercentMatch = 0;
135         var BestMatch = -1;
136         for (i=0; i<this.Answers.length; i++){
137                 if ((this.Answers[i].PercentMatch > PercentMatch)&&(this.Answers[i].PercentCorrect == 100)){
138                         BestMatch = i;
139                         PercentMatch = this.Answers[i].PercentMatch;
140                 }
141         }
142         if (BestMatch > -1){
143                 this.Score = this.Answers[BestMatch].PercentMatch;
144                 this.Feedback = PartlyIncorrect + ' ';
145                 this.Feedback += '<span class="PartialAnswer">' + this.Answers[BestMatch].CorrectStart;
146                 this.Feedback += '<span class="Highlight">' + this.Answers[BestMatch].WrongMiddle + '</span>';
147                 this.Feedback += this.Answers[BestMatch].CorrectEnd + '</span>';
148                 this.Hint = '<span class="PartialAnswer">' + this.Answers[BestMatch].CorrectStart;
149                 this.Hint += '<span class="Highlight">' + this.Answers[BestMatch].Hint + '</span></span>';
150                 this.HintPenalty = this.Answers[BestMatch].HintPenalty;
151         }
152         else{
153                 this.Score = 0;
154                 this.Feedback = '';
155         }
158 CheckAnswerArray.prototype.GetBestMatch = CheckAnswerArray_GetBestMatch;
160 function CheckShortAnswer(QNum){
161 //bail if question doesn't exist or exercise finished
162         if ((State[QNum].length < 1)||(Finished == true)){return;}
164 //bail if question already complete
165         if (State[QNum][0] > -1){return;}
167 //Get the guess (TrimString added to fix bug for 6.0.4.3)
168         var G = TrimString(document.getElementById('Q_' + QNum + '_Guess').value);
170 //If no guess, bail with message; no penalty
171         if (G.length < 1){
172                 ShowMessage(PleaseEnter);
173                 return;
174         }
176 //Increment tries
177         State[QNum][2]++;
179 //Create a check object
180         var CA = new CheckAnswerArray(CaseSensitive);
182         CA.ClearAll();
183         for (var ANum=0; ANum<I[QNum][3].length; ANum++){
184                 CA.AddAnswer(G, I[QNum][3][ANum][0], I[QNum][3][ANum][3], I[QNum][3][ANum][1]);
185         }
186         CA.GetBestMatch();
188 //Store any match in the state tracking field
189         if (State[QNum][5].length > 0){State[QNum][5] += ' | ';}
190         if (CA.MatchNum > -1){
191                 State[QNum][5] += String.fromCharCode(65+CA.MatchNum);
192         }
193 //Else store the student's answer
194         else{
195                 State[QNum][5] += G;
196         }
198 //Add the percent correct value for this answer to the Q State (works for all
199 //situations, wrong or right)
200         State[QNum][3] += CA.Score;
202 //Now branch, based on the nature of the match
203 //Is it a complete match?
204         if (CA.CompleteMatch == true){
206 //Is it with a wrong answer, or a right answer?
207                 if (CA.Score == 100){
208 //It's right
209                         CalculateShortAnsQuestionScore(QNum);
210 //Get correct answer list if required, assuming there are any other correct alternatives
211                         if (ShowAlsoCorrect == true){
212                                 var AlsoCorrectList = GetCorrectList(QNum, G, false);
213                                 if (AlsoCorrectList.length > 0){
214                                         CA.Feedback += '<br />' + CorrectList + '<br />' + AlsoCorrectList;
215                                 }
216                         }
218 //Get the overall score and add it to the feedback
219                         if (ContinuousScoring == true){
220                                 CalculateOverallScore();
221                                 CA.Feedback += '<br />' + YourScoreIs + ' ' + Score + '%.';
222                                 WriteToInstructions(YourScoreIs + ' ' + Score + '%.');
223                         }
224                         ShowMessage(CA.Feedback);
225 //Put the answer in
226                         ReplaceGuessBox(QNum, G);
227                         CheckFinished();
228                         return;
229                 }
230         }
232 //Otherwise, it's a match to a predicted wrong/partially correct, or a partial
233 //match to a right answer
234         if (CA.Feedback.length < 1){CA.Feedback = DefaultWrong;}
235 //Remove any previous score unless exercise is finished (6.0.3.8+)
236         if (Finished == false){
237                 WriteToInstructions(strInstructions);
238         }
239         ShowMessage(CA.Feedback);
241 //If necessary, switch a hybrid question to m/c
242         if (State[QNum][2] >= HybridTries){
243                 SwitchHybridDisplay(QNum);
244         }
247 function CalculateShortAnsQuestionScore(QNum){
248         var Tries = State[QNum][2] + State[QNum][4]; //include tries and hint penalties;
249         var PercentCorrect = State[QNum][3];
250         var HintPenalties = State[QNum][4];
252 //Make sure it's not already complete
253         if (State[QNum][0] < 0){
254                 if (HintPenalties >= 1){
255                         State[QNum][0] = 0;
256                 }
257                 else{
258                         State[QNum][0] = (PercentCorrect/(100*Tries));
259                 }
260                 if (State[QNum][0] < 0){
261                         State[QNum][0] = 0;
262                 }
263         }
266 function SwitchHybridDisplay(QNum){
267         if (document.getElementById('Q_' + QNum + '_Hybrid_MC') != null){
268                 document.getElementById('Q_' + QNum + '_Hybrid_MC').style.display = '';
269                 if (document.getElementById('Q_' + QNum + '_SA') != null){
270                         document.getElementById('Q_' + QNum + '_SA').style.display = 'none';
271                 }
272         }
275 function GetCorrectArray(QNum){
276         var Result = new Array();
277         for (var ANum=0; ANum<I[QNum][3].length; ANum++){
278                 if (I[QNum][3][ANum][2] == 1){ //This is an acceptable correct answer
279                         Result.push(I[QNum][3][ANum][0]);
280                 }
281         }
282         return Result;
285 function GetCorrectList(QNum, Answer, IncludeAnswer){
286         var As = GetCorrectArray(QNum);
287         var Result = '';
288         for (var ANum=0; ANum<As.length; ANum++){
289                 if ((IncludeAnswer == true)||(As[ANum] != Answer)){
290                         Result += As[ANum] + '<br />';
291                 }
292         }
293         return Result;
296 function GetFirstCorrectAnswer(QNum){
297         var As = GetCorrectArray(QNum);
298         if (As.length > 0){
299                 return As[0];
300         }
301         else{
302                 return '';
303         }
306 function ReplaceGuessBox(QNum, Ans){
307         if (document.getElementById('Q_' + QNum + '_SA') != null){
308                 var El = document.getElementById('Q_' + QNum + '_SA');
309                 while (El.childNodes.length > 0){
310                         El.removeChild(El.childNodes[0]);
311                 }
312                 var A = document.createElement('span');
313                 A.setAttribute('class', 'Answer');
314                 var T = document.createTextNode(Ans);
315                 A.appendChild(T);
316                 El.appendChild(A);
317         }
320 [inclShowAnswer]
322 function ShowAnswers(QNum){
323 //bail if question doesn't exist or exercise finished
324         if ((State[QNum].length < 1)||(Finished == true)){return;}
326 //Get the answer list to display
327         var Ans = GetCorrectList(QNum, '', false);
328         Ans = CorrectList + '<br />' + Ans;
330 //Display feedback
331         ShowMessage(Ans);
333 //Set the score for this question to 0 if no
334         if (State[QNum][0] < 1){
335                 State[QNum][0] = 0;
336         }
338 //Get the first correct answer
339         var FirstAns = GetFirstCorrectAnswer(QNum);
341 //Replace the textbox
342         ReplaceGuessBox(QNum, FirstAns);
344 //Remove any current score
345         WriteToInstructions(strInstructions);
347 //This may be the last, so check finished status
348         CheckFinished();
351 [/inclShowAnswer]
353 [inclHint]
355 function ShowHint(QNum){
356 //bail if question doesn't exist or exercise finished
357         if ((State[QNum].length < 1)||(Finished == true)){return;}
359 //bail if question already complete
360         if (State[QNum][0] > -1){return;}
362 //Get the guess
363         var G = document.getElementById('Q_' + QNum + '_Guess').value;
365 //If no guess, give the first correct bit
366         if (G.length < 1){
367                 var Ans = GetFirstCorrectAnswer(QNum);
368                 var Hint = Ans.charAt(0);
369                 ShowMessage(NextCorrect + '<br />' + Hint);
370 //Penalty for hint
371                 State[QNum][4] += (1/Ans.length);
372                 return;
373         }
375 //Increment tries
376         State[QNum][2]++;
378 //Create a check object
379         var CA = new CheckAnswerArray(CaseSensitive);
381         CA.ClearAll();
382         for (var ANum=0; ANum<I[QNum][3].length; ANum++){
383 //Use only correct answers
384                 if (I[QNum][3][ANum][2] == 1){
385                         CA.AddAnswer(G, I[QNum][3][ANum][0], I[QNum][3][ANum][3], I[QNum][3][ANum][1]);
386                 }
387         }
388         CA.GetBestMatch();
389         if (CA.CompleteMatch == true){
390 //It's right!
391                 CheckShortAnswer(QNum);
392                 return;
393         }
394         else{
395                 if (CA.Hint.length > 0){
396                         ShowMessage(NextCorrect + '<br />' + CA.Hint);
397                         State[QNum][4] += CA.HintPenalty;
398                 }
399                 else{
400                         ShowMessage(DefaultWrong + '<br />' + NextCorrect + '<br />' + GetFirstCorrectAnswer(QNum).charAt(0));
401                 }
402         }
405 [/inclHint]