2 // PLEASE NOTE that this version is more recent than the incorrectly
3 // numbered v6.1, dated 2003.11.17. From now on, version numbers will
4 // follow those of Hot Potatoes.
5 /* hot-potatoes.js (v6.0.4.0 - 2005.02.18)
6 * =======================================
7 * by Gordon Bateson, February 2003
8 * Copyright (c) 2003 Gordon Bateson. All Rights Reserved.
10 * You are hereby granted a royalty free license to use or modify this
11 * software provided that this copyright notice appears on all copies.
13 * This software is provided "AS IS" without a warranty of any kind.
15 * Documentation and downloads may be available from:
16 * http://www.kanazawa-gu.ac.jp/~gordon/research/hot-potatoes/
18 // This JavaScript library modifies the SendResults and StartUp functions
19 // used by hotpot v5 and v6, so that more (or less!) details about the
20 // student can be input, and more details of a quiz's questions and answers
21 // can be submitted to the server when the quiz is finished
22 // If the arrays below (Login, DB, JBC, ...) are set BEFORE calling this
23 // script, they will NOT be overwritten. Any array that is not set, will
24 // use the defaults below. This is useful if you want to use different
25 // settings for different quizzes.
29 if (window
.Login
==null) {
31 Login
[0] = true; // Show prompt for user name
32 // This can also be a string of user names ...
33 // Login[0] = "Guest,Peter,Paul,Mary,Webmaster";
34 // or an array of user names (and on-screen texts) (and passwords) ...
35 // Login[0] = new Array("Guest", "001,Peter,xxxx", "002,Paul,yyyy", "003,Mary,zzzz", "Webmaster");
36 // and can also be written as ...
37 // Login[0] = new Array(
38 // new Array("Guest"),
39 // new Array("001", "Peter", "xxxx"),
40 // new Array("002", "Paul", "yyyy"),
41 // new Array("003", "Mary", "zzzz"),
42 // new Array("Webmaster")
44 Login
[1] = true; // Show prompt for student's UserID
45 // If there is no password prompt (i.e. Logon[3] is false), this value
46 // will be checked against the password information, if any, in Login[0]
47 Login
[2] = false; // Show prompt for student's email
48 Login
[3] = false; // Show prompt for quiz password, and check this value against
49 // the password information, if any, in Login[0]
50 // This can also be a string required to start the quiz ...
51 // Login[3] = "password";
52 Login
[4] = true; // Show prompt for the cookie expiry date
53 // If false, cookies expire at the end of the current session
54 Login
[5] = "guest,webmaster"
55 // guest user names (case insensitive) ...
56 // Login[5] = "guest,webmaster";
57 // These users do NOT need to fill in other login fields
58 // and their quiz results are NOT added to the database
59 // the Login prompts and error messages
60 // are defined in the MSG array (see below)
63 // Database (for use with BFormMail)
65 if (window
.DB
==null) {
67 DB
[0] = true; // append form fields to database on server
68 // If you are NOT using BFormMail's database feature,
69 // set DB[0]=false, and you can then safely ignore DB[1 to 5]
70 DB
[1] = "/home/gordon/public_html/cgi/hot-potatoes-data";
71 // append_db folder path (no trailing slash)
72 // Can be either an absolute path e.g. "/home/gordon/public_html/cgi/hot-potatoes-data"
73 // or a relative (to CGI bin) path e.g. "hot-potatoes-data"
74 DB
[2] = "hot-potatoes";
75 // append_db file name (no extension)
76 // If left blank, the quiz file name, without extension, will be used
77 // i.e. each quiz will have its results stored in a different file.
78 // If filled in, this file will store the results for ALL quizzes.
79 // Database files and folders must be set up BEFORE running the quiz
80 // must have appropriate access privileges (on Unix, use "chmod 666").
81 DB
[3] = ""; // append_db extension (if left blank, ".txt" will be used)
82 DB
[4] = ""; // db_fields (if left blank, ALL quiz fields will be sent)
83 DB
[5] = ""; // db_delimiter (if left blank, tab will be used)
84 DB
[6] = "REMOTE_ADDR,HTTP_USER_AGENT";
85 // env_report ('REMOTE_ADDR','HTTP_USER_AGENT' and a few others)
86 // for a complete description of these fields are, see ...
87 // http://www.infosheet.com/stuff/BFormMail.readme
88 // Switches DB[7] and DB[8] force the settings in the ResultForm
89 // In v5 and v6 quizzes, these settings wil be override those in the original quiz
90 // If the quiz results are to be sent to an LMS (via the "store" form)
91 // then switches DB[7] and DB[8] are not used
92 DB
[7] = ''; // URL of form processing script
93 // e.g. http://www.kanazawa-gu.ac.jp/~gordon/cgi/bformmail.cgi
94 DB
[8] = ''; // email address to which results should be sent
95 // e.g. gordon@kanazawa-gu.ac.jp
97 // By default the quiz's question's scores will be returned.
98 // If you want more detailed information, set the flags below:
102 if (window
.JBC
==null) {
104 JBC
[0] = true; // show separator line between answers on email
105 JBC
[1] = true; // show number of attempts to answer question
106 JBC
[2] = true; // show question texts
107 JBC
[3] = true; // show right answer(s)
108 JBC
[4] = true; // show wrong answer(s)
109 JBC
[5] = true; // show ignored answer(s)
110 JBC
[6] = false; // show answer as text (false) or number (true)
112 // JBC quizzes use the global variables 'I' and 'Status'
113 // I : an array of JBC_QUESTIONs (one for each question)
115 // [0] : question text
116 // [1] : array of JBC_ANSWERs (one for each answer)
117 // [2] : single/multi flag
118 // 0 : single answer (using 'button')
119 // 1 : multiple answers (using 'checkbox')
122 // [1] : answer feedback
123 // [2] : correct answer flag
124 // 0 : this is NOT the correct answer
125 // 1 : this is the correct answer
126 // Status : an array of JBC_QUESTION_STATUSes
127 // JBC_QUESTION_STATUS:
128 // [0] : correctly answered yet flag
129 // 0 : this question has NOT been correctly answered
130 // 1 : this question has been correctly answered
131 // [1] : array of JBC_ANSWER_STATUSes (one for each answer)
132 // '0' : initial value
133 // 'R' : single answer question was answered 'R'ight
134 // 'W' : single answer question was answered 'W'rong
135 // 'C' : multiple answer question's checkbox was 'C'hecked
136 // 'U' : multiple answer question's checkbox was 'U'nchecked
137 // [2] : number of times this question has been wrongly answered
138 // [3] : score (out of 1) for this question (maybe undefined on HP<5.5)
139 // 0 : not correct yet
140 // 0<[3]<1 : correct but only after [2] wrong attempts
141 // 1 : correct first time (bravo!)
142 // N.B. score = (numberOfAnswers - numberofWrongTries) / numberOfAnswers
146 if (window
.JCloze
==null) {
147 JCloze
= new Array();
148 JCloze
[0] = true; // show separator line between answers on email
149 JCloze
[1] = true; // show student's correct answer
150 JCloze
[2] = true; // show other correct answer(s), if any
151 JCloze
[3] = true; // show wrong answer(s), if any (NOT available for v5)
152 JCloze
[4] = false; // show number of hints + checks (legacy field, replaced by [7]+[9])
153 JCloze
[5] = false; // show if clue was asked for or not (legacy field, replaced by [8])
154 JCloze
[6] = true; // show clue
155 JCloze
[7] = true; // show number of hints (=next letter requests)
156 JCloze
[8] = true; // show number of clues
157 JCloze
[9] = true; // show number of checks
159 // JCloze quizzes use the global variables 'I' and 'State'
160 // I : array of JCLOZE_ANSWERs
163 // [1] : array of JCLOZE_ANSWER_TEXTs
164 // [2] : clue for this answer
165 // JCLOZE_ANSWER_TEXT :
166 // [0] : array (seems unnecessary, just the text would be enough?)
167 // [0] : text of possible answer
168 // State : array of JCLOZE_ANSWER_STATEs
169 // JCLOZE_ANSWER_STATE (v5) :
170 // [0] : clue asked for or not
171 // [1] : number of hints (show next letter) and penalties ('check' an incorrect answer)
172 // [2] : length of answer matched
173 // [3] : score for this item
174 // [4] : already answered correctly
175 // [5] : answer entered in text box (right or not)
176 // JCLOZE_ANSWER_STATE (v6)
177 // this.ClueGiven = false;
178 // this.HintsAndChecks = 0;
179 // this.MatchedAnswerLength = 0;
180 // this.ItemScore = 0;
181 // this.AnsweredCorrectly = false;
182 // this.Guesses = new Array(); last guess is correct answer
186 if (window
.JCross
==null) {
187 JCross
= new Array();
188 JCross
[0] = true; // show separator line between answers on email
189 JCross
[1] = true; // show number of penalties (hints or checks before complete)
190 JCross
[2] = true; // show number of letters
191 JCross
[3] = true; // show correct answers
192 JCross
[4] = true; // show clues
193 JCross
[5] = true; // show wrong answers
194 JCross
[6] = true; // show if clue was asked for or not
195 JCross
[7] = true; // show number of hints (=next letter requests)
196 JCross
[8] = true; // show number of checks
197 // there are no "ignored" answers for JCross quizzes
199 // JCross quizzes use the following global variables:
200 // L : letters (of correct answers)
201 // C : clue numbers (CL in v6)
203 // 'L', 'C' ('CL') and 'G' are all 2-dimensional arrays (rows x cols)
205 // v5 quizzes additionally use the following single-dimension arrays
206 // A : clues for across (horizontal) words
207 // D : clues for down (vertical) words
208 // N.B. form is only sent when all answers are correct so
209 // you can't find out what 'wrong' answers were entered
213 if (window
.JMatch
==null) {
214 JMatch
= new Array();
215 JMatch
[0] = true; // show separator line between answers on email
216 JMatch
[1] = false; // show number of penalties (= total number of checks)
217 JMatch
[2] = true; // show LHS texts (the question)
218 JMatch
[3] = true; // show correct answers
219 JMatch
[4] = true; // show wrong answers
220 JMatch
[5] = true; // show checks (per match) [empty or unchanged RHS are not counted]
221 // JMatch has no "clue" or "hint" buttons
222 // there cannot be any "ignored" answers
224 // v5 JMatch quizzes use the global variables 'I' and 'Status' (and 'RItems')
225 // v6 JMatch quizzes use only 'Status'
226 // v6+ JMatch quizzes use 'F' and 'D' (see below)
227 // I : an array of JMATCH_PAIRs (one for each pair)
231 // [2] : fixed (=not jumbled) flag
234 // [3] : index in drop down list selection
235 // Status : an array of JMATCH_PAIR_STATUSes
236 // JMATCH_PAIR_STATUS:
237 // [0] : correctly matched yet flag
238 // 0 : this pair has NOT been correctly matched
239 // 1 : this pair has been correctly matched
240 // [1] : number of times this item has been wrongly matched
242 // [2] : id of original SELECT element containing possible matches
243 // Note that after matching, this SELECT is removed, so don't try looking for it :-)
244 // v6+ JMatch quizzes use the global variables 'F' and 'D'
245 // F : array of JMATCH_FIXED_ITEMs
246 // JMATCH_FIXED_ITEM:
249 // D : array of JMATCH_DRAGGABLE_ITEMs
250 // JMATCH_DRAGGABLE_ITEM
252 // [1] : tag of the F item to which it SHOULD be dragged
253 // [2] : tag of the F item to which it was dragged (initally 0)
254 // N.B. form is only sent when all answers are correct so
255 // you can't find out what 'wrong' answers were entered
259 if (window
.JMix
==null) {
261 JMix
[0] = true; // show separator line between answers on email
262 JMix
[1] = false; // show number of wrong guesses (replaced by JMix[5])
263 JMix
[2] = true; // show right answer
264 JMix
[3] = true; // show wrong answer, if any
265 JMix
[4] = false; // show answer as text (false) or number (true)
266 JMix
[5] = true; // show number of checks
267 JMix
[6] = true; // show number of hints (=show next word)
269 // JMix quizzes use the global variables
270 // 'Segments', 'GuessSequence' and 'Penalties'
271 // Segments : array of JMix_QUESTIONs
274 // [1] : order in sequence
276 // GuessSequence : array of 'order in sequence' numbers
277 // Penalties : number of incorrect guesses
281 if (window
.JQuiz
==null) {
283 JQuiz
[0] = true; // show separator line between answers on email
284 JQuiz
[1] = true; // show question text
285 JQuiz
[2] = true; // show student's correct answer(s)
286 JQuiz
[3] = false; // show wrong and ignored answer(s) (legacy field superceded by [8] & [9])
287 JQuiz
[4] = true; // show number of hints requested
288 JQuiz
[5] = false; // show number of checks of incorrect answers (legacy field superceded by [12])
289 // HP6 v6 quizzes only
290 JQuiz
[6] = false; // show answer value (false) or A,B,C... index (true)
291 JQuiz
[7] = false; // show all students answers
292 JQuiz
[8] = true; // show student's wrong answers
293 JQuiz
[9] = true; // show ignored answers (not relevant for multi-select questions)
294 JQuiz
[10] = true; // show score weightings
295 JQuiz
[11] = true; // show question type
296 JQuiz
[12] = true; // show number of checks (if true, then JQuiz[5] of will be ignored)
297 JQuiz
[13] = true; // show number of times ShowAnswer button was pressed (usually 0 or 1)
299 // v5 JQuiz quizzes use the global variables 'I' and 'Status'
300 // I : array of JQUIZ_ANSWERs
302 // [0] : question text
303 // [1] : array of JQUIZ_ANSWER_TEXTs (one for each answer)
304 // JQUIZ_ANSWER_TEXT :
305 // [0] : array (seems unnecessary, just the text would be enough?)
306 // [0] : text of possible answer
307 // Status : array of JQUIZ_ANSWER_STATEs
308 // JQUIZ_ANSWER_STATE :
309 // [0] : question done or not
310 // [1] : number of wrong checks
311 // [2] : number of hints asked for
312 // [3] : student's answer
313 // [4] : score for this question
314 // v6 JQuiz quizzes use the global variables 'I' and 'State'
315 // I : array of JQUIZ_QUESTIONs
318 // [1] : ?? (always set to '')
319 // [2] : question type
320 // '0'=multiple-choice, '1'=short-answer, '2'=hybrid, '3'=multi-select
321 // [3] : array of JQUIZ_ANSWERSs (one for each possible answer)
323 // [0] : answer value
324 // [1] : feedback text
325 // [2] : correct answer flag (1=a correct answer, 0=a wrong answer)
326 // [3] : weighted score (as percentage) if correct
327 // [4] : flag (usually set to 1, but for hybrid answers that are not
328 // to be included in multiple choice options, it is set to 0)
329 // State : array of JQUIZ_QUESTION_STATEs
330 // JQUIZ_QUESTION_STATE :
331 // [0] : score (-1 shows not done yet)
332 // [1] : array showing on which number try each JQUIZ_ANSWER was selected
333 // [2] : number of attempts at this question
334 // [3] : total of weighted scores of correct answers that were selected
335 // i.e. each time a correct answer is selected,
336 // its JQUIZ_ANSWER[3] weighting is added to this total
337 // so when all the correct answers have been selected, this will be 100
338 // [4] : penalties incurred for hints (score is set to zero if >= 1)
339 // [5] : - for multiple choice, short-answer and hybrid questions, this is a
340 // comma-delimited list showing order in which answers were chosen
341 // - for multi-select fields, this is bar-delimted ('|') list of settings
342 // showing whether each checkbox was selected ('Y') on not ('N') when the
343 // 'Check' button was clicked. The final item in the list will be the
344 // settings for the correct answer.
345 // N.B. JBC, JMatch(v5) and JQuiz(v5) all use global variables 'I' and 'Status'
346 // JBC : I[0].length==3 && !window.RItems
347 // JQuiz(v5) : I[0].length==2
348 // JMatch(v5) : I[0].length==4 && window.RItems
349 // N.B. JCloze(v5+6) and JQuiz(v6) both use global variables 'I' and 'State'
350 // JCloze (v5) : I[0].length==3 && State[0].Guesses==null
351 // JCloze (v6) : I[0].length==3 && State[0].Guesses!=null
352 // JQuiz (v6) : I[0].length==4
356 if (window
.Rhubarb
==null) {
357 Rhubarb
= new Array();
358 Rhubarb
[0] = true; // show correct words (so far)
359 Rhubarb
[1] = true; // show correct words as count (true) or list (false)
360 Rhubarb
[2] = true; // show wrong words
361 Rhubarb
[3] = false; // show wrong words as count (true) or list (false)
362 Rhubarb
[4] = false; // show ignored words (not implemented yet)
363 Rhubarb
[5] = true; // show hints
368 if (window
.Sequitur
==null) {
369 Sequitur
= new Array();
370 Sequitur
[0] = true; // show count of correct button clicks
371 Sequitur
[1] = true; // show count of wrong button clicks
376 if (window
.MSG
==null) {
385 MSG
[5] = 'Start the Quiz';
387 // Cookie menu options (only used if Login[4] is true)
388 MSG
[7] = 'keep for this session only';
389 MSG
[8] = 'keep for one day';
390 MSG
[9] = 'keep for one month';
391 MSG
[10] = 'do NOT keep cookies';
392 // Login error messages
393 MSG
[11] = 'Sorry, you were unable to login. Please try again later.';
394 MSG
[12] = 'Please fill in all the information.';
395 MSG
[13] = 'Incorrect Password. Please try again.';
396 MSG
[14] = 'Incorrect ID. Please try again.';
397 MSG
[15] = 'Email address does not appear to be valid.';
398 // day and month names (used in Start_Time and End_Time)
399 MSG
[16] = new Array('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
400 MSG
[17] = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
402 MSG
[18] = 'Please enable pop-up windows on your browser.';
403 // browser specific instuctions on how to enable popup windows
405 var s
= n
.userAgent
.toLowerCase();
406 if (n
.appName
=='Netscape' && s
.indexOf('gecko')>=0) {
408 MSG
[18] += '\n\n' + 'Edit->Preferences, ' + (s
.indexOf('mac')>=0 ? 'Advanced->Scripts & Plugins' : 'Privacy & Security->Popup Window Controls');
409 } else if (s
.indexOf('safari')>=0) {
411 MSG
[18] += '\n\n' + 'on Safari menu, uncheck "Block Pop-Up Windows"';
412 } else if (s
.indexOf('firebird')>=0) {
414 MSG
[18] += '\n\n' + 'Preferences->Web Features, uncheck "Block Pop-Up Windows"';
415 } else if (s
.indexOf('msie 6')>=0) {
417 MSG
[18] += '\n\n' + 'Tools->Pop-up Blocker->Turn Off Pop-up Blocker';
420 //if (window.FEEDBACK==null) {
421 // FEEDBACK = new Array();
422 // FEEDBACK[0] = ''; // url of feedback page/script
423 // FEEDBACK[1] = ''; // array of array('teachername', 'value');
424 // FEEDBACK[2] = ''; // 'student name' [formmail only]
425 // FEEDBACK[3] = ''; // 'email@somewhere.com>' [formmail only]
426 // FEEDBACK[4] = ''; // window width
427 // FEEDBACK[5] = ''; // window height
428 // FEEDBACK[6] = ''; // 'Send a message to teacher' [prompt/button text]
429 // FEEDBACK[7] = ''; // 'Title'
430 // FEEDBACK[8] = ''; // 'Teacher'
431 // FEEDBACK[8] = ''; // 'Message'
432 // FEEDBACK[10] = ''; // 'Close this window' [formmail only]
438 for (var i
=0; i
<=8; i
++) {
441 // indexes for the HP array (makes the code further down easier to read)
454 if (window
.ServerFields
==null) {
455 ServerFields
= new Array();
456 // these fields will be added to the ResultForm and submitted to the CGI script on the server.
457 // 'Sort', 'return_link_title', 'return_link_url' and 'print_blank_fields' are useful for formmail
458 // override the HP setting of sort fields (forces ALL fields to be displayed)
459 ServerFields
[0] = new Array('sort', '');
460 // add link to close pop-up results window
461 ServerFields
[1] = new Array('return_link_title', 'Close this window');
462 ServerFields
[2] = new Array('return_link_url', 'javascript:self.close()');
463 // make sure zero values are printed
464 ServerFields
[3] = new Array('print_blank_fields', 'yes');
465 // you can also set other fields for your customized CGI script
466 // e.g. adding a server defined start time (instead of a client defined start time)
467 // ServerFields[4] = new Array('serverStartTime', '<?php echo date("Y-m-d H:i:s") ?>');
469 // *********************
471 // (not required by LMS)
472 // *********************
473 function QuizLogin(LoginPrompt
) {
474 if (!is_LMS() && (Login
[0] || Login
[1] || Login
[2] || Login
[3])) {
478 + '<body bgColor="#cccccc" onLoad="opener.setFocus(self)">'
481 + 'self.expiry=null;'
483 if (Login
[4]) { // cookie expiry
484 html
+= "opener.checkOK(self,'CookieExpiry');";
486 if (Login
[0]) { // user name
487 html
+= "opener.checkOK(self,'UserName');";
489 if (Login
[1]) { // user ID
490 html
+= "opener.checkOK(self,'UserID');";
492 if (Login
[2]) { // user email
493 html
+= "opener.checkOK(self,'UserEmail');";
495 if (Login
[3]) { // quiz password
496 html
+= "opener.checkOK(self,'Password');";
499 + 'opener.StartQuiz();'
502 + 'if(isNaN(self.tries))self.tries=0;'
504 + 'if(self.tries<3){'
505 + 'opener.setFocus(self);'
507 + "alert(opener.MSG[11]);"
516 + '<caption>' + LoginPrompt
+ '</caption>';
518 if (Login
[0]) { // user name
519 var v
= getCookie(self
, 'UserName');
521 + '<th align=right nowrap>' + MSG
[0] + ' :</th>'
524 if (typeof(Login
[0])=='boolean') { // text box
525 html
+= '<input type=text name=UserName value="' + v
+ '">';
526 } else { // drop down menu of names
527 // pattern to match commas and white space
528 var comma
= (window
.RegExp
) ? new RegExp('\\s*,\\s*') : ',';
529 // convert list of names to array, if necessary
530 if (typeof(Login
[0])=='string') {
531 Login
[0] = Login
[0].split(comma
);
533 html
+= '<select name=UserName size=1>'
534 + '<option value=""></option>'
536 for(var i
=0; i
<Login
[0].length
; i
++) {
537 // convert name details to array if nececount_cary
538 if (typeof(Login
[0][i
])=='string') {
539 Login
[0][i
] = Login
[0][i
].split(comma
);
541 html
+= makeOption(Login
[0][i
][0], v
, Login
[0][i
][1]);
549 if (Login
[1]) { // user ID
550 var v
= getCookie(self
, 'UserID');
551 html
+= '<tr><th align=right nowrap>' + MSG
[1] + ' :</th><td><input type=text name=UserID value="' + v
+ '"></td></tr>';
553 if (Login
[2]) { // user email
554 var v
= getCookie(self
, 'UserEmail');
555 html
+= '<tr><th align=right nowrap>' + MSG
[2] +' :</th><td><input type=text name=UserEmail value="' + v
+ '"></td></tr>';
557 if (Login
[3]) { // quiz password
558 var v
= getCookie(self
, 'Password');
559 html
+= '<tr><th align=right nowrap>' + MSG
[3] + ' :</th><td><input type=password name=Password value="' + v
+ '"></td></tr>';
561 if (Login
[4]) { // cookie lifespan
562 var v
= getCookie(self
, 'CookieExpiry');
564 + '<th align=right nowrap>' + MSG
[4] + ' :</th>'
566 + '<select name="CookieExpiry" size=1>'
567 + makeOption('session', v
, MSG
[7])
568 + makeOption('day', v
, MSG
[8])
569 + makeOption('month', v
, MSG
[9])
570 + makeOption('never', v
, MSG
[10])
579 + '<input type=submit value="' + MSG
[5] + '"> '
580 + '<input type=button value="' + MSG
[6] + '" onClick="opener.goBack();self.close();">'
583 + '</table></form></body></html>'
585 // set height of Login Window
586 var m
= navigator
.userAgent
.indexOf('Mac')>=0;
587 var h
= (m
? 80 : 100);
588 for (var i
=0; i
<5; i
++) h
+= (Login
[i
] ? (m
? 20 : 25) : 0);
589 // open up a new window
590 if (!openWindow('', '', (m
? 320 : 300), h
, 'RESIZABLE', html
)) {
591 alert(MSG
[18]); // unable to open popup window
593 } else { // no Login required
594 window
.UserName
= window
.UserID
= window
.UserEmail
= window
.Password
= '';
599 function makeOption(value
, v
, txt
) {
600 return '<option value="' + value
+ '"' + (value
==v
? ' SELECTED' : '') + '>' + (txt
? txt
: value
) + '</option>';
602 function setFocus(w
) {
603 w
.focus(); // bring window to the front
604 var obj
= w
.document
.forms
[0].elements
;
605 for(var i
=0; i
<obj
.length
; i
++) {
606 var v
= getValue(w
, i
);
607 if (v
=='' || obj
[i
].type
=='submit') {
613 function checkOK(w
, n
){
614 var v
= getValue(w
, n
, true);
615 if (v
|| (n
!='UserName' && isGuest())) {
616 if (n
=='CookieExpiry') setCookieExpiry(w
, v
);
617 setCookie(self
, n
, v
, w
.expiry
);
618 if (n
!='CookieExpiry') eval('self.' + n
+ '=v');
620 if (w
.ok
) alert(MSG
[12]);
624 function getValue(w
, n
, flag
) {
625 var obj
= w
.document
.forms
[0].elements
[n
];
626 var TYPE
= obj
.type
.toUpperCase(); // required for ns4 (win)
627 if (obj
.options
&& TYPE
.indexOf('SELECT')>=0){
628 var v
= obj
.options
[obj
.selectedIndex
].value
;
634 if (n
=='Password' || (n
=='UserID' && !Login
[3])) {
635 var pwd
= getPassword(w
);
636 if (pwd
&& v
!=pwd
) msg
= MSG
[n
=='Password' ? 13 : 14];
638 if (n
=='UserEmail' && window
.RegExp
) {
640 r
= r
+ '(\\.' + r
+ ')';
641 r
= new RegExp('^(' + r
+ '*)@(' + r
+ '+)$');
642 if (v
.match(r
)==null) msg
= MSG
[15];
646 if (w
.ok
) alert(msg
);
652 function getPassword(w
) {
654 if (Login
[3] && typeof(Login
[3])=='string') {
656 } else if ((Login
[3] || Login
[1]) && typeof(Login
[0])=='object') {
657 var username
= getValue(w
, 'UserName');
658 for(var i
=0; i
<Login
[0].length
; i
++) {
659 if (username
==Login
[0][i
][0]) {
660 pwd
= Login
[0][i
][2];
667 function setCookieExpiry(w
, v
) {
669 w
.expiry
= new Date('Thu, 01-Jan-70 00:00:01 GMT');
670 } else if (v
=='day' || v
=='month') {
671 var ms
= (v
=='month' ? 31 : 1) * 60 * 60 * 24 * 1000;
672 w
.expiry
= new Date((new Date()).getTime() + ms
);
675 function setCookie(w
, name
, value
, expires
, path
, domain
, secure
) {
676 if (name
) w
.document
.cookie
= ''
677 + 'HP_' + name
+ "=" + escape(value
)
678 + (expires
? "; expires=" + expires
.toGMTString() : "")
679 + (path
? "; path=" + path
: "")
680 + (domain
? "; domain=" + domain
: "")
681 + (secure
? "; secure" : "")
684 function getCookie(w
, n
) {
685 var c
= w
.document
.cookie
;
686 var i
= c
.indexOf('HP_' + n
+ '=');
687 var j
= (i
<0) ? -1 : c
.indexOf(';', (i
+= n
.length
+ 4));
688 return (i
<0) ? '' : unescape(c
.substring(i
, ((j
<0) ? c
.length
: j
)));
691 if (w
==null) w
= self
; // default
692 if (w
.history
.length
) w
.history
.back();
694 function openWindow(url
, name
, width
, height
, attributes
, html
) {
695 // set height, width and attributes
696 if (window
.screen
&& width
&& height
) {
697 var W
= screen
.availWidth
;
698 var H
= screen
.availHeight
;
699 width
= Math
.min(width
, W
);
700 height
= Math
.min(height
, H
);
702 + (attributes
? (attributes
+',') : '')
703 + 'WIDTH='+width
+',HEIGHT='+height
706 // create global hpWindows object, if necessary
707 if (!window
.hpWindows
) window
.hpWindows
= new Array();
708 // initialize window object
710 // has a window with this name been opened before?
711 if (name
&& hpWindows
[name
]) {
712 // http://www.webreference.com/js/tutorial1/exist.html
713 if (hpWindows
[name
].open
&& !hpWindows
[name
].closed
) {
717 hpWindows
[name
] = null;
720 // check window is not already open
722 // workaround for "Access is denied" errors in IE when offline
723 // based on an idea seen at http://www.devshed.com/Client_Side/JavaScript/Mini_FAQ
724 var ie_offline
= (document
.all
&& location
.protocol
=='file:');
725 // try and open the new window
726 w
= window
.open((ie_offline
? '' : url
), name
, attributes
);
727 // check window opened OK (user may have prevented popups)
730 if (window
.screen
&& width
&& height
) {
731 w
.moveTo((W
-width
)/2, (H-height)/2);
733 // add content, if required
741 } else if (url
&& ie_offline
) {
744 if (name
) hpWindows
[name
] = w
;
749 // *********************
750 // Send results by email
751 // (not required by LMS)
752 // *********************
753 function SendAllResults(Score
) {
754 // check this quiz is not generated by a LMS
756 // add flat file database details to the results form
757 AddDatabaseDetailsToResultForm();
758 // add student details to the results form
759 AddStudentDetailsToResultForm();
760 // add question details to the results form
761 AddQuestionDetailsToResultForm();
762 // add server fields, if any, to results form
763 AddServerFieldsToResultForm();
764 // change "method" of form, because "get" only allows 512 byts of data
765 ResultForm
= replaceLast('method="get"', 'method="post"', ResultForm
);
766 // create results window and form
767 var w
= openWindow('', '', 500, 400, 'RESIZABLE,SCROLLBARS,LOCATION', ResultForm
);
768 // check window opened OK (user may have prevented popups)
770 // get shortcut to form object
771 var form
= w
.document
.forms
[0];
772 // update some important field values
773 form
.Score
.value
= Score
+ '%';
774 form
.realname
.value
= UserName
;
775 form
.Start_Time
.value
= getTime(Start_Time
);
776 form
.End_Time
.value
= getTime();
777 // force email subject and Exercise title
778 form
.subject
.value
= document
.title
;
779 form
. Exercise
.value
= document
.title
;
780 // update DB fields, if required
781 if (DB
[0] && !isGuest()) set_db_fields(form
);
782 if (DB
[7]) form
.action
= DB
[7];
783 if (DB
[8]) form
.recipient
.value
= DB
[8];
784 // if this is a Netscape browser, check if the referer will be set OK
785 if (navigator
.appName
=='Netscape' && (location
.protocol
=='file:' || navigator
.userAgent
.indexOf('Netscape6')>=0)) {
786 // ns4 and ns7 set referer to 'file:// ...' when running a quiz offline
787 // ns6.2 (at least) always sets referer to 'about:blank'
788 // Netscape's setting of referer can cause BFormMail
789 // to reject the form, so encode the form data as a URL
790 var url
= form
.action
;
791 var obj
= form
.elements
;
792 for (var i
=0; i
<obj
.length
; i
++) {
793 var v
= escape(obj
[i
].value
);
794 v
= v
.replace((new RegExp('\\+', 'g')), '%2B');
795 url
+= (i
==0 ? '?' : '&') + obj
[i
].name
+ '=' + v
;
797 w
.location
.href
= url
;
798 } else { // browser can POST form ok
801 } else { // unable to open popup window
807 // check username is not a "guest" user
809 var n
= getCookie(self
, 'UserName').toLowerCase();
811 // convert list of user names to array, if necessary
812 if(typeof(Login
[5])=='string') {
813 Login
[5] = Login
[5].split(',');
815 for(var i
=0; i
<Login
[5].length
; i
++) {
816 if (n
==Login
[5][i
].toLowerCase()) {
824 function set_db_fields(form
) {
825 // update list of DB fields, if required
826 if (DB
[4]=='' && window
.RegExp
) {
827 // add administration fields
830 + (Login
[1] ? ',ID' : '')
831 + (Login
[2] ? ',email' : '')
832 + (Login
[3] ? ',password' : '')
833 + ',Score,Start_Time,End_Time'
835 // add answer fields (except separators)
836 var r
= new RegExp('^[^_]+_q\\d\\d_\\w+$');
837 for(var i
=0; i
<form
.elements
.length
; i
++) {
838 var n
= form
.elements
[i
].name
;
839 if (r
.test(n
)) db_fields
+= ',' + n
;
841 form
.db_fields
.value
= db_fields
;
843 // make sure delimiter is set (NS6+ requires this be set here, not any earlier)
844 form
.db_delimiter
.value
= (DB
[5] ? DB
[5] : '\t');
846 function AddStudentDetailsToResultForm() {
848 if (Login
[0]) { // user name
849 // use 'realname' instead of a separate 'Name' field
850 // sDetails += hpHiddenField('Name', window.UserName);
852 if (Login
[1]) { // user ID
853 sDetails
+= hpHiddenField('ID', window
.UserID
);
855 if (Login
[2]) { // user email
856 sDetails
+= hpHiddenField('email', window
.UserEmail
);
858 if (sDetails
&& window
.RegExp
) {
859 // insert sDetails before '<input...Score...></input>'
860 var r
= new RegExp('<input[^>]*Score[^>]*><\\/input>', 'i');
861 var m
= r
.exec(ResultForm
);
863 ResultForm
= ResultForm
.replace(m
[0], sDetails
+ m
[0] + makeSeparator('Time_'));
867 if (Login
[3]) { // quiz password
868 sDetails
+= hpHiddenField('Password', window
.Password
);
869 ResultForm
= replaceLast('</form>', sDetails
+ '</form>', ResultForm
);
872 function AddQuestionDetailsToResultForm() {
873 var qDetails
= GetQuestionDetails();
875 // insert qDetails before the final </form> tag in the ResultForm
876 ResultForm
= replaceLast('</form>', qDetails
+ '</form>', ResultForm
);
879 function AddDatabaseDetailsToResultForm() {
880 if (window
.DB
&& DB
[0] && !isGuest()) {
883 if (folder
&& folder
.charAt(folder
.length
-1)!='/') folder
+= '/';
886 file
= location
.href
;
887 file
= file
.substring(file
.lastIndexOf('/')+1);
888 var i
= file
.indexOf('?');
889 if (i
>= 0) file
= file
.substring(0, i
);
890 var i
= file
.lastIndexOf('.');
891 if (i
>= 0) file
= file
.substring(0, i
);
893 var ext
= (DB
[3] ? DB
[3] : 'txt');
894 if (ext
.charAt(0)!='.') ext
= '.' + ext
;
895 dbDetails
+= hpHiddenField('append_db', folder
+ file
+ ext
);
896 dbDetails
+= hpHiddenField('db_fields', DB
[4]);
897 dbDetails
+= hpHiddenField('db_delimiter', ''); // NS6+ requires this be set later
898 if (DB
[6]) dbDetails
+= hpHiddenField('env_report', DB
[6]);
899 // insert dbDetails before the final </form> tag in the ResultForm
900 ResultForm
= replaceLast('</form>', dbDetails
+ '</form>', ResultForm
);
903 function AddServerFieldsToResultForm() {
904 if (window
.ServerFields
) {
905 var s
= ''; // input tags for s(erver fields)
906 for (var i
=0; i
<ServerFields
.length
; i
++) {
907 if (ServerFields
[i
][0] && window
.RegExp
) {
908 // remove previous field value, if any
909 var r
= new RegExp('<input[^>]*name\\s*=\\s*["\']\\s*' + ServerFields
[i
][0] + '[^>]*>(\\s*<\\/input>)?', 'i');
910 if (r
.test(ResultForm
)) {
911 ResultForm
= ResultForm
.replace(r
, '');
914 if (ServerFields
[i
][1]) {
915 s
+= hpHiddenField(ServerFields
[i
][0], ServerFields
[i
][1]);
918 if (s
) ResultForm
= replaceLast('</form>', s
+ '</form>', ResultForm
);
921 function replaceLast(a
, b
, c
) {
922 // replace last occurrence of 'a' in 'c' with 'b'
924 var i
= c
.lastIndexOf(a
);
925 return (i
<0 || l
==0) ? c
: (c
.substring(0, i
) + b
+ c
.substring(i
+l
));
927 // *************************
928 // Extract question details
929 // *************************
930 function GetQuestionDetails() {
931 var hp
= hpVersion();
932 var t
= hpQuizType();
933 var v
= hpQuizVersion();
934 return (t
==1) ? GetJbcQuestionDetails(hp
, v
) :
935 (t
==2) ? GetJClozeQuestionDetails(hp
, v
) :
936 (t
==3) ? GetJCrossQuestionDetails(hp
, v
) :
937 (t
==4) ? GetJMatchQuestionDetails(hp
, v
) :
938 (t
==5) ? GetJMixQuestionDetails(hp
, v
) :
939 (t
==6) ? GetJQuizQuestionDetails(hp
, v
) :
940 (t
==7) ? GetRhubarbDetails(hp
, v
) :
941 (t
==8) ? GetSequiturDetails(hp
, v
) : '';
943 function GetJbcQuestionDetails(hp
, v
) {
945 // check the quiz version
946 if (hp
==5 || hp
==6) {
947 // get question details
948 for(var q
=0; q
<I
.length
; q
++) {
949 // initialize strings to hold answer details
950 var aDetails
= new Array();
951 aDetails
[0] = new Array(); // right
952 aDetails
[1] = new Array(); // wrong
953 aDetails
[2] = new Array(); // ignored
954 // get answer details
955 for(var a
=0; a
<I
[q
][1].length
; a
++) {
956 var i
= (Status
[q
][1][a
]=='R') ? 0 : (Status
[q
][1][a
]=='0') ? 2 : 1;
957 aDetails
[i
][aDetails
[i
].length
] = (JBC
[6] ? a
: I
[q
][1][a
][0]);
959 // format 'Q' (a padded, two-digit version of 'q')
960 var Q
= getQ('JBC', q
);
961 // add separator, if required
962 if (JBC
[0]) qDetails
+= makeSeparator(Q
);
963 if (JBC
[1]) { // number of attempts to answer question
964 qDetails
+= hpHiddenField(Q
+'attempts', Status
[q
][2] + (Status
[q
][0]==1 ? 1 : 0));
966 if (JBC
[2]) { // question text
967 qDetails
+= hpHiddenField(Q
+'text', I
[q
][0]);
969 if (JBC
[3] && (DB
[0] || aDetails
[0].length
>0)) { // right
970 qDetails
+= hpHiddenField(Q
+'right', aDetails
[0]);
972 if (JBC
[4] && (DB
[0] || aDetails
[1].length
>0)) { // wrong
973 qDetails
+= hpHiddenField(Q
+'wrong', aDetails
[1]);
975 if (JBC
[5] && (DB
[0] || aDetails
[2].length
>0)) { // ignored
976 qDetails
+= hpHiddenField(Q
+'ignored', aDetails
[2]);
978 // calculate score for this question, if required (for HP version < 5.5)
979 if (isNaN(Status
[q
][3])) {
980 var a1
= Status
[q
][1].length
; // answers
981 var a2
= Status
[q
][2]; // attempts
982 Status
[q
][3] = (a1
<1 || a1
<(a2
-1)) ? 0 : ((a1
- (a2
-1)) / a1
);
984 // add 'score' for this question
985 qDetails
+= hpHiddenField(Q
+'score', Math
.floor(Status
[q
][3]*100)+'%');
990 function GetJClozeQuestionDetails(hp
, v
) {
992 // check the quiz version
993 if (hp
==5 || hp
==6) {
994 var r
= hpRottmeier();
995 if (parseInt(r
)==2) { // Rottmeier Find-It 3a+3b
996 qDetails
+= hpHiddenField('JCloze_penalties', window
.TotWrongChoices
);
998 // get details for each question
999 var q_max
= (r
==0) ? State
.length
: GapList
.length
; // could use I.length for both
1000 for (var q
=0; q
<q_max
; q
++) {
1001 // format 'Q' (a padded, two-digit version of 'q')
1002 var Q
= getQ('JCloze', q
);
1003 // add separator, if required
1004 if (JCloze
[0]) qDetails
+= makeSeparator(Q
);
1006 var x
= (hp
==5) ? State
[q
][3] : (r
==0) ? State
[q
].ItemScore
: GapList
[q
][1].Score
;
1007 qDetails
+= hpHiddenField(Q
+'score', Math
.floor(x
*100)+'%');
1008 var correct
= (HP
[_correct
][q
] ? HP
[_correct
][q
] : '');
1009 if (JCloze
[1]) { // student's correct answer
1010 qDetails
+= hpHiddenField(Q
+'correct', correct
);
1012 if (JCloze
[2]) { // ignored answers
1013 var x
= new Array();
1014 if (r
!=2.1) { // exclude Find-It 3a
1015 for (var i
=0, ii
=0; i
<I
[q
][1].length
; i
++) {
1016 var s
= I
[q
][1][i
][0];
1017 if (typeof(s
)=='string' && s
!='') {
1018 if (s
.toUpperCase() == correct
.toUpperCase()) {
1019 var is_ignored
= false;
1022 var is_ignored
= true;
1023 var iii_max
= HP
[_wrong
][q
] ? HP
[_wrong
][q
].length
: 0;
1024 for (var iii
=0; iii
<iii_max
; iii
++) {
1025 if (s
.toUpperCase() == HP
[_wrong
][q
][iii
].toUpperCase()) {
1026 var is_ignored
= false;
1036 qDetails
+= hpHiddenField(Q
+'ignored', x
);
1039 var x
= (HP
[_wrong
][q
] ? HP
[_wrong
][q
] : '');
1040 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1042 if (JCloze
[4]) { // number of penalties (Hints + Checks)
1043 var x
= (hp
==5) ? State
[q
][1] : (r
==0) ? State
[q
].HintsAndChecks
: (r
==1) ? GapList
[q
][1].NumOfTrials
: (r
==2.2) ? GapList
[q
][1].HintsAndChecks
: 0;
1044 qDetails
+= hpHiddenField(Q
+'penalties', x
);
1046 if (JCloze
[5]) { // clue shown?
1047 var x
= (hp
==5) ? State
[q
][0] : (r
==0) ? State
[q
].ClueGiven
: (r
==1) ? GapList
[q
][1].ClueAskedFor
: false;
1048 qDetails
+= hpHiddenField(Q
+'clue_shown', (x
? 'YES' : 'NO'));
1050 if (JCloze
[6]) { // clue text
1051 qDetails
+= hpHiddenField(Q
+'clue_text', I
[q
][2]);
1053 if (JCloze
[7]) { // number of hints
1054 var x
= (HP
[_hints
][q
] ? HP
[_hints
][q
] : 0);
1055 qDetails
+= hpHiddenField(Q
+'hints', x
);
1057 if (JCloze
[8]) { // number of clues
1058 var x
= HP
[_clues
][q
] ? HP
[_clues
][q
] : 0;
1059 qDetails
+= hpHiddenField(Q
+'clues', x
);
1061 if (JCloze
[9]) { // number of checks (including the final one for the correct answer)
1062 var x
= (HP
[_checks
][q
] ? HP
[_checks
][q
] : 0);
1063 qDetails
+= hpHiddenField(Q
+'checks', x
);
1069 function GetJCrossQuestionDetails(hp
, v
) {
1071 // check the quiz version
1072 if (hp
==5 || hp
==6) {
1073 // inialize letter count
1075 // get details for each question
1076 for (var row
=0; row
<L
.length
; row
++) {
1077 for (var col
=0; col
<L
[row
].length
; col
++) {
1078 // increment letter count, if required
1079 if (L
[row
][col
]) letters
++;
1080 // show answers and clues, if required
1081 var q
= (hp
==5) ? C
[row
][col
] : CL
[row
][col
];
1083 for (var i
=0; i
<2; i
++) { // 0==across, 1==down
1084 var AD
= (i
==0) ? 'A' : 'D';
1085 var acrossdown
= (i
==0) ? 'across' : 'down';
1087 var clue
= (hp
==5) ? eval(AD
+'['+q
+']') : GetJCrossClue('Clue_'+AD
+'_'+q
);
1089 // format 'Q' (a padded, two-digit version of 'q')
1090 var Q
= getQ('JCross', q
) + acrossdown
+ '_'; // e.g. JCross_01_across_
1093 qDetails
+= makeSeparator(Q
);
1096 var x
= (HP
[_correct
][AD
] && HP
[_correct
][AD
][q
]) ? HP
[_correct
][AD
][q
] : '';
1097 qDetails
+= hpHiddenField(Q
+'correct', x
);
1099 if (JCross
[4]) qDetails
+= hpHiddenField(Q
+'clue', clue
);
1101 var x
= (HP
[_wrong
][AD
] && HP
[_wrong
][AD
][q
]) ? HP
[_wrong
][AD
][q
] : '';
1102 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1105 var x
= HP
[_clues
][q
] ? HP
[_clues
][q
] : 0;
1106 qDetails
+= hpHiddenField(Q
+'clues', x
);
1109 var x
= (HP
[_hints
][AD
] && HP
[_hints
][AD
][q
]) ? HP
[_hints
][AD
][q
] : 0;
1110 qDetails
+= hpHiddenField(Q
+'hints', x
);
1113 var x
= (HP
[_checks
][AD
] && HP
[_checks
][AD
][q
]) ? HP
[_checks
][AD
][q
] : '';
1114 qDetails
+= hpHiddenField(Q
+'checks', x
);
1121 if (JCross
[2]) { // show number of letters
1122 qDetails
= hpHiddenField('JCross_letters', letters
) + qDetails
;
1124 if (JCross
[1]) { // show penalties
1125 var x
= (window
.Penalties
) ? Penalties
: 0;
1126 qDetails
= hpHiddenField('JCross_penalties', x
) + qDetails
;
1131 function GetJCrossClue(id
) {
1132 var obj
= (document
.getElementById
) ? document
.getElementById(id
) : null;
1133 return (obj
) ? GetTextFromNodeN(obj
, 'Clue') : '';
1135 function GetJCrossWord(a
, r
, c
, goDown
) {
1136 // a is a 2-dimensional array of letters, r is a row number, c is a column number
1138 while (r
<a
.length
&& c
<a
[r
].length
&& a
[r
][c
]) {
1148 function GetJMatchText(q
, className
) {
1149 var obj
= (document
.getElementById
) ? document
.getElementById('Questions') : null;
1150 return (obj
) ? GetTextFromNodeN(obj
, className
, q
) : '';
1152 function GetJMatchRHS(v
, q
, getCorrect
) {
1154 if (v
==5.1 || v
==6.1) { // Drag-and-drop
1155 var max_i
= (window
.F
&& window
.D
) ? D
.length
: 0;
1156 for (var i
=0; i
<max_i
; i
++) {
1157 if (F
[q
][1]==D
[i
][getCorrect
? 1 : 2]) break;
1159 if (i
<max_i
) rhs
= D
[i
][0];
1160 } else if (v
==5 || v
==6) { // drop-down list of options
1161 var obj
=document
.getElementById(Status
[q
][2]);
1162 if (obj
) { // not correct yet
1164 var k
= GetKeyFromSelect(obj
);
1165 var i_max
= obj
.options
.length
;
1166 for (var i
=0; i
<i_max
; i
++) {
1167 if (obj
.options
[i
].value
==k
) break;
1169 if (i
>=i_max
) i
= 0; // shouldn't happen
1171 // get current guess, if any
1172 var i
= obj
.selectedIndex
;
1174 if (i
) rhs
= obj
.options
[i
].innerHTML
;
1176 rhs
= GetJMatchText(q
, 'RightItem');
1181 function GetJMixQuestionDetails(hp
, v
) {
1183 // check the quiz version
1184 if (hp
==5 || hp
==6) {
1185 var q
= 0; // question number
1186 // format 'Q' (a padded, two-digit version of 'q')
1187 var Q
= getQ('JMix', q
);
1188 // add separator, if required
1189 if (JMix
[0]) qDetails
+= makeSeparator(Q
);
1190 // add 'score' for this question
1191 var score
= HP
[_correct
]==null ? 0 : ((Segments
.length
-Penalties
)/Segments
.length
);
1192 qDetails
+= hpHiddenField(Q
+'score', Math
.floor(score
*100)+'%');
1193 if (JMix
[1]) { // number of wrong guesses
1194 qDetails
+= hpHiddenField(Q
+'wrongGuesses', Penalties
);
1196 if (JMix
[2]) { // right answer
1197 var x
= (HP
[_correct
][q
]) ? HP
[_correct
][q
] : '';
1198 qDetails
+= hpHiddenField(Q
+'correct', x
);
1200 if (JMix
[3]) { // wrong answer(s)
1201 var x
= (HP
[_wrong
][q
]) ? HP
[_wrong
][q
] : '';
1202 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1204 if (JMix
[5]) { // checks
1205 var x
= (HP
[_checks
][q
]) ? HP
[_checks
][q
] : 0;
1206 qDetails
+= hpHiddenField(Q
+'checks', x
);
1208 if (JMix
[6]) { // hints
1209 var x
= (HP
[_hints
][q
]) ? HP
[_hints
][q
] : 0;
1210 qDetails
+= hpHiddenField(Q
+'hints', x
);
1215 function GetJMixSequence(indexes
) {
1216 var s
= new Array();
1217 for (var i
=0; i
<indexes
.length
; i
++) {
1218 s
[i
] = JMix
[4] ? indexes
[i
] : GetJMixSegmentText(indexes
[i
]);
1222 function GetJMixSegmentText(index
){
1223 var i_max
= Segments
.length
;
1224 for (var i
=0; i
<i_max
; i
++) {
1225 if (Segments
[i
][1] == index
) break;
1227 return (i
<i_max
) ? Segments
[i
][0] : '';
1229 function GetJQuizQuestionDetails(hp
, v
) {
1231 // HP5.5 uses "Status" for v5 and v6 JMatch quizzes (HP6 uses "State")
1232 // var hp = (window.Status) ? 5 : (window.State) ? 6 : 0;
1233 // check the quiz version
1234 if (hp
==5 || hp
==6) {
1235 // get details for each question
1236 var max_q
= (hp
==5) ? Status
.length
: State
.length
;
1237 for (var q
=0; q
<max_q
; q
++) {
1238 // skip this question if it was not used (HP6 v6 only)
1239 if (hp
==6 && !State
[q
]) continue;
1240 // format 'Q' (a padded, two-digit version of 'q')
1241 var Q
= getQ('JQuiz', q
);
1243 if (JQuiz
[0]) qDetails
+= makeSeparator(Q
);
1244 if (hp
==6 && JQuiz
[11]) { // question type
1245 var x
= parseInt(I
[q
][2]);
1246 x
= (x
==0) ? 'multiple-choice' : (x
==1) ? 'short-answer' : (x
==2) ? 'hybrid' : (x
==3) ? 'multi-select' : 'n/a';
1247 qDetails
+= hpHiddenField(Q
+'type', x
);
1250 var x
= (hp
==5) ? Status
[q
][4]*10 : I
[q
][0]*State
[q
][0];
1252 qDetails
+= hpHiddenField(Q
+'score', Math
.floor(x
)+'%');
1253 if (hp
==6 && JQuiz
[10]) { // weighting
1254 qDetails
+= hpHiddenField(Q
+'weighting', I
[q
][0]);
1256 if (JQuiz
[1]) { // question text
1257 var x
= (hp
==5) ? I
[q
][0] : (document
.getElementById
) ? GetTextFromNodeN(document
.getElementById('Q_'+q
), 'QuestionText') : '';
1258 qDetails
+= hpHiddenField(Q
+'question', x
);
1260 if (JQuiz
[2]) { // student's correct answers
1261 var x
= (HP
[_correct
][q
]) ? HP
[_correct
][q
] : '';
1262 qDetails
+= hpHiddenField(Q
+'correct', x
);
1264 if (JQuiz
[3]) { // ignored and wrong answers
1265 var x
= (hp
==5) ? new Array() : GetJQuizAnswerDetails(q
, 1);
1267 for (var i
=0; i
<I
[q
][1].length
; i
++) {
1268 var correct
= HP
[_correct
][q
] ? HP
[_correct
][q
] : '';
1269 if (I
[q
][1][i
][0] && I
[q
][1][i
][0].toUpperCase()!=correct
.toUpperCase()) {
1270 x
[x
.length
] = I
[q
][1][i
][0];
1274 if (DB
[0] || x
) qDetails
+= hpHiddenField(Q
+'other', x
);
1276 if (hp
==6 && JQuiz
[7]) { // all selected answers
1277 var x
= GetJQuizAnswerDetails(q
, 0);
1278 qDetails
+= hpHiddenField(Q
+'selected', x
);
1280 if (JQuiz
[8]) { // wrong answers
1281 var x
= (HP
[_wrong
][q
]) ? HP
[_wrong
][q
] : '';
1282 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1284 if (hp
==6 && JQuiz
[9]) { // ignored answers
1285 var x
= GetJQuizAnswerDetails(q
, 4);
1286 qDetails
+= hpHiddenField(Q
+'ignored', x
);
1288 if (JQuiz
[4]) { // number of hints
1289 var x
= (HP
[_hints
][q
]) ? HP
[_hints
][q
] : 0;
1290 qDetails
+= hpHiddenField(Q
+'hints', x
);
1292 if (JQuiz
[5] || JQuiz
[12]) { // number of checks
1293 if (JQuiz
[12]) { // strictly checks only
1294 var x
= (HP
[_checks
][q
]) ? HP
[_checks
][q
] : 0;
1295 } else { // checks (+ hints in HP6)
1296 var x
= (hp
==5) ? Status
[q
][1] : (State
[q
][2]-1);
1298 qDetails
+= hpHiddenField(Q
+'checks', x
);
1300 if (JQuiz
[13]) { // ShowAnswer button
1301 var x
= (HP
[_clues
][q
]) ? HP
[_clues
][q
] : 0;
1302 qDetails
+= hpHiddenField(Q
+'clues', x
);
1308 function GetTextFromNodeN(obj
, className
, n
) {
1309 // returns the text under the nth node of obj with the target class name
1311 if (obj
&& className
) {
1312 if (typeof(n
)=='undefined') {
1315 var nodes
= GetNodesByClassName(obj
, className
);
1316 if (n
<nodes
.length
) {
1317 txt
+= GetTextFromNode(nodes
[n
]);
1322 function GetNodesByClassName(obj
, className
) {
1323 // returns an array of nodes with the target classname
1324 var nodes
= new Array();
1326 if (className
&& obj
.className
==className
) {
1328 } else if (obj
.childNodes
) {
1329 for (var i
=0; i
<obj
.childNodes
.length
; i
++) {
1330 nodes
= nodes
.concat(GetNodesByClassName(obj
.childNodes
[i
], className
));
1336 function GetTextFromNode(obj
) {
1337 // return text in (and under) a single DOM node
1340 if (obj
.nodeType
==3) {
1341 txt
= obj
.nodeValue
+ ' ';
1343 if (obj
.childNodes
) {
1344 for (var i
=0; i
<obj
.childNodes
.length
; i
++) {
1345 txt
+= GetTextFromNode(obj
.childNodes
[i
]);
1351 function GetJQuizAnswerDetails(q
, flag
) {
1352 // flag : the type of information required about the student's answers
1353 // 0 : all student's answers
1354 // 1 : student's wrong and ignored answers
1355 // 2 : student's correct answers
1356 // 3 : student's wrong answers
1357 // 4 : ignored answers
1358 var x
= State
[q
][5]; //Sequence of answers chosen by number
1359 if (I
[q
][2]=='3') { // multi-select
1361 var x
= new Array();
1363 // get required part of 'x' and convert to array
1364 if (x
.charAt(0)=='|') {
1365 // HP 6.0 and 6.1 (always has leading bar)
1366 var i
= x
.lastIndexOf('|');
1367 var x
= x
.substring((flag
==2 ? (i
+1) : 1), ((flag
==0 || flag
==2) ? x
.length
: i
)).split('|');
1369 // HP 6.2 (no leading delimiter)
1370 var i
= x
.lastIndexOf(' | ');
1371 var x
= x
.substring((flag
==2 ? (i
+3) : 0), ((flag
==0 || flag
==2) ? x
.length
: i
)).split(' | ');
1374 for (var i
=0; i
<x
.length
; i
++) {
1375 var a
= new Array();
1376 for (var ii
=0; ii
<x
[i
].length
; ii
++) {
1377 if (x
[i
].charAt(ii
)=='Y') {
1378 var s
= JQuiz
[6] ? String
.fromCharCode(97+ii
) : I
[q
][3][ii
][0];
1379 if (s
&& s
.replace
&& window
.RegExp
) {
1380 s
= s
.replace(new RegExp('\\+', 'g'), '+');
1387 } else if (x
) { // multiple-choice, short-answer and hybrid
1388 if (x
.charAt(x
.length
-1)==',') {
1389 // HP 6.0 and 6.1 (always has trailing comma)
1390 x
= x
.substring(0, x
.length
-1).split(',');
1392 // HP 6.2 (short answer also contains student entered text)
1396 var a
= new Array();
1397 if (flag
==1 || flag
==2 || flag
==3) {
1398 for (var i
=0; i
<x
.length
; i
++) {
1399 var is_correct
= false;
1400 if (x
[i
].length
==1) { // single letter
1401 var ii
= x
[i
].charCodeAt(0) - 65;
1402 if (I
[q
][3] && I
[q
][3][ii
] && I
[q
][3][ii
][2]) {
1403 var is_correct
= true;
1411 if (flag
==1 || flag
==3) {
1421 if (flag
==1 || flag
==4) {
1422 for (var i
=0; i
<I
[q
][3].length
; i
++) {
1423 var s
= String
.fromCharCode(65+i
);
1424 for (var ii
=0; ii
<x
.length
; ii
++) {
1425 if (x
[ii
]==s
) break;
1427 if (ii
==x
.length
) a
.push(s
);
1432 // convert answer indexes to values, if required
1433 if (JQuiz
[6]==false) {
1434 for (var i
=0; i
<x
.length
; i
++) {
1435 if (x
[i
].length
==1) { // single letter
1436 var ii
= x
[i
].charCodeAt(0) - 65;
1437 if (I
[q
][3] && I
[q
][3][ii
]) {
1438 x
[i
] = I
[q
][3][ii
][0];
1448 function GetRhubarbDetails(v
) {
1451 var q
= 0; // always zero
1452 var Q
= getQ('Rhubarb', q
);
1453 if (document
.title
) { // use quiz title as question name
1454 qDetails
+= hpHiddenField(Q
+'name', document
.title
);
1456 if (Rhubarb
[0]) { // correct words
1457 var x
= (HP
[_correct
][q
]) ? HP
[_correct
][q
] : '';
1458 if (Rhubarb
[1]) { // count of correct words
1459 for (var i
=0,ii
=0; i
<x
.length
; i
++) {
1464 qDetails
+= hpHiddenField(Q
+'correct', x
);
1466 if (Rhubarb
[2]) { // wrong words
1467 var x
= (HP
[_wrong
][q
]) ? HP
[_wrong
][q
] : '';
1468 if (Rhubarb
[3]) { // count of wrong words
1471 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1473 if (Rhubarb
[4]) { // ignored
1475 qDetails
+= hpHiddenField(Q
+'ignored', x
);
1477 if (Rhubarb
[5]) { // hints
1478 var x
= (HP
[_hints
][q
]) ? HP
[_hints
][q
] : '';
1479 qDetails
+= hpHiddenField(Q
+'hints', x
);
1484 function GetSequiturDetails(v
) {
1487 var q
= 0; // always zero
1488 var Q
= getQ('Sequitur', q
);
1489 if (document
.title
) { // use quiz title as question name
1490 qDetails
+= hpHiddenField(Q
+'name', document
.title
);
1492 if (Sequitur
[0]) { // number of correct buttons chosen
1493 var x
= (HP
[_correct
][q
]) ? HP
[_correct
][q
] : '';
1494 qDetails
+= hpHiddenField(Q
+'correct', x
);
1496 if (Sequitur
[1]) { // number of wrong buttons chosen
1497 var x
= (HP
[_wrong
][q
]) ? HP
[_wrong
][q
] : '';
1498 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1503 // *********************
1504 // click event handlers
1505 // *********************
1506 function hpClick(x
, args
) {
1507 // x is the button type
1508 // args is either empty, a single argument, or an array of arguments
1509 var btn
= (x
==1) ? 'Hint' : (x
==2) ? 'Clue' : (x
==3) ? 'Check' : (x
==4) ? 'Enter' : '';
1511 // convert args to array, if necessary
1512 var t
= typeof(args
);
1514 // do nothing (args is already an array)
1515 } else if (t
=='undefined') {
1518 args
= new Array(''+args
);
1520 // call handler for this kind of button
1521 var x
= eval('hpClick'+btn
+'('+hpVersion()+','+hpQuizType()+','+hpQuizVersion()+',args);');
1524 function hpClickHint(hp
, t
, v
, args
) {
1525 if (t
==2 || t
==5 || t
==6 || t
==7) { // JCloze, JMix, JQuiz, Rhubarb
1526 var q
= args
[0]; // clue/question number
1527 if (!HP
[_hints
][q
]) HP
[_hints
][q
] = 0;
1530 if (t
==3) { // JCross
1532 var q
= args
[0]; // clue/question number
1533 var AD
= args
[1]; // direction ('A' or 'D')
1534 if (!HP
[_hints
][AD
]) HP
[_hints
][AD
] = new Array();
1535 if (!HP
[_hints
][AD
][q
]) HP
[_hints
][AD
][q
] = 0;
1536 HP
[_hints
][AD
][q
]++;
1541 function hpClickClue(hp
, t
, v
, args
) {
1542 if (t
==2 || t
==3 || t
==6) { // JCloze or JCross, or JQuiz (ShowAnswer button)
1543 var q
= args
[0]; // clue/question number
1544 if (!HP
[_clues
][q
]) HP
[_clues
][q
] = 0;
1549 function hpClickCheck(hp
, t
, v
, args
) {
1550 if (t
==2) { // JCloze
1552 var r
= hpRottmeier();
1553 var already_correct
= 'true';
1555 already_correct
= (hp
==5) ? 'State[i][4]==1' : 'State[i].AnsweredCorrectly==true';
1556 } else if (r
==1) { // DropDown
1557 already_correct
= 'GapList[i][1].GapLocked==true';
1558 } else if (r
==2.1) { // Find-It 3a
1559 already_correct
= 'GapList[i][1].ErrorFound==true';
1560 } else if (r
==2.2) { // Find-It 3b
1561 already_correct
= 'GapList[i][1].GapSolved==true';
1563 var i_max
= I
.length
;
1564 for (var i
=0; i
<i_max
; i
++) {
1565 if (eval(already_correct
)) continue;
1567 if (r
==0 || r
==2.2) {
1569 } else if (r
==1) { // DropDown
1571 g
= eval('document.Cloze.Gap'+i
+'.value');
1573 var ii
= Get_SelectedDropValue(i
);
1574 if (isNaN(ii
) || ii
<0) { // 'null' || -1
1575 g
= ''; // no guess yet
1577 if (window
.MakeIndividualDropdowns
) {
1578 var is_wrong
= (ii
!=0);
1581 var is_wrong
= (ii
!=i
);
1586 } else if (r
==2.1 && i
==args
[0]) { // Find-It 3a
1590 if (!HP
[_checks
][i
]) HP
[_checks
][i
] = 0;
1592 if (!HP
[_guesses
][i
]) HP
[_guesses
][i
] = new Array();
1593 var ii
= HP
[_guesses
][i
].length
;
1594 // is this a new guess at this gap?
1595 if (ii
==0 || g
!=HP
[_guesses
][i
][ii
-1]) {
1596 HP
[_guesses
][i
][ii
] = g
;
1598 // Rottmeier DropDown 2.4
1601 var G
= g
.toUpperCase();
1602 var ii_max
= I
[i
][1].length
;
1603 for (var ii
=0; ii
<ii_max
; ii
++) {
1604 if (window
.CaseSensitive
) {
1605 if (g
==I
[i
][1][ii
][0]) break;
1607 if (G
==I
[i
][1][ii
][0].toUpperCase()) break;
1610 var is_wrong
= (ii
==ii_max
);
1612 if (is_wrong
) { // guess is wrong
1613 if (!HP
[_wrong
][i
]) HP
[_wrong
][i
] = new Array();
1614 var ii_max
= HP
[_wrong
][i
].length
;
1615 for (var ii
=0; ii
<ii_max
; ii
++) {
1616 if (HP
[_wrong
][i
][ii
]==g
) break;
1619 HP
[_wrong
][i
][ii
] = g
;
1621 } else { // guess is correct
1622 HP
[_correct
][i
] = g
;
1629 if (t
==3) { // JCross
1631 var q
= args
[0]; // clue/question number
1632 for (var row
=0; row
<L
.length
; row
++) {
1633 for (var col
=0; col
<L
[row
].length
; col
++) {
1634 var q
= (v
==5) ? C
[row
][col
] : CL
[row
][col
];
1636 hpClickCheckJCrossV5V6(hp
, v
, 'A', q
, row
, col
);
1637 hpClickCheckJCrossV5V6(hp
, v
, 'D', q
, row
, col
);
1643 if (t
==4) { // JMatch
1644 var a
= new Array();
1645 var extra
= ''; // extra js code to eval(uate)
1646 var guess
= ''; // js code to eval(uate) guess
1647 var correct
= ''; // js code to eval(uate) correct answer
1648 if (window
.D
&& window
.F
) {
1649 // drag-and-drop, i.e. v5+ and v6+ (HP5 and HP6)
1651 guess
= 'GetJMatchRHS(v,i)';
1652 correct
= 'GetJMatchRHS(v,i,true)';
1653 } else if (window
.GetKeyFromSelect
) {
1656 guess
= 'GetJMatchRHS(v,i)';
1657 correct
= 'GetJMatchRHS(v,i,true)';
1658 } else if (window
.GetAnswer
) {
1661 guess
= "(I[i][2]==0||I[i][0]=='')?'':GetAnswer(i)";
1662 correct
= 'I[i][3])';
1663 } else if (window
.Draggables
) {
1666 s
= "Draggables[i].correct=='1'";
1667 } else if (window
.CorrectAnswers
) {
1670 guess
= 'document.QuizForm.elements[i*2].selectedIndex';
1671 correct
= 'CorrectAnswers[i]';
1673 for (var i
=0; i
<a
.length
; i
++) {
1674 // check this match has not already been finished
1675 if (!HP
[_correct
][i
]) {
1676 // do extra setup, if necessary
1677 if (extra
) eval(extra
);
1678 // get the guess, if any
1679 var g
= ''+eval(guess
);
1681 // is the guess correct?
1682 if (g
==eval(correct
)) {
1683 HP
[_correct
][i
] = g
;
1684 } else { // wrong answer
1685 // initialize wrong guess array if necessary
1686 if (!HP
[_wrong
][i
]) HP
[_wrong
][i
] = new Array();
1687 // check to see if the guess is already in the guess array
1688 var i_max
= HP
[_wrong
][i
].length
;
1689 for (var ii
=0; ii
<i_max
; ii
++) {
1690 if (HP
[_wrong
][i
][ii
]==g
) break;
1692 // add the guess if it was not found
1694 HP
[_wrong
][i
][ii
]=g
;
1696 g
= null; // this is not a new answer
1699 // increment checks for this question, if necessary
1701 if (!HP
[_checks
][i
]) HP
[_checks
][i
] = 0;
1709 // get question number (always 0)
1711 // check question has not already been answered correctly
1712 if (!HP
[_correct
][q
]) {
1713 // match current guess against possible correct answers
1714 var a_max
= Answers
.length
;
1715 for (var a
=0; a
<a_max
; a
++) {
1716 var i_max
= Answers
[a
].length
;
1717 for (var i
=0; i
<i_max
; i
++) {
1718 if (Answers
[a
][i
] != GuessSequence
[i
]) break;
1720 if (i
==i_max
) break; // correct answer was found
1722 // at this point, (a==a_max) means guess is wrong
1723 // get array of segment texts in this g(uess)
1724 var g
= GetJMixSequence(GuessSequence
);
1725 // convert g(uess) array and to a s(tring)
1727 var i_max
= g
.length
;
1728 for (var i
=0; i
<i_max
; i
++) {
1731 s
+= (s
=='' ? '' : '+') + g
[i
];
1735 if (a
==a_max
) { // wrong
1736 if (!HP
[_wrong
][q
]) HP
[_wrong
][q
] = new Array();
1737 var i
= HP
[_wrong
][q
].length
;
1738 HP
[_wrong
][q
][i
] = s
;
1740 HP
[_correct
][q
] = s
;
1742 // increment checks for this question
1743 if (!HP
[_checks
][q
]) HP
[_checks
][q
] = 0;
1748 if (t
==6) { // JQuiz
1749 if (hp
==5 || hp
==6) {
1750 var q
= args
[0]; // clue/question number
1753 var g
= TrimString(eval('BottomFrame.document.QForm' + q
+ '.Guess').value
);
1755 var g
= TrimString(eval('document.QForm.Guess').value
);
1760 // increment check count
1761 if (!HP
[_checks
][q
]) HP
[_checks
][q
] = 0;
1764 var G
= g
.toUpperCase(); // used for shortanswer only
1765 var correct_answer
= ''; // used for multiselect only
1766 // set index of answer array in I (the question array)
1767 var ans
= (hp
==5) ? 1 : 3;
1768 var i_max
= I
[q
][ans
].length
;
1769 for (var i
=0; i
<i_max
; i
++) {
1770 // is this a (possible) correct answer?
1771 if (hp
==5 || (hp
==6 && I
[q
][ans
][i
][2])) {
1772 if (hp
==6 && I
[q
][2]==3) { // multiselect
1773 correct_answer
+= (correct_answer
? '+' : '') + I
[q
][ans
][i
][0];
1774 } else { // multichoice, shortanswer
1775 if (window
.CaseSensitive
) {
1776 if (g
==I
[q
][ans
][i
][0]) break;
1778 if (G
==I
[q
][ans
][i
][0].toUpperCase()) break;
1783 if (i
==i_max
&& g
!=correct_answer
) { // wrong
1784 if (!HP
[_wrong
][q
]) HP
[_wrong
][q
] = new Array();
1785 var i_max
= HP
[_wrong
][q
].length
;
1786 for (var i
=0; i
<i_max
; i
++) {
1787 if (HP
[_wrong
][q
][i
]==g
) break;
1789 if (i
==i_max
) HP
[_wrong
][q
][i
] = g
;
1791 HP
[_correct
][q
] = g
;
1796 if (t
==7) { // Rhubarb
1798 var q
= 0; // question number (always zero)
1799 var g
= args
[0]; // InputWord from CheckGuess()
1801 var G
= g
.toUpperCase();
1802 var i_max
= Words
.length
;
1803 for (var i
=0; i
<i_max
; i
++) {
1804 if (G
==Words
[i
].toUpperCase()) break;
1806 if (i
<i_max
) { // correct
1807 if (!HP
[_correct
][q
]) HP
[_correct
][q
] = new Array();
1808 HP
[_correct
][q
][i
] = g
;
1810 if (!HP
[_wrong
][q
]) HP
[_wrong
][q
] = new Array();
1811 var i_max
= HP
[_wrong
][q
].length
;
1812 for (var i
=0; i
<i_max
; i
++) {
1813 if (G
==HP
[_wrong
][q
][i
].toUpperCase()) break;
1815 if (i
==i_max
) HP
[_wrong
][q
][i
] = g
;
1820 if (t
==8) { // Sequitur
1822 var q
= 0; // question number (always zero)
1823 if (CurrentCorrect
==args
[0]) { // correct button chosen
1824 if (!HP
[_correct
][q
]) HP
[_correct
][q
] = 0;
1827 if (!HP
[_wrong
][q
]) HP
[_wrong
][q
] = 0;
1834 function hpClickCheckJCrossV5V6(hp
, v
, AD
, q
, row
, col
) {
1835 // v is the version of Hot Potatoes
1836 // AD is the direction ('A' or 'D')
1837 // make sure HP[_checks] and HP[_correct] are initialized
1838 if (!HP
[_checks
][AD
]) HP
[_checks
][AD
] = new Array();
1839 if (!HP
[_correct
][AD
]) HP
[_correct
][AD
] = new Array();
1841 var clue
= (hp
==5) ? eval('window.'+AD
) : GetJCrossClue('Clue_'+AD
+'_' + q
);
1842 // is this a question that has not been answered correctly yet?
1843 if (clue
&& !HP
[_correct
][AD
][q
]) {
1845 var guess
= GetJCrossWord(G
, row
, col
, (AD
=='D'));
1846 var correct
= GetJCrossWord(L
, row
, col
, (AD
=='D'));
1847 if (guess
==correct
) {
1848 HP
[_correct
][AD
][q
] = correct
;
1851 // make sure HP[_wrong] is initialized
1852 if (!HP
[_wrong
][AD
]) HP
[_wrong
][AD
] = new Array();
1853 if (!HP
[_wrong
][AD
][q
]) HP
[_wrong
][AD
][q
] = new Array();
1854 // check this guess has not been entered before
1855 var i_max
= HP
[_wrong
][AD
][q
].length
;
1856 for (var i
=0; i
<i_max
; i
++) {
1857 if (HP
[_wrong
][AD
][q
]==guess
) break;
1859 // add the guess if it has not been entered before
1861 HP
[_wrong
][AD
][q
][i
] = guess
;
1865 // update HP[_checks], if necessary
1867 if (!HP
[_checks
][AD
]) HP
[_checks
][AD
] = new Array();
1868 if (!HP
[_checks
][AD
][q
]) HP
[_checks
][AD
][q
] = 0;
1869 HP
[_checks
][AD
][q
]++;
1873 function hpClickEnter(hp
, t
, v
, args
) {
1874 if (t
==3) { // JCross
1875 var q
= args
[0]; // clue/question number
1876 if (!HP
[_enter
][q
]) HP
[_enter
][q
] = 0;
1881 function GetJMatchQuestionDetails(hp
, v
) {
1883 // HP5.5 uses "I" for v5 and v6 JMatch quizzes
1884 // var hp5 = (window.I) ? true : false;
1885 // check the quiz version
1886 if (hp
==5 || hp
==6) {
1887 if (JMatch
[1] && v
==6.1) { // attempts
1888 qDetails
+= hpHiddenField('JMatch_attempts', Penalties
+1);
1890 // get number of questions
1891 var max_q
= (hp
==5 || v
==6) ? Status
.length
: F
.length
;
1892 // get details for each question
1893 for (var q
=0; q
<max_q
; q
++) {
1894 // format 'Q' (a padded, two-digit version of 'q')
1895 var Q
= getQ('JMatch', q
);
1896 // add separator, if required
1897 if (JMatch
[0] && (JMatch
[1] || JMatch
[2] || JMatch
[3])) {
1898 qDetails
+= makeSeparator(Q
);
1900 if (JMatch
[1] && (hp
==5 || v
==6)) { // attempts
1901 qDetails
+= hpHiddenField(Q
+'attempts', Status
[q
][1]);
1903 if (JMatch
[2]) { // LHS text (the question)
1904 var x
= (v
==5) ? I
[q
][0] : (v
==6) ? GetJMatchText(q
, 'LeftItem') : F
[q
][0];
1905 qDetails
+= hpHiddenField(Q
+'lhs', x
);
1907 if (JMatch
[3]) { // correct answer (if any)
1908 var x
= HP
[_correct
][q
] ? HP
[_correct
][q
] : '';
1909 qDetails
+= hpHiddenField(Q
+'correct', x
);
1911 if (JMatch
[4]) { // wrong answers (if any)
1912 var x
= HP
[_wrong
][q
] ? HP
[_wrong
][q
] : '';
1913 qDetails
+= hpHiddenField(Q
+'wrong', x
);
1915 if (JMatch
[5]) { // checks
1916 var x
= HP
[_checks
][q
] ? HP
[_checks
][q
] : 0;
1917 qDetails
+= hpHiddenField(Q
+'checks', x
);
1923 // *********************
1924 // library functions
1925 // *********************
1926 function pad(i
, l
) {
1928 while (s
.length
<l
) s
= '0' + s
;
1931 function getQ(section
, q
) {
1932 // Q is a padded, two-digit version of the question number, 'q', prefixed by 'section'
1933 return section
+ '_q' + (q
<9 ? '0' : '') + (q
+1) + '_';
1935 function makeSeparator(Q
) {
1936 return is_LMS() ? '' : hpHiddenField(Q
.substring(0, Q
.length
-1), '---------------------------------');
1938 function hpHiddenField(name
, value
, comma
, forceHTML
) {
1940 var t
= typeof(value
);
1942 value
= encode_entities(value
);
1943 } else if (t
=='object') { // array
1945 var i_max
= values
.length
;
1947 if (comma
==null) comma
= ',';
1948 for (var i
=0; i
<i_max
; i
++) {
1949 values
[i
] = trim(values
[i
]);
1950 if (values
[i
]!=null && values
[i
]!='') {
1951 value
+= (i
==0 ? '' : comma
) + encode_entities(values
[i
]);
1955 if (is_LMS() && !forceHTML
) {
1956 if (value
&& value
.indexOf
&& value
.indexOf('<')>=0 && value
.indexOf('>')>=0) {
1957 value
= '<![CDATA[' + value
+ ']]>';
1959 field
= '<field><fieldname>' + name
+ '</fieldname><fielddata>' + value
+ '</fielddata></field>';
1961 field
= '<input type=hidden name="' + name
+ '" value="' + value
+ '">';
1966 if (s
==null) s
= '';
1969 while (i
<ii
&& s
.charAt(i
)==' ') {
1972 while (ii
>i
&& s
.charAt(ii
-1)==' ') {
1975 return s
.substring(i
, ii
);
1977 function encode_entities(s_in
) {
1978 var i_max
= (s_in
) ? s_in
.length
: 0;
1980 for (var i
=0; i
<i_max
; i
++) {
1981 var c
= s_in
.charCodeAt(i
);
1982 // 34 : double quote .......["] &
1983 // 38 : single quote .......['] '
1984 // 43 : plus sign ..........[+]
1985 // 44 : comma ..............[,]
1986 // 60 : left angle bracket .[<] <
1987 // 62 : right angle bracket [>] >
1988 // >=128 multibyte character
1989 s_out
+= (c
==43 || c
==44 || c
>=128) ? ('&#x' + pad(c
.toString(16), 4) + ';') : s_in
.charAt(i
);
1993 // *********************
1996 // *********************
1997 function getTime(obj
) {
1998 obj
= obj
? obj
: new Date();
1999 // get year, month and day
2000 // for an LMS : yyyy-mm-dd
2001 // for email : DayName MonthName dd yyyy
2003 obj
.getFullYear() + '-' + pad(obj
.getMonth()+1, 2) + '-' + pad(obj
.getDate(), 2) :
2004 MSG
[16][obj
.getDay()] + ' ' + MSG
[17][obj
.getMonth()] + ' ' + pad(obj
.getDate(), 2) + ' ' + obj
.getFullYear()
2006 // get hours, minutes and seconds (hh:mm:ss)
2007 s
+= ' ' + pad(obj
.getHours(), 2) + ':' + pad(obj
.getMinutes(), 2) + ':' + pad(obj
.getSeconds(), 2);
2008 // get time difference
2009 // for an LMS : +xxxx
2010 // for email : GMT+xxxx
2011 var x
= obj
.getTimezoneOffset(); // e.g. -540
2013 s
+= ' ' + (is_LMS() ? '' : 'GMT') + (x
<0 ? '+' : '-');
2015 s
+= pad(parseInt(x
/60), 2) + pad(x - (parseInt(x/60)*60), 2);
2019 function getFunc(fn
) {
2020 if (typeof(fn
)=='string') {
2021 fn
= eval('window.' + fn
);
2023 return (typeof(fn
)=='function') ? fn
: null;
2025 function getFuncCode(fn
, extraCode
, anchorCode
, beforeAnchor
) {
2027 var obj
= getFunc(fn
);
2030 var i1
= s
.indexOf('{')+1;
2031 var i2
= s
.lastIndexOf('}');
2032 if (i1
>0 && i1
<i2
) {
2033 s
= s
.substring(i1
, i2
);
2039 s
= replaceLast(anchorCode
, extraCode
+ anchorCode
, s
);
2041 s
= replaceLast(anchorCode
, anchorCode
+ extraCode
, s
);
2053 function getArgsStr(args
, addQuotes
) {
2054 // make s(tring) version of function args array
2056 var i_max
= args
.length
;
2057 for (var i
=0; i
<i_max
; i
++) {
2059 s
+= '"' + args
[i
] + '",';
2069 function getFuncArgs(fn
, flag
) {
2070 // flag==0 : return args as string
2071 // flag==1 ; return args as array
2073 var a
= new Array();
2074 var obj
= getFunc(fn
);
2076 var s
= obj
.toString();
2077 var i1
= s
.indexOf('(') + 1;
2078 var i2
= s
.indexOf(')', i1
);
2079 // add args to a(rray)
2080 while (i1
>0 && i1
<i2
) {
2081 var i3
= s
.indexOf(',', i1
); // next comma
2082 if (i3
<0 || i3
>i2
) i3
= i2
;
2083 a
[i
++] = trim(s
.substring(i1
, i3
));
2087 return flag
? a
: getArgsStr(a
);
2089 function getPrompt(fn
) {
2090 // the LoginPrompt is the text string in the first prompt(...) statement
2091 // v5 : in the StartUp function
2092 // v6 : in the GetUserName function
2093 // Note: netscape uses double-quote as delimiter, others use single quote
2094 var s
= getFuncCode(fn
);
2095 var i1
= s
.indexOf('prompt') + 8;
2096 var i2
= s
.indexOf(s
.charAt(i1
-1), i1
);
2097 var p
= (i1
>=8 && i2
>i1
) ? s
.substring(i1
, i2
) : '';
2098 // make sure browser has decoded the unicode prompt properly
2099 // this check is mainly for ns4, but there may be others
2100 if (window
.RegExp
) {
2101 var r
= new RegExp('u([0-9A-F]{4})');
2104 p
= p
.replace(m
[0], '&#' + parseInt(m
[1], 16) + ';');
2110 function getStartUpCode(fn
) {
2111 // the main initialization code comes from the StartUp function
2112 // v5 : the code before "UserName", if any,
2113 // and the code after the 2nd subsequent '}'
2114 // v6 : the code before and after 'GetUserName();'
2115 // i.e. all the code except the call to 'GetUserName();'
2116 var s
= getFuncCode(fn
);
2117 var i1
= s
.indexOf('GetUserName();');
2121 var i1
= s
.indexOf('UserName');
2122 var i2
= s
.indexOf('}', s
.indexOf('}', i1
+8)+1)+1;
2124 return (0<i1
&& i1
<i2
) ? s
.substring(0, i1
) + s
.substring(i2
) : '';
2127 if (!window
.hpCheckedForm
) {
2128 window
.hpCheckedForm
= true;
2129 window
.hpFoundForm
= hpFindForm('store') ? true : false;
2133 function hpFeedback() {
2137 if (FEEDBACK
[1] && FEEDBACK
[2]) { // formmail
2138 html
+= '<html><body>'
2139 + '<form action="' + FEEDBACK
[0] + '" method="POST">'
2140 + '<table border="0">'
2141 + '<tr><th valign="top" align="right">' + FEEDBACK
[7] + ':</th><td>' + document
.title
+ '</td></tr>'
2142 + '<tr><th valign="top" align="right">' + FEEDBACK
[8] + ': </th><td>'
2144 if (typeof(FEEDBACK
[1])=='string') {
2145 html
+= FEEDBACK
[1] + hpHiddenField('recipient', FEEDBACK
[1], ',', true);
2146 } else if (typeof(FEEDBACK
[1])=='object') {
2147 var i_max
= FEEDBACK
[1].length
;
2148 if (i_max
==1) { // one teacher
2149 html
+= FEEDBACK
[1][0][0] + hpHiddenField('recipient', FEEDBACK
[1][0][0]+' <'+FEEDBACK
[1][0][1]+'>', ',', true);
2150 } else if (i_max
>1) { // several teachers
2151 html
+= '<select name="recipient">';
2152 for (var i
=0; i
<i_max
; i
++) {
2153 html
+= '<option value="'+FEEDBACK
[1][i
][1]+'">' + FEEDBACK
[1][i
][0] + '</option>';
2155 html
+= '</select>';
2158 html
+= '</td></tr>'
2159 + '<tr><th valign="top" align="right">' + FEEDBACK
[9] + ':</th>'
2160 + '<td><TEXTAREA name="message" rows="10" cols="40"></TEXTAREA></td></tr>'
2161 + '<tr><td> </td><td><input type="submit" value="' + FEEDBACK
[6] + '">'
2162 + hpHiddenField('realname', FEEDBACK
[2], ',', true)
2163 + hpHiddenField('email', FEEDBACK
[3], ',', true)
2164 + hpHiddenField('subject', document
.title
, ',', true)
2165 + hpHiddenField('title', document
.title
, ',', true)
2166 + hpHiddenField('return_link_title', FEEDBACK
[10], ',', true)
2167 + hpHiddenField('return_link_url', 'javascript:self.close()', ',', true)
2168 + '</td></tr></table></form></body></html>'
2170 } else if (FEEDBACK
[1]) { // url only
2171 if (typeof(FEEDBACK
[1])=='object') {
2172 var i_max
= FEEDBACK
[1].length
;
2173 if (i_max
>1) { // several teachers
2174 html
+= '<html><body>'
2175 + '<form action="' + FEEDBACK
[0] + '" method="POST" onsubmit="this.action+=this.recipient.options[this.recipient.selectedIndex].value">'
2176 + '<table border="0">'
2177 + '<tr><th valign="top" align="right">' + FEEDBACK
[7] + ':</th><td>' + document
.title
+ '</td></tr>'
2178 + '<tr><th valign="top" align="right">' + FEEDBACK
[8] + ': </th><td>'
2180 html
+= '<select name="recipient">';
2181 for (var i
=0; i
<i_max
; i
++) {
2182 html
+= '<option value="'+FEEDBACK
[1][i
][1]+'">' + FEEDBACK
[1][i
][0] + '</option>';
2184 html
+= '</select>';
2185 html
+= '</td></tr>'
2186 + '<tr><td> </td><td><input type="submit" value="' + FEEDBACK
[6] + '">'
2187 + '</td></tr></table></form></body></html>'
2189 } else if (i_max
==1) { // one teacher
2190 url
= FEEDBACK
[0] + FEEDBACK
[1][0][1];
2192 } else if (typeof(FEEDBACK
[1])=='string') {
2193 url
= FEEDBACK
[0] + FEEDBACK
[1];
2199 var w
= openWindow(url
, 'feedback', FEEDBACK
[4], FEEDBACK
[5], 'RESIZABLE,SCROLLBARS', html
);
2201 // unable to open popup window
2207 // ********************
2209 // ********************
2210 function hpNewFunction(f
, a
, s
) {
2211 if (window
.C
&& C
.safari
) {
2212 if (f
=='CheckAnswers') {
2213 if (s
.indexOf('TotalChars-State[i].HintsAndChecks/')>=0) {
2214 // special fix for "CheckAnswers" in JCloze
2215 s
= s
.replace(/TotalChars-State\[i\]\.HintsAndChecks/g, '(TotalChars-State[i].HintsAndChecks)');
2217 if (s
.indexOf('TotalChars-GapList[x][1].HintsAndChecks/')>=0) {
2218 // special fix for "CheckAnswers" in JCloze (Find-It)
2219 s
= s
.replace(/TotalChars-GapList\[x\]\[1\]\.HintsAndChecks/g, '(TotalChars-GapList[x][1].HintsAndChecks)');
2221 if (s
.indexOf('CorrectLetters-Penalties/')>=0) {
2222 // special fix for "CheckAnswers" in JMatch
2223 s
= s
.replace(/CorrectLetters-Penalties/g, '(CorrectLetters-Penalties)');
2225 if (s
.indexOf('TotCorrectChoices-Penalties/')>=0) {
2226 // special fix for "CheckAnswers" in JMix (v6)
2227 s
= s
.replace(/TotCorrectChoices-Penalties/g, '(TotCorrectChoices-Penalties)');
2229 if (s
.indexOf('TotalCorrect-Penalties/')>=0) {
2230 // special fix for "CheckAnswers" in JMix (v6+) Drag-and_Drop
2231 s
= s
.replace(/TotalCorrect-Penalties/g, '(TotalCorrect-Penalties)');
2234 if (s
.indexOf('replace(\\[')>=0) {
2235 s
= s
.replace(/\\\[/g, '/\\[');
2236 s
= s
.replace(/\\\]/g, '\\]/g');
2238 if (s
.indexOf('for (i')>=0 || s
.indexOf('for (x')>=0) {
2239 s
= s
.replace(/for \(/g, 'for (var ');
2241 eval('window.' + f
+ '=function(' + getArgsStr(a
) + '){' + s
+ '}');
2243 eval('window.' + f
+ '=new Function(' + getArgsStr(a
, true) + 's);');
2246 function hpInterceptFeedback() {
2247 // modify the function which writes feedback
2248 // v6: ShowMessage(Feedback)
2249 // but Rhubarb prints score in other functions, so use 'CheckFinished'
2250 // v5: WriteFeedback(Feedback)
2251 // v4: WriteFeedback(Stuff)
2252 // v3: WriteFeedback(Feedback) [except JMatch]
2253 // v3: CheckAnswer() [JMatch only]
2255 if (window
.CheckWord
) { // Rhubarb
2256 f
= 'CheckFinished';
2257 window
.FEEDBACK
= null;
2258 } else if (window
.ShowText
) { // Sequitur
2260 window
.FEEDBACK
= null;
2261 } else { // JBC, JCloze, JCross, JMatch, JMix, JQuiz
2262 f
= window
.ShowMessage
? 'ShowMessage' : window
.WriteFeedback
? 'WriteFeedback' : 'CheckAnswer';
2265 var s
= getFuncCode(f
) + 'Finish();';
2266 var a
= getFuncArgs(f
, true);
2267 if (a
[0] && window
.FEEDBACK
&& FEEDBACK
[0]) {
2268 s
= a
[0] + "+='<br /><br />" + '<a href="javascript:hpFeedback();">' + FEEDBACK
[6] + "</A>';" + s
;
2270 hpNewFunction(f
, a
, s
);
2273 function hpInterceptHints() {
2274 // modify the function which shows hints
2276 // JCloze v3-v6: ShowHint()
2277 // JCross v3: Cheat(), v4: ShowHint(), v5-v6[HP5]: ShowHint(Across,x,y,BoxName), v6[HP6]: ShowHint(Across,ClueNum,x,y,BoxId)
2279 // JMix v5-v6: CheckAnswer(CheckType=1)
2280 // JQuiz v3: CheckAnswer(ShowHint=true, QNum), v4: CheckAnswer(ShowHint=true), v5-v6[HP5]: CheckAnswer(ShowHint=true,QNum), v6[HP6]: ShowHint(QNum)
2281 var x
= ''; // extra code, if any
2284 } else if (window
.ShowHint
) {
2286 var a
= getFuncArgs(f
, true);
2288 if (window
.FindCurrent
) {
2290 x
= 'var q=window.Locked?-1:FindCurrent();if(q>=0&&GetHint(q))hpClick(1,q);';
2293 // work out which box would have a hint added
2294 // work out which question that box is part of using GridMap and WinLetters
2296 } else if (a
[0]=='Across') {
2297 if (a
[1]=='ClueNum') {
2299 x
= "var args=new Array(ClueNum,Across?'A':'D');hpClick(1,args);";
2300 } else if (a
[1]=='x' && a
[2]=='y') {
2301 // JCross v5-v6 [HP5]
2302 x
= "var args=new Array(C[x][y],Across?'A':'D');hpClick(1,args);";
2304 } else if (a
[0]=='QNum') {
2306 x
= 'hpClick(1,QNum);';
2308 } else if (window
.Hint
) {
2311 var a
= getFuncArgs(f
, true);
2312 x
= 'hpClick(1,0);'; // question number is always zero
2314 } else if (window
.CheckAnswer
) {
2315 var f
= 'CheckAnswer';
2316 var a
= getFuncArgs(f
, true);
2317 if (a
[0]=='ShowHint') {
2319 // JQuiz v3, v5-v6[HP5]
2320 x
= 'if(ShowHint)hpClick(1,QNum);';
2323 x
= 'if(ShowHint)hpClick(1,QNum-1);'; // QNum is a global variable
2325 } else if (a
[0]=='CheckType') {
2327 x
= 'if(CheckType==1)hpClick(1,0);'; // question number is always 0;
2330 // add the e(x)tra code, if any, to the start of the hint (f)unction
2332 var s
= getFuncCode(f
, x
, '', true);
2333 hpNewFunction(f
, a
, s
);
2336 function hpInterceptClues() {
2337 // modify the function which shows clues (or ShowAnswers in JQuiz)
2339 // JCloze v3-v6: ShowClue(ItemNum)
2340 // JCross v3-v4: ShowClue(ClueNum), v5-v6: ShowClue(ClueNum,x,y)
2343 // JQuiz ShowAnswers(QNum)
2344 var x
= ''; // extra code, if any
2345 if (window
.ShowClue
) {
2347 var a
= getFuncArgs(f
, true);
2348 if (a
[0]=='ItemNum') {
2350 x
= 'if(!window.Locked)hpClick(2,ItemNum);'; // v6 [HP6] uses window.Locked
2351 } else if (a
[0]=='ClueNum') {
2352 if (a
[1]=='x' && a
[2]=='y') {
2353 if (window
.A
&& window
.D
) {
2354 // JCross v5-v6 [HP5]
2355 x
= 'if(A[ClueNum]||D[ClueNum])hpClick(2,ClueNum);';
2356 } else if (document
.getElementById
) {
2358 x
= "if(document.getElementById('clue_' + ClueNum)||document.getElementById('Clue_D_' + ClueNum))hpClick(2,ClueNum);";
2361 if (window
.AClues
&& window
.DClues
) {
2363 x
= 'if(AClues[ClueNum]||DClues[ClueNum])hpClick(2,ClueNum);';
2368 // JQuiz: there is no "ShowClue" function but there is a "ShowAnswer" function
2369 if (window
.ShowAnswers
) {
2370 var f
= 'ShowAnswers';
2371 var a
= getFuncArgs(f
, true);
2373 if (window
.ShowMessage
) {
2375 x
= 'if(State[QNum][0]<1)hpClick(2,QNum);';
2376 } else if (window
.WriteFeedback
) {
2378 x
= 'if(State[QNum-1][0]<1)hpClick(2,QNum-1);';
2380 } else if (window
.Status
) {
2381 // JQuiz v5-v6 [HP5]
2382 x
= 'if(Status[QNum][0]<0)hpClick(5,QNum);';
2385 // add the e(x)tra code, if any, to the start of the clue (f)unction
2387 var s
= getFuncCode(f
, x
, '', true);
2388 var s
= getFuncCode(f
, '', '', true);
2389 hpNewFunction(f
, a
, s
);
2392 function hpInterceptChecks() {
2393 // modify the function which handles checks
2395 // JCloze CheckAnswers()
2396 // NB: Rottmeier Find-It 3a: CheckText(GapState,GapId)
2398 // JMatch HP5 v3, v5, v6: CheckAnswer(), HP5 v4: CheckResults(), HP6: CheckAnswers()
2399 // JMix CheckAnswer(CheckType)
2401 // HP5: CheckAnswer(ShowHint, QNum)
2402 // HP6: CheckMCAnswer, CheckMultiSelAnswer, CheckShortAnswer
2403 // Rhubarb CheckWord(InputWord)
2404 // Sequitur CheckAnswer(Chosen, Btn)
2405 // HP6 JQuiz has three "Check Answer" functions
2406 var f
= new Array('CheckMCAnswer', 'CheckMultiSelAnswer', 'CheckShortAnswer');
2407 for (var i
=0; i
<f
.length
; i
++) {
2408 if (eval('window.' + f
[i
])) {
2409 var a
= getFuncArgs(f
[i
], true);
2411 if (f
[i
]=='CheckMCAnswer') {
2412 x
+= "var args=new Array(QNum,I[QNum][3][ANum][0]);";
2413 } else if (f
[i
]=='CheckShortAnswer') {
2415 + "var obj=document.getElementById('Q_'+QNum+'_Guess');"
2416 + "var args=new Array(QNum,obj.value);"
2418 } else if (f
[i
]=='CheckMultiSelAnswer') {
2421 + "for (var ANum=0; ANum<I[QNum][3].length; ANum++){"
2422 + "var obj=document.getElementById('Q_'+QNum+'_'+ANum+'_Chk');"
2423 + "if (obj.checked)g+=(g?'+':'')+I[QNum][3][ANum][0];"
2425 + "var args=new Array(QNum,g);"
2429 x
= "if(!Finished&&State[QNum].length&&State[QNum][0]<0){" + x
+ "hpClick(3,args)}";
2430 var s
= getFuncCode(f
[i
], x
, '', true);
2431 hpNewFunction(f
[i
], a
, s
);
2435 var f
= ''; // function name
2436 var x
= ''; // extra code, if any
2437 if (window
.CheckAnswer
) {
2439 var a
= getFuncArgs(f
, true);
2440 if (a
[0]=='ShowHint') {
2442 // JQuiz v3, v5-v6[HP5]
2443 x
= 'if(!ShowHint&&Status[QNum][0]<1)hpClick(3,QNum);';
2446 x
= 'if(!ShowHint&&State[QNum-1][0]<1)hpClick(3,QNum-1);'; // QNum is a global variable
2448 } else if (a
[0]=='CheckType') {
2450 x
= 'if(CheckType==0)hpClick(3,0);'; // question number is always 0;
2451 } else if (a
[0]=='Chosen') {
2453 x
= 'if (!(CurrentNumber==TotalSegments||AllDone||Btn.innerHTML==IncorrectIndicator))hpClick(3,Chosen);';
2455 } else if (window
.CheckWord
) {
2457 var a
= getFuncArgs(f
, true);
2458 if (a
[0]=='InputWord') {
2460 x
= 'if(!window.AllDone)hpClick(3,InputWord);';
2462 } else if (window
.CheckText
&& !window
.CheckAnswers
) { // Rottmeier Find-It (3a)
2464 var a
= getFuncArgs(f
, true);
2465 if ((a
[0]=='bool' && a
[1]=='item') || (a
[0]=='GapState' && a
[1]=='GapId')) {
2466 x
= 'if(!window.Finished&&'+a
[0]+')hpClick(3,'+a
[1]+');';
2470 var s
= getFuncCode(f
, x
, '', true);
2471 hpNewFunction(f
, a
, s
);
2473 // JMatch has three possible check functions, depending on the version
2474 // (NB: other quiz types also have these functions)
2475 var f
= new Array('CheckAnswers', 'CheckAnswer', 'CheckResults');
2476 for (var i
=0; i
<f
.length
; i
++) {
2477 if (eval('window.' + f
[i
])) {
2478 var a
= getFuncArgs(f
[i
], true);
2480 var s
= getFuncCode(f
[i
], "hpClick(3);", '', true);
2481 hpNewFunction(f
[i
], a
, s
);
2482 break; // out of the loop
2490 // add Array.push if required (allows v6 quizzes to run on ie5win)
2491 if (Array
.prototype && Array
.prototype.push
==null) {
2492 Array
.prototype.push
= new Function("x", "this[this.length]=x");
2494 // add attachEvent function, if required (allows HP5 v6 quizzes to run on ie5mac)
2495 // NOTE: to allow v6 quizzes on ie5mac, the following code
2496 // needs to be inserted BEFORE the Hot Potatoes javascript
2497 if (window
.attachEvent
==null) {
2498 window
.attachEvent
= new Function('evt', 'fn', 'eval("window."+evt+"="+fn)');
2500 if (document
.attachEvent
==null) {
2501 document
.attachEvent
= new Function('evt', 'fn', 'eval("document."+evt+"="+fn)');
2503 // fix the ShowMessage function for NS6
2504 // by removing calls to a button's "focus()" method
2505 if (navigator
.userAgent
.indexOf("Netscape6")>=0 && window
.ShowMessage
) {
2506 var s
= ShowMessage
.toString();
2507 var r
= new RegExp('document\\.getElementById\\((\'|")FeedbackOKButton(\'|")\\)\\.focus\\(\\);', 'gi');
2508 s
= s
.substring(s
.indexOf('{')+1, s
.lastIndexOf('}')).replace(r
, '');
2509 window
.ShowMessage
= new Function('Feedback', s
);
2511 // ns6.0 (in JMix at least) has an error in the FocusAButton function too
2512 // this could be fixed as follows ...
2513 //if (window.FocusAButton) {
2514 // window.FocusAButton = new Function('return true');
2516 // however, ns6.0 then crashes completely when the mouse moves over a link, so don't bother
2517 // Hot Potatoes quiz sniffing
2519 // JBC uses "QuizForm", which contains elements called "Q*_**" (* and ** start at 1)
2520 // JCloze uses "Cloze" form
2521 // JCross uses "Crossword" form
2522 // JMatch uses "QuizForm", which contains elements called "1,2,3..x" and "x+1,x+2...",
2523 // and "CheckForm" form, which contains an element called "ScoreBox"
2524 // it is also the only HP quiz type to use an array called "CorrectAnswers"
2525 // JQuiz uses "QForm*" forms (* starts at 1), which each contain an element called "Guess"
2527 // JBC uses "QForm" form in "QuestionDiv", which contains elements called "FB* (* starts at 0)"
2528 // JCloze uses "Cloze" form in "QuestionDiv"
2529 // JCross uses "Crossword" form in "CWDiv"
2530 // JMatch uses "ExCheck" form in "TitleDiv"
2532 // JQuiz uses "QForm" form in "QuestionDiv", which contains an element called "Answer"
2534 // JBC uses "QForm" form, which contains elements called "FB_*_**" (* and ** start at 0)
2535 // JCloze uses "Cloze" form
2536 // JCross writes out "AnswerForm" from a variable called "GetAnswerOpener"
2537 // HP5.3: uses "AnswerForm" in "BottomFrame"
2538 // HP5.5: uses "AnswerForm" in "TopFrame", but it is only there when an answer is being input
2539 // JMatch uses "QForm" form, which contains elements called "sel*" (which disappear by the time the quiz is finished)
2540 // JMix uses "ButtonForm"
2541 // JQuiz uses "QForm*" and "Buttons*" (one for each question)
2543 // JBC uses "QForm" form (elements have no name or id)
2544 // JCloze uses "Cloze" form (elements have no name or id)
2545 // JCross does not use any forms,
2546 // HP5: has "GridDiv" in "MainDiv"
2547 // HP6: has "Clues" table in "MainDiv"
2548 // JMatch has "MatchDiv" in "MainDiv"
2549 // HP5: uses "QForm" form, which contains elements called "sel*"
2550 // HP6: uses "QForm" form, which contains elements called "s*_**"
2551 // JMix does not use any forms, but has "SegmentDiv" in "MainDiv"
2553 // HP5: uses "QForm" form, which contains an element called "Guess"
2554 // HP6: has "Questions" ordered list in "MainDiv"
2556 // JMatch has DIVs called "D*" and "F*" (* starts at 0)
2557 // JMix has DIVs called "D*" and "Drop*" (* starts at 0)
2558 // useful sniffing tools (Cut and Paste to browser address box)
2559 //javascript:var s="";var x=new quiz_obj();for(X in x)s+=","+X+"="+x[X];alert(s.substring(1));
2560 //javascript:var s="";var x=document.layers;for(var i=0;i<x.length;i++)s+=","+x[i].name;alert(s.substring(1))
2561 //javascript:var s="";var x=document.forms;for(var i=0;i<x.length;i++)s+=","+x[i].id;alert(s.substring(1))
2562 //javascript:var s="";var x=document.forms;for(var i=0;i<x.length;i++)s+=","+x[i].name;alert(s.substring(1))
2563 //javascript:var s="";var x=document.forms.QForm.elements;for(var i=0;i<x.length;i++)s+=","+x[i].id;alert(s.substring(1))
2564 //javascript:var s="";var x=document.forms.QForm.elements;for(var i=0;i<x.length;i++)s+=","+x[i].name;alert(s.substring(1))
2565 function hpDetectQuiz() {
2566 // "sniff" (=detect) the quiz's type and intended browser version
2567 // and cache the values in a global variable called "quiz"
2568 // Hot Potatoes version
2569 // 5 : HP5.3 (mac) or HP5.5 (win)
2570 // 6 : HP6.0 (mac) or HP6.0 (win)
2571 // intended browser version
2572 // 3 : ns3, ie3 (frames)
2573 // 4 : ns4, ie4 (cross browser dhtml)
2574 // 5 : ie5 (frames, send results via CGI)
2575 // 6 : ie6, op7, gecko (w3 standards)
2576 // 6.1 : "drag and drop" versions of JMatch and JMix v6
2585 // 7 : rhubarb (TexToys)
2586 // 8 : Sequitur (TexToys)
2587 // rottmeier quiz type
2588 // 1 : drop-down (JCloze)
2589 // 2 : find-it (JCloze)
2590 // shortcut to window object
2592 // create the global "quiz" object, if necessary
2593 if (!w
.quiz
) w
.quiz
= new Object();
2594 // Hot Potatoes version
2596 // HP5 v4-v5: BrowserCheck()
2597 // v3: WinStringToMac() [JCloze, JCross, JQuiz]
2598 // v3: winrightchar [JBC, JMatch]
2599 // v3: DownTime() [JBC, JCloze, JQuiz]
2601 quiz
.hp
= (w
.Client
) ? 6 : (w
.BrowserCheck
) ? 5 : (w
.WinStringToMac
|| w
.winrightchar
) ? 5 : -1;
2603 // check the version and type are not already set
2604 if (!quiz
.v
|| !quiz
.t
) {
2605 // initialize version and type
2608 // set shortcuts to DOM objects
2611 if (f
.QuizForm
&& f
.CheckForm
&& w
.CorrectAnswers
) {
2614 } else if (w
.FeedbackFrame
&& w
.CodeFrame
) {
2616 f
= CodeFrame
.document
.forms
;
2617 t
= (f
.QuizForm
) ? 1 : (f
.Cloze
) ? 2 : (f
.Crossword
) ? 3 : (f
.QForm1
) ? 6 : 0;
2618 } else if (w
.DynLayer
) {
2621 // for NS4, adjust "f" to point to a forms object in a layer
2622 var lyr
= d
.QuestionDiv
|| d
.CWDiv
|| d
.TitleDiv
|| null;
2623 if (lyr
) f
= lyr
.document
.forms
;
2625 t
= (f
.QForm
&& f
.QForm
.FB0
) ? 1 : (f
.Cloze
) ? 2 : (f
.Crossword
) ? 3 : (f
.ExCheck
) ? 4 : (f
.QForm
&& f
.QForm
.Answer
) ? 6 : 0;
2626 } else if (w
.TopFrame
&& w
.BottomFrame
) {
2628 f
= BottomFrame
.document
.forms
;
2629 t
= (f
.QForm
&& f
.QForm
.elements
[0].name
.substring(0,3)=='FB_') ? 1 : (f
.Cloze
) ? 2 : (w
.GetAnswerOpener
&& GetAnswerOpener
.indexOf('AnswerForm')>=0) ? 3 : (f
.QForm
&& w
.RItems
) ? 4 : (f
.ButtonForm
) ? 5 : (f
.QForm0
&& f
.Buttons0
) ? 6 : 0;
2630 } else if (hpObj(d
, 'MainDiv')) {
2632 var obj
= (f
.QForm
) ? f
.QForm
.elements
: null;
2633 t
= (obj
&& obj
.length
>0 && obj
[0].id
=='') ? 1 : (f
.Cloze
) ? 2 : (hpObj(d
, 'GridDiv') || hpObj(d
, 'Clues')) ? 3 : hpObj(d
, 'MatchDiv') ? 4 : hpObj(d
, 'SegmentDiv') ? 5 : ((f
.QForm
&& f
.QForm
.Guess
) || hpObj(d
, 'Questions')) ? 6 : 0;
2634 } else if (hpObj(d
, 'D0')) {
2635 v
= 6.1; // drag and drop (HP5 and HP6)
2636 t
= (hpObj(d
, 'F0')) ? 4 : (hpObj(d
, 'Drop0')) ? 5 : 0;
2637 } else if (w
.Words
&& f
.Rhubarb
) {
2639 t
= 7; // rhubarb (TexToys)
2640 } else if (w
.Segments
&& hpObj(d
, 'Story')) {
2642 t
= 8; // sequitur (TexToys)
2644 quiz
.v
= v
; // intended browser version
2645 quiz
.t
= t
; // quiz type
2648 function hpRottmeier() {
2650 if (typeof(quiz
.r
)=='undefined') { // first-time only
2652 if (quiz
.t
==2) { // JCloze
2653 if (quiz
.hp
==5) { // HP5
2655 } else if (quiz
.hp
==6) { // HP6
2656 if (window
.Create_StateArray
) { // Rottmeier
2657 var obj
= new Create_StateArray();
2658 if (typeof(obj
.GapLocked
)=='boolean') {
2659 quiz
.r
= 1; // drop-down (v2.4)
2660 } else if (typeof(obj
.ErrorFound
)=='boolean') {
2661 if (typeof(obj
.GapSolved
)!='boolean') {
2662 quiz
.r
= 2.1; // find-it (v3.1a)
2664 quiz
.r
= 2.2; // find-it (v3.1b)
2667 obj
= null; // prevents memory leakage on some versions of IE
2674 function hpVersion() {
2678 function hpQuizType() {
2682 function hpQuizVersion() {
2686 function hpScoreEngine(score_i
, a
, s
, aa
, ss
, count_c
, count_i
) {
2687 // calculate the score for the quiz so far
2688 // score_i : amount by which to increment "score"
2690 // s : condition, if any, on outer array (=a)
2691 // if true, the score will be incremented by "score_i"
2692 // aa : inner array, if any
2693 // ss : condition, if any, on inner array (=aa)
2694 // count_c : condition, if any, on which "count" is to be incremented
2695 // count_i : amount by which to increment "count"
2696 // "a" and "aa" may be passed as arrays or strings containing the name of an array
2697 // "s" and "ss" are strings containing an expression to be eval(uated)
2698 // "score_i", "count_i" and "count_c" strings containing an expression to be eval(uated)
2701 // set default condition to increment "count", and amount by which to increment the count
2702 if (count_c
==null) count_c
= "true";
2703 if (count_i
==null) count_i
= "1";
2704 // set length of outer array. if any
2705 var l
= (typeof(a
)=="string") ? eval(a
+ ".length") : a
? a
.length
: 0;
2706 // loop through outer array
2707 for (var i
=0; i
<l
; i
++) {
2708 if (s
==null && ss
==null) {
2709 score
+= eval(score_i
);
2710 if (eval(count_c
)) count
+= eval(count_i
);
2712 score
+= eval(s
) ? eval(score_i
) : 0;
2713 if (eval(count_c
)) count
+= eval(count_i
);
2715 // set length of inner array, if any
2716 var ll
= (typeof(aa
)=="string") ? eval(aa
+ ".length") : aa
? aa
.length
: 0;
2717 // loop through inner array. checking inner condition
2718 for (var ii
=0; ii
<ll
; ii
++) {
2719 score
+= eval(ss
) ? eval(score_i
) : 0;
2720 if (eval(count_c
)) count
+= eval(count_i
);
2725 // get p(enalties) for JCross and JMatch (and JMix ?)
2726 if (window
.Penalties
) {
2727 score
-= (Penalties
- (hpFinished() ? 0 : 1));
2729 // adjust count for Find-It 3a and 3b
2730 if (window
.TotWrongChoices
) {
2731 if (window
.CheckText
&& !window
.CheckAnswers
) { // Find-It 3a
2732 // this seems a little odd, but will replicate behavior of CalculateScore()
2733 count
= score
+ TotWrongChoices
;
2735 count
+= TotWrongChoices
;
2738 score
= Math
.floor(100*score
/count
);
2739 if (score
<0) { // just in case
2745 function hpScore() {
2746 var x
= ''; // score
2747 var hp
= hpVersion();
2748 var t
= hpQuizType();
2749 var v
= hpQuizVersion();
2751 if (v
==3) x
= hpScoreEngine(1, DoneStatus
, "i>0 && a[i]=='0'"); // doesn't work
2752 else if (v
==4) x
= hpScoreEngine(1, DoneStatus
, "a[i]==0"); // doesn't work
2753 else if (v
==5 || v
==6) x
= hpScoreEngine("a[i][3]", Status
, "a[i][3]");
2754 } else if (t
==2) { // jcloze
2755 if (v
==3 || v
==4) x
= hpScoreEngine("a[i]", Scores
);
2756 else if (hp
==5) x
= hpScoreEngine("a[i][3]", State
); // v==5 && v==6
2758 var r
= hpRottmeier();
2759 if (r
==0) x
= x
= hpScoreEngine("a[i].ItemScore", State
);
2760 else if (r
==1) x
= hpScoreEngine("a[i][1].Score", GapList
, "a[i][1].GapLocked"); // dropdown
2761 else if (r
==2.1) x
= hpScoreEngine(1, GapList
, "a[i][1].ErrorFound"); // Find-It 3a
2762 else if (r
==2.2) x
= hpScoreEngine("a[i][1].Score", GapList
, "a[i][1].GapSolved"); // Find-It 3b
2764 } else if (t
==3) { // jcross
2765 if (v
==3) x
= hpScoreEngine(1, CorrectAnswers
, "document.QuizForm.elements[i*2].selectedIndex==a[i]");
2766 else if (v
==4) x
= hpScoreEngine(1, WinLetters
, "ConvertCase(GetBoxValue(i),1).charAt(0)==a[i].charAt(0)");
2767 else if (v
==5 || v
==6) {
2768 if (window
.CaseSensitive
) { // HP 6.2
2769 x
= hpScoreEngine(1, L
, "", "L[i]", "L[i][ii] && L[i][ii]==G[i][ii]", "L[i][ii]");
2771 x
= hpScoreEngine(1, L
, "", "L[i]", "L[i][ii] && L[i][ii].toUpperCase()==G[i][ii].toUpperCase()", "L[i][ii]");
2774 } else if (t
==4) { // jmatch
2775 if (v
==3) x
= hpScoreEngine(1, CorrectAnswers
, "document.QuizForm.elements[i*2].selectedIndex==a[i]");
2776 else if (v
==4) x
= hpScoreEngine(1, Draggables
, "a[i].correct=='1'");
2777 else if (v
==5) x
= hpScoreEngine(1, I
, "I[i][2]<1 && I[i][0].length>0 && Status[i][0]==1");
2778 else if (v
==6) x
= hpScoreEngine(1, Status
, "Status[i][0]==1");
2779 else if (v
==5.1 || v
==6.1) x
= hpScoreEngine(1, D
, "D[i][2]==D[i][1] && D[i][2]>0", "", "", "i<F.length");
2780 } else if (t
==5) { // jmix
2781 // there was no v3 or v4 of JMix
2782 if (v
==5 || v
==6 || v
==6.1) x
= Math
.floor(100*(Segments
.length
-Penalties
)/Segments
.length
);
2783 } else if (t
==6) { // jquiz
2785 if (v
==3 || v
==4) x
= hpScoreEngine("a[i][4]/10", State
, "a[i][0]==1");
2786 else if (v
==5 || v
==6) x
= hpScoreEngine("a[i][4]/10", Status
, "a[i][0]==1", "", "", "true", "1");
2788 if (v
==6) x
= hpScoreEngine("I[i][0]*a[i][0]", State
, "a[i]&&a[i][0]>=0", "", "", "a[i]", "I[i][0]");
2790 } else if (t
==7) { // rhubarb
2792 x
= Math
.floor(100*Correct
/TotalWords
);
2794 } else if (t
==8) { // sequitur
2796 var myTotalPoints
= TotalPoints
- (hpFinished() ? 0 : (OptionsThisQ
-1));
2797 x
= Math
.floor(100*ScoredPoints
/myTotalPoints
);
2802 function hpFinishedEngine(a
, s
, aa
, ss
) {
2803 // determine whether or not all quistions in a quiz are finished
2805 // s : condition, if any, on outer array
2806 // if true for any element in "a", the quiz is NOT finished
2807 // aa : inner array, if any
2808 // ss : condition, if any, on inner array
2809 // if true for any element in "aa", the quiz is NOT finished
2810 // the arrays "a" and "aa" may be passed as arrays or strings to be eval(uated)
2811 // the conditions "s" and "ss" are specified as strings to be eval(uated)
2812 // assume a positive result
2814 // set length of outer array. if any
2815 var l
= (typeof(a
)=="string") ? eval(a
+ ".length") : a
? a
.length
: 0;
2816 // loop through outer array
2817 for (var i
=0; i
<l
; i
++) {
2818 // do outer condition, if any
2819 if (s
&& eval(s
)) x
= false;
2820 // set length of inner array, if any
2821 var ll
= (typeof(aa
)=="string") ? eval(aa
+ ".length") : aa
? aa
.length
: 0;
2822 // loop through inner array. checking inner condition
2823 for (var ii
=0; ii
<ll
; ii
++) {
2824 if (ss
&& eval(ss
)) x
= false;
2829 function hpTimedOut() {
2830 // v5 uses "min" and "sec"
2832 return (typeof(self
.Seconds
)=='number' && Seconds
==0) || (typeof(self
.min
)=='number' && min
==0 && typeof(self
.sec
)=='number' && sec
==0);
2834 function hpFinished() {
2835 // assume false result
2837 var hp
= hpVersion();
2838 var t
= hpQuizType();
2839 var v
= hpQuizVersion();
2841 if (v
==3) x
= hpFinishedEngine(DoneStatus
, "i>0 && a[i]=='0'");
2842 else if (v
==4) x
= hpFinishedEngine(DoneStatus
, "a[i]==0");
2843 else if (v
==5 || v
==6) x
= hpFinishedEngine(Status
, "a[i][0]==0");
2844 } else if (t
==2) { // jcloze
2845 var r
= hpRottmeier();
2846 if (r
==1) x
= hpFinishedEngine(GapList
, "a[i][1].GapLocked==false"); // drop-down
2847 else if (r
==2.1) x
= hpFinishedEngine(GapList
, "a[i][1].ErrorFound==false"); // find-it 3a
2848 else if (r
==2.2) x
= hpFinishedEngine(GapList
, "a[i][1].GapSolved==false"); // find-it 3b
2849 else if (v
==3 || v
==4 || v
==5 || v
==6) x
= hpFinishedEngine(I
, "CheckAnswer(i)==-1");
2850 // also: else if (v==5 || v==6) x = hpFinishedEngine(State, "a[i][4]!=1")
2851 } else if (t
==3) { // jcross
2852 if (v
==3) x
= hpFinishedEngine(document
.Crossword
.elements
, "ConvertCase(is.mac?unescape(MacStringToWin(a[i].value)):a[i].value,1)!=Letters[i]");
2853 else if (v
==4) x
= hpFinishedEngine(WinLetters
, "ConvertCase(GetBoxValue(i),1).charAt(0) != a[i].charAt(0)");
2854 else if (v
==5 || v
==6) {
2855 if (window
.CaseSensitive
) { // 6.2
2856 x
= hpFinishedEngine(L
, "", "L[i]", "L[i][ii] && L[i][ii]!=G[i][ii]");
2858 x
= hpFinishedEngine(L
, "", "L[i]", "L[i][ii] && L[i][ii].toUpperCase()!=G[i][ii].toUpperCase()");
2861 } else if (t
==4) { // jmatch
2862 if (v
==3) x
= hpFinishedEngine(CorrectAnswers
, "document.QuizForm.elements[i*2].selectedIndex != a[i]");
2863 else if (v
==4) x
= hpFinishedEngine(Draggables
, "a[i].correct!='1'");
2864 else if (v
==5) x
= hpFinishedEngine(I
, "I[i][2]<1 && I[i][0].length>0 && Status[i][0]<1 && GetAnswer(i)!=I[i][3]");
2865 else if (v
==6) x
= hpFinishedEngine(Status
, "Status[i][0]<1");
2866 else if (v
==5.1 || v
==6.1) x
= hpFinishedEngine(D
, "", F
, "F[ii][1]==D[i][1]&&D[i][1]!=D[i][2]");
2867 } else if (t
==5) { // jmix
2868 // there was no v3 or v4 of JMix
2869 if (v
==5 || v
==6 || v
==6.1) x
= !hpFinishedEngine(Answers
, "a[i].join(',')=='" + GuessSequence
.join(',') + "'");
2870 } else if (t
==6) { // jquiz
2871 if (v
==3 || v
==4) x
= hpFinishedEngine(State
, "a[i][0]==0");
2872 else if (v
==5 || v
==6) {
2873 if (hp
==5) x
= hpFinishedEngine(Status
, "a[i][0]<1");
2874 else if (hp
==6) x
= hpFinishedEngine(State
, "a[i] && a[i][0]<0");
2876 } else if (t
==7) { // rhubarb
2877 if (v
==6) x
= hpFinishedEngine(DoneList
, "a[i]==1");
2878 } else if (t
==8) { // sequitur
2879 if (v
==6) x
= (CurrentNumber
==TotalSegments
|| AllDone
);
2883 function hpObj(d
, id
) {
2884 return d
.getElementById
? d
.getElementById(id
) : d
.all
? d
.all
[id
] : d
[id
];
2886 function GetViewportHeight() {
2887 if (window
.innerHeight
) {
2891 return document
.documentElement
.clientHeight
;
2893 return document
.body
.clientHeight
;
2897 function hpIsStrict() {
2898 if (!window
.hpStrictIsSet
) {
2899 window
.hpStrictIsSet
= true;
2900 window
.hpStrict
= false;
2901 var s
= document
.compatMode
;
2902 if (s
&& s
=="CSS1Compat") { // ie6
2903 window
.hpStrict
= true;
2905 var obj
= document
.doctype
;
2907 var s
= obj
.systemId
|| obj
.name
; // n6 || ie5mac
2908 if (s
&& s
.indexOf("strict.dtd") >= 0) {
2909 window
.hpStrict
= true;
2914 return window
.hpStrict
;
2919 //hpInterceptFeedback();
2920 //hpInterceptHints();
2921 //hpInterceptClues();
2922 hpInterceptChecks();
2923 function hpFindForm(formname
, w
) {
2924 if (w
==null) w
= self
;
2925 var f
= w
.document
.forms
[formname
];
2926 if (f
==null && w
.frames
) {
2927 for (var i
=0; i
<w
.frames
.length
; i
++) {
2928 f
= hpFindForm(formname
, w
.frames
[i
]);
2934 function Finish(quizstatus
) {
2935 var mark
= hpScore();
2936 window
.hpForm
= hpFindForm('store');
2937 if (hpForm
) { // LMS
2938 hpForm
.starttime
.value
= getTime(Start_Time
);
2939 hpForm
.endtime
.value
= getTime();
2940 hpForm
.mark
.value
= mark
;
2941 hpForm
.detail
.value
= '<?xml version="1.0"?><hpjsresult><fields>'+GetQuestionDetails()+'</fields></hpjsresult>';
2942 if (hpForm
.status
) {
2944 // 4=completed, 3=abandoned, 2=timed-out or 1=in-progress
2945 quizstatus
= hpFinished() ? 4 : hpTimedOut() ? 2 : 1;
2947 hpForm
.status
.value
= quizstatus
;
2949 if (!window
.hpQuizResultsSent
) {
2950 if (hpForm
.status
&& quizstatus
==4) {
2951 window
.hpQuizResultsSent
= true;
2953 if (quizstatus
==4) { // completed
2954 // wait 2 seconds for student to see feedback
2955 setTimeout("hpForm.submit();", 2000);
2960 } else if (hpFinished()) {
2961 SendAllResults(mark
);
2964 // create form to send results
2965 if (DB
[7] && DB
[8] && !is_LMS()) {
2968 + '<form name="Results" action="" method="post" enctype="x-www-form-encoded">'
2969 + hpHiddenField('recipient', '')
2970 + hpHiddenField('subject', '')
2971 + hpHiddenField('Exercise', '')
2972 + hpHiddenField('realname', '')
2973 + hpHiddenField('Score', '')
2974 + hpHiddenField('Start_Time', '')
2975 + hpHiddenField('End_Time', '')
2976 + hpHiddenField('title', 'Thanks!')
2981 // reassign the StartUp function
2982 var p
= getPrompt(window
.GetUserName
|| window
.StartUp
);
2983 var c
= getStartUpCode(window
.StartUp
);
2985 if (window
.C
&& C
.safari
) {
2986 eval('window.StartUp=function(){QuizLogin("' + p
+ '")}');
2987 eval('window.StartQuiz=function(){' + c
+ '}');
2989 window
.StartUp
= new Function('QuizLogin("' + p
+ '")');
2990 window
.StartQuiz
= new Function(c
);
2992 // "QuizLogin" finshes by calling "StartQuiz"
2994 // reassign the SendResults function
2995 window
.SendResults
= SendAllResults
;
2997 var Start_Time
= new Date();