3 //JQUIZ-SPECIFIC SCORM-RELATED JAVASCRIPT CODE
5 function SetScormScore(){
6 //Reports the current score and any other information back to the LMS
8 API.LMSSetValue('cmi.core.score.raw', Score);
9 //Now send detailed reports about each item
10 for (var i=0; i<State.length; i++){
11 if (State[i] != null){
12 var ItemLabel = 'Item_' + (i+1).toString();
13 var ThisItemScore = '';
14 var ThisItemStatus = '';
15 API.LMSSetValue('cmi.objectives.' + i + '.id', 'obj'+ItemLabel);
16 API.LMSSetValue('cmi.interactions.' + i + '.id', 'int'+ItemLabel);
17 API.LMSSetValue('cmi.objectives.' + i + '.score.min', '0');
18 API.LMSSetValue('cmi.objectives.' + i + '.score.max', '100');
20 ThisItemScore = Math.floor(State[i][0] * 100) + '';
21 ThisItemStatus = 'completed';
25 ThisItemStatus = 'incomplete';
27 API.LMSSetValue('cmi.objectives.' + i + '.score.raw', ThisItemScore);
28 API.LMSSetValue('cmi.objectives.' + i + '.status', ThisItemStatus);
29 API.LMSSetValue('cmi.interactions.' + i + '.weighting', I[i][0]);
30 //We can only use the performance type, because we're storing multiple responses of various types.
31 API.LMSSetValue('cmi.interactions.' + i + '.type', 'performance');
32 API.LMSSetValue('cmi.interactions.' + i + '.student_response', State[i][5]);
41 //JQUIZ CORE JAVASCRIPT CODE
44 var CorrectIndicator = '[strCorrectIndicator]';
45 var IncorrectIndicator = '[strIncorrectIndicator]';
46 var YourScoreIs = '[strYourScoreIs]';
47 var ContinuousScoring = [boolContinuousScoring];
48 var CorrectFirstTime = '[strCorrectFirstTime]';
49 var ShowCorrectFirstTime = [boolShowCorrectFirstTime];
50 var ShuffleQs = [boolShuffleQs];
51 var ShuffleAs = [boolShuffleAs];
52 var DefaultRight = '[strDefaultRight]';
53 var DefaultWrong = '[strDefaultWrong]';
54 var QsToShow = [QsToShow];
58 var QArray = new Array();
59 var ShowingAllQuestions = false;
60 var ShowAllQuestionsCaption = '[strShowAllQuestionsCaptionJS]';
61 var ShowOneByOneCaption = '[strShowOneByOneCaptionJS]';
62 var State = new Array();
65 var strInstructions = '';
68 //The following variable can be used to add a message explaining that
69 //the question is finished, so no further marking will take place.
70 var strQuestionFinished = '';
72 function CompleteEmptyFeedback(){
74 for (QNum=0; QNum<I.length; QNum++){
75 //Only do this if not multi-select
76 if (I[QNum][2] != '3'){
77 for (ANum = 0; ANum<I[QNum][3].length; ANum++){
78 if (I[QNum][3][ANum][1].length < 1){
79 if (I[QNum][3][ANum][2] > 0){
80 I[QNum][3][ANum][1] = DefaultRight;
83 I[QNum][3][ANum][1] = DefaultWrong;
91 function SetUpQuestions(){
92 var AList = new Array();
93 var QList = new Array();
95 Qs = document.getElementById('Questions');
96 while (Qs.getElementsByTagName('li').length > 0){
97 QList.push(Qs.removeChild(Qs.getElementsByTagName('li')[0]));
100 if (QsToShow > QList.length){
101 QsToShow = QList.length;
103 while (QsToShow < QList.length){
104 DumpItem = Math.floor(QList.length*Math.random());
105 for (j=DumpItem; j<(QList.length-1); j++){
106 QList[j] = QList[j+1];
108 QList.length = QList.length-1;
110 if (ShuffleQs == true){
111 QList = Shuffle(QList);
113 if (ShuffleAs == true){
115 for (var i=0; i<QList.length; i++){
116 As = QList[i].getElementsByTagName('ol')[0];
119 while (As.getElementsByTagName('li').length > 0){
120 AList.push(As.removeChild(As.getElementsByTagName('li')[0]));
122 AList = Shuffle(AList);
123 for (j=0; j<AList.length; j++){
124 As.appendChild(AList[j]);
130 for (i=0; i<QList.length; i++){
131 Qs.appendChild(QList[i]);
132 QArray[QArray.length] = QList[i];
135 //Show the first item
136 QArray[0].style.display = '';
138 //Now hide all except the first item
139 for (i=1; i<QArray.length; i++){
140 QArray[i].style.display = 'none';
147 function SetFocusToTextbox(){
148 //if there's a textbox, set the focus in it
149 if (QArray[CurrQNum].getElementsByTagName('input')[0] != null){
150 QArray[CurrQNum].getElementsByTagName('input')[0].focus();
151 //and show a keypad if there is one
152 if (document.getElementById('CharacterKeypad') != null){
153 document.getElementById('CharacterKeypad').style.display = 'block';
157 if (QArray[CurrQNum].getElementsByTagName('textarea')[0] != null){
158 QArray[CurrQNum].getElementsByTagName('textarea')[0].focus();
159 //and show a keypad if there is one
160 if (document.getElementById('CharacterKeypad') != null){
161 document.getElementById('CharacterKeypad').style.display = 'block';
164 //This added for 6.0.4.11: hide accented character buttons if no textbox
166 if (document.getElementById('CharacterKeypad') != null){
167 document.getElementById('CharacterKeypad').style.display = 'none';
173 function ChangeQ(ChangeBy){
174 //The following line prevents moving to another question until the current
175 //question is answered correctly. Uncomment it to enable this behaviour.
176 // if (State[CurrQNum][0] == -1){return;}
177 if (((CurrQNum + ChangeBy) < 0)||((CurrQNum + ChangeBy) >= QArray.length)){return;}
178 QArray[CurrQNum].style.display = 'none';
179 CurrQNum += ChangeBy;
180 QArray[CurrQNum].style.display = '';
181 //Undocumented function added 10/12/2004
182 ShowSpecialReadingForQuestion();
187 var HiddenReadingShown = false;
188 function ShowSpecialReadingForQuestion(){
189 //Undocumented function for showing specific reading text elements which change with each question
190 //Added on 10/12/2004
191 if (document.getElementById('ReadingDiv') != null){
192 if (HiddenReadingShown == true){
193 document.getElementById('ReadingDiv').innerHTML = '';
195 if (QArray[CurrQNum] != null){
197 var Children = QArray[CurrQNum].getElementsByTagName('div');
198 for (var i=0; i<Children.length; i++){
199 if (Children[i].className=="HiddenReading"){
200 document.getElementById('ReadingDiv').innerHTML = Children[i].innerHTML;
201 HiddenReadingShown = true;
202 //Hide the ShowAllQuestions button to avoid confusion
203 if (document.getElementById('ShowMethodButton') != null){
204 document.getElementById('ShowMethodButton').style.display = 'none';
212 function SetQNumReadout(){
213 document.getElementById('QNumReadout').innerHTML = (CurrQNum+1) + ' / ' + QArray.length;
214 if ((CurrQNum+1) >= QArray.length){
215 if (document.getElementById('NextQButton') != null){
216 document.getElementById('NextQButton').style.visibility = 'hidden';
220 if (document.getElementById('NextQButton') != null){
221 document.getElementById('NextQButton').style.visibility = 'visible';
225 if (document.getElementById('PrevQButton') != null){
226 document.getElementById('PrevQButton').style.visibility = 'hidden';
230 if (document.getElementById('PrevQButton') != null){
231 document.getElementById('PrevQButton').style.visibility = 'visible';
239 RemoveBottomNavBarForIE();
241 //If there's only one question, no need for question navigation controls
243 document.getElementById('QNav').style.display = 'none';
246 //Stash the instructions so they can be redisplayed
247 strInstructions = document.getElementById('InstructionsDiv').innerHTML;
258 PreloadImages([PreloadImageList]);
261 CompleteEmptyFeedback();
268 setTimeout('StartTimer()', 50);
271 //Check search string for q parameter
272 if (document.location.search.length > 0){
273 if (ShuffleQs == false){
274 var JumpTo = parseInt(document.location.search.substring(1,document.location.search.length))-1;
275 if (JumpTo <= QsToShow){
280 //Undocumented function added 10/12/2004
281 ShowSpecialReadingForQuestion();
284 function ShowHideQuestions(){
285 FuncBtnOut(document.getElementById('ShowMethodButton'));
286 document.getElementById('ShowMethodButton').style.display = 'none';
287 if (ShowingAllQuestions == false){
288 for (var i=0; i<QArray.length; i++){
289 QArray[i].style.display = '';
291 document.getElementById('Questions').style.listStyleType = 'decimal';
292 document.getElementById('OneByOneReadout').style.display = 'none';
293 document.getElementById('ShowMethodButton').innerHTML = ShowOneByOneCaption;
294 ShowingAllQuestions = true;
297 for (var i=0; i<QArray.length; i++){
299 QArray[i].style.display = 'none';
302 document.getElementById('Questions').style.listStyleType = 'none';
303 document.getElementById('OneByOneReadout').style.display = '';
304 document.getElementById('ShowMethodButton').innerHTML = ShowAllQuestionsCaption;
305 ShowingAllQuestions = false;
307 document.getElementById('ShowMethodButton').style.display = 'inline';
310 function CreateStatusArray(){
312 //For each item in the item array
313 for (QNum=0; QNum<I.length; QNum++){
314 //Check if the question still exists (hasn't been nuked by showing a random selection)
315 if (document.getElementById('Q_' + QNum) != null){
316 State[QNum] = new Array();
317 State[QNum][0] = -1; //Score for this q; -1 shows question not done yet
318 State[QNum][1] = new Array(); //answers
319 for (ANum = 0; ANum<I[QNum][3].length; ANum++){
320 State[QNum][1][ANum] = 0; //answer not chosen yet; when chosen, will store its position in the series of choices
322 State[QNum][2] = 0; //tries at this q so far
323 State[QNum][3] = 0; //incrementing percent-correct values of selected answers
324 State[QNum][4] = 0; //penalties incurred for hints
325 State[QNum][5] = ''; //Sequence of answers chosen by number
335 function CheckMCAnswer(QNum, ANum, Btn){
336 //if question doesn't exist, bail
337 if (State[QNum].length < 1){return;}
340 Feedback = I[QNum][3][ANum][1];
342 //Now show feedback and bail if question already complete
343 if (State[QNum][0] > -1){
344 //Add an extra message explaining that the question
345 // is finished if defined by the user
346 if (strQuestionFinished.length > 0){Feedback += '<br />' + strQuestionFinished;}
348 ShowMessage(Feedback);
352 //Hide the button while processing
353 Btn.style.display = 'none';
355 //Increment the number of tries
358 //Add the percent-correct value of this answer
359 State[QNum][3] += I[QNum][3][ANum][3];
361 //Store the try number in the answer part of the State array, for tracking purposes
362 State[QNum][1][ANum] = State[QNum][2];
363 if (State[QNum][5].length > 0){State[QNum][5] += ' | ';}
364 State[QNum][5] += String.fromCharCode(65+ANum);
366 //Should this answer be accepted as correct?
367 if (I[QNum][3][ANum][2] < 1){
371 Btn.innerHTML = IncorrectIndicator;
373 //Remove any previous score unless exercise is finished (6.0.3.8+)
374 if (Finished == false){
375 WriteToInstructions(strInstructions);
378 //Check whether this leaves just one MC answer unselected, in which case the Q is terminated
379 var RemainingAnswer = FinalAnswer(QNum);
380 if (RemainingAnswer > -1){
381 //Behave as if the last answer had been selected, but give no credit for it
382 //Increment the number of tries
385 //Calculate the score for this question
386 CalculateMCQuestionScore(QNum);
388 //Get the overall score and add it to the feedback
389 CalculateOverallScore();
390 if ((ContinuousScoring == true)||(Finished == true)){
391 Feedback += '<br />' + YourScoreIs + ' ' + Score + '%.';
392 WriteToInstructions(YourScoreIs + ' ' + Score + '%.');
399 Btn.innerHTML = CorrectIndicator;
401 //Calculate the score for this question
402 CalculateMCQuestionScore(QNum);
404 //Get the overall score and add it to the feedback
405 if (ContinuousScoring == true){
406 CalculateOverallScore();
407 if ((ContinuousScoring == true)||(Finished == true)){
408 Feedback += '<br />' + YourScoreIs + ' ' + Score + '%.';
409 WriteToInstructions(YourScoreIs + ' ' + Score + '%.');
414 //Show the button again
415 Btn.style.display = 'inline';
417 //Finally, show the feedback
418 ShowMessage(Feedback);
420 //Check whether all questions are now done
424 function CalculateMCQuestionScore(QNum){
425 var Tries = State[QNum][2] + State[QNum][4]; //include tries and hint penalties
426 var PercentCorrect = State[QNum][3];
427 var TotAns = GetTotalMCAnswers(QNum);
428 var HintPenalties = State[QNum][4];
430 //Make sure it's not already complete
432 if (State[QNum][0] < 0){
434 if (HintPenalties >= 1){
438 //This line calculates the score for this question
443 State[QNum][0] = ((TotAns-((Tries*100)/State[QNum][3]))/(TotAns-1));
446 //Fix for Safari bug added for version 6.0.3.42 (negative infinity problem)
447 if ((State[QNum][0] < 0)||(State[QNum][0] == Number.NEGATIVE_INFINITY)){
453 function GetTotalMCAnswers(QNum){
455 for (var ANum=0; ANum<I[QNum][3].length; ANum++){
456 if (I[QNum][3][ANum][4] == 1){ //This is an MC answer
463 function FinalAnswer(QNum){
464 var UnchosenAnswers = 0;
465 var FinalAnswer = -1;
466 for (var ANum=0; ANum<I[QNum][3].length; ANum++){
467 if (I[QNum][3][ANum][4] == 1){ //This is an MC answer
468 if (State[QNum][1][ANum] < 1){ //This answer hasn't been chosen yet
474 if (UnchosenAnswers == 1){
486 function CheckMultiSelAnswer(QNum){
487 //bail if question doesn't exist or exercise finished
488 if ((State[QNum].length < 1)||(Finished == true)){return;}
490 //Increment the tries for this question
495 if (State[QNum][5].length > 0){State[QNum][5] += ' | ';}
497 //Check if there are any mismatches
500 for (var ANum=0; ANum<I[QNum][3].length; ANum++){
501 CheckBox = document.getElementById('Q_' + QNum + '_' + ANum + '_Chk');
502 if (CheckBox.checked == true){
503 State[QNum][5] += 'Y';
506 State[QNum][5] += 'N';
508 ShouldBeChecked = (I[QNum][3][ANum][2] == 1);
509 if (ShouldBeChecked == CheckBox.checked){
513 Feedback = I[QNum][3][ANum][1];
516 //Add the hit readout
517 Feedback = Matches + ' / ' + I[QNum][3].length + '<br />' + Feedback;
518 if (Matches == I[QNum][3].length){
520 CalculateMultiSelQuestionScore(QNum);
521 if (ContinuousScoring == true){
522 CalculateOverallScore();
523 if ((ContinuousScoring == true)||(Finished == true)){
524 Feedback += '<br />' + YourScoreIs + ' ' + Score + '%.';
525 WriteToInstructions(YourScoreIs + ' ' + Score + '%.');
530 //It's wrong -- Remove any previous score unless exercise is finished (6.0.3.8+)
531 if (Finished == false){
532 WriteToInstructions(strInstructions);
537 ShowMessage(Feedback);
539 //Check whether all questions are now done
543 function CalculateMultiSelQuestionScore(QNum){
544 var Tries = State[QNum][2];
545 var TotAns = State[QNum][1].length;
547 //Make sure it's not already complete
548 if (State[QNum][0] < 0){
549 State[QNum][0] = (TotAns - (Tries-1)) / TotAns;
550 if (State[QNum][0] < 0){
558 function CalculateOverallScore(){
559 var TotalWeighting = 0;
562 for (var QNum=0; QNum<State.length; QNum++){
563 if (State[QNum] != null){
564 if (State[QNum][0] > -1){
565 TotalWeighting += I[QNum][0];
566 TotalScore += (I[QNum][0] * State[QNum][0]);
570 if (TotalWeighting > 0){
571 Score = Math.floor((TotalScore/TotalWeighting)*100);
574 //if TotalWeighting is 0, no questions so far have any value, so
575 //no penalty should be shown.
580 function CheckFinished(){
583 for (var QNum=0; QNum<State.length; QNum++){
584 if (State[QNum] != null){
585 if (State[QNum][0] < 0){
590 if (AllDone == true){
592 //Report final score and submit if necessary
593 CalculateOverallScore();
594 FB = YourScoreIs + ' ' + Score + '%.';
595 if (ShowCorrectFirstTime == true){
597 for (QNum=0; QNum<State.length; QNum++){
598 if (State[QNum] != null){
599 if (State[QNum][0] >= 1){
604 FB += '<br />' + CorrectFirstTime + ' ' + CFT + '/' + QsToShow;
606 WriteToInstructions(FB);
610 window.clearInterval(Interval);
614 if (TimeOver == true){
626 setTimeout('SendResults(' + Score + ')', 50);
630 Detail = '<?xml version="1.0"?><hpnetresult><fields>';
631 for (QNum=0; QNum<State.length; QNum++){
632 if (State[QNum] != null){
633 if (State[QNum][5].length > 0){
634 Detail += '<field><fieldname>Question #' + (QNum+1) + '</fieldname><fieldtype>question-tracking</fieldtype><fieldlabel>Q ' + (QNum+1) + '</fieldlabel><fieldlabelid>QuestionTrackingField</fieldlabelid><fielddata>' + State[QNum][5] + '</fielddata></field>';
638 Detail += '</fields></hpnetresult>';
639 setTimeout('Finish()', SubmissionTimeout);
643 SetScormIncomplete();
650 document.getElementById('Timer').innerHTML = '[strTimesUp]';
656 ShowMessage('[strTimesUp]');
658 //Set all remaining scores to 0
659 for (var QNum=0; QNum<State.length; QNum++){
660 if (State[QNum] != null){
661 if (State[QNum][0] < 0){