mirror of
https://github.com/moodle/moodle.git
synced 2025-02-13 12:34:28 +01:00
1980 lines
63 KiB
JavaScript
1980 lines
63 KiB
JavaScript
<!--
|
|
// PLEASE NOTE that this version is more recent than the incorrectly
|
|
// numbered v6.1, dated 2003.11.17. From now on, version numbers will
|
|
// follow those of Hot Potatoes.
|
|
|
|
/* hot-potatoes.js (v6.0.4.0 - 2005.02.18)
|
|
* =======================================
|
|
* by Gordon Bateson, February 2003
|
|
* Copyright (c) 2003 Gordon Bateson. All Rights Reserved.
|
|
*
|
|
* You are hereby granted a royalty free license to use or modify this
|
|
* software provided that this copyright notice appears on all copies.
|
|
*
|
|
* This software is provided "AS IS" without a warranty of any kind.
|
|
*
|
|
* Documentation and downloads may be available from:
|
|
* http://www.kanazawa-gu.ac.jp/~gordon/research/hot-potatoes/
|
|
*/
|
|
|
|
// This JavaScript library modifies the SendResults and StartUp functions
|
|
// used by hotpot v5 and v6, so that more (or less!) details about the
|
|
// student can be input, and more details of a quiz's questions and answers
|
|
// can be submitted to the server when the quiz is finished
|
|
|
|
// If the arrays below (Login, DB, JBC, ...) are set BEFORE calling this
|
|
// script, they will NOT be overwritten. Any array that is not set, will
|
|
// use the defaults below. This is useful if you want to use different
|
|
// settings for different quizzes.
|
|
|
|
// ************
|
|
// Login Screen
|
|
// ************
|
|
|
|
if (window.Login==null) {
|
|
Login = new Array();
|
|
Login[0] = true; // Show prompt for user name
|
|
// This can also be a string of user names ...
|
|
// Login[0] = "Guest,Peter,Paul,Mary,Webmaster";
|
|
// or an array of user names (and on-screen texts) (and passwords) ...
|
|
// Login[0] = new Array("Guest", "001,Peter,xxxx", "002,Paul,yyyy", "003,Mary,zzzz", "Webmaster");
|
|
// and can also be written as ...
|
|
// Login[0] = new Array(
|
|
// new Array("Guest"),
|
|
// new Array("001", "Peter", "xxxx"),
|
|
// new Array("002", "Paul", "yyyy"),
|
|
// new Array("003", "Mary", "zzzz"),
|
|
// new Array("Webmaster")
|
|
// );
|
|
Login[1] = true; // Show prompt for student's UserID
|
|
// If there is no password prompt (i.e. Logon[3] is false), this value
|
|
// will be checked against the password information, if any, in Login[0]
|
|
Login[2] = false; // Show prompt for student's email
|
|
Login[3] = false; // Show prompt for quiz password, and check this value against
|
|
// the password information, if any, in Login[0]
|
|
// This can also be a string required to start the quiz ...
|
|
// Login[3] = "password";
|
|
Login[4] = true; // Show prompt for the cookie expiry date
|
|
// If false, cookies expire at the end of the current session
|
|
Login[5] = "guest,webmaster"
|
|
// guest user names (case insensitive) ...
|
|
// Login[5] = "guest,webmaster";
|
|
// These users do NOT need to fill in other login fields
|
|
// and their quiz results are NOT added to the database
|
|
|
|
// the Login prompts and error messages
|
|
// are defined in the MSG array (see below)
|
|
}
|
|
|
|
|
|
// *********
|
|
// Database (for use with BFormMail)
|
|
// *********
|
|
|
|
if (window.DB==null) {
|
|
DB = new Array();
|
|
DB[0] = true; // append form fields to database on server
|
|
// If you are NOT using BFormMail's database feature,
|
|
// set DB[0]=false, and you can then safely ignore DB[1 to 5]
|
|
DB[1] = "/home/gordon/public_html/cgi/hot-potatoes-data";
|
|
// append_db folder path (no trailing slash)
|
|
// Can be either an absolute path e.g. "/home/gordon/public_html/cgi/hot-potatoes-data"
|
|
// or a relative (to CGI bin) path e.g. "hot-potatoes-data"
|
|
DB[2] = "hot-potatoes";
|
|
// append_db file name (no extension)
|
|
// If left blank, the quiz file name, without extension, will be used
|
|
// i.e. each quiz will have its results stored in a different file.
|
|
// If filled in, this file will store the results for ALL quizzes.
|
|
// Database files and folders must be set up BEFORE running the quiz
|
|
// must have appropriate access privileges (on Unix, use "chmod 666").
|
|
DB[3] = ""; // append_db extension (if left blank, ".txt" will be used)
|
|
DB[4] = ""; // db_fields (if left blank, ALL quiz fields will be sent)
|
|
DB[5] = ""; // db_delimiter (if left blank, tab will be used)
|
|
DB[6] = "REMOTE_ADDR,HTTP_USER_AGENT";
|
|
// env_report ('REMOTE_ADDR','HTTP_USER_AGENT' and a few others)
|
|
|
|
// for a complete description of these fields are, see ...
|
|
// http://www.infosheet.com/stuff/BFormMail.readme
|
|
|
|
// Switches DB[7] and DB[8] force the settings in the ResultForm
|
|
// In v5 and v6 quizzes, these settings wil be override those in the original quiz
|
|
|
|
// If the quiz results are to be sent to an LMS (via the "store" form)
|
|
// then switches DB[7] and DB[8] are not used
|
|
|
|
DB[7] = ''; // URL of form processing script
|
|
// e.g. http://www.kanazawa-gu.ac.jp/~gordon/cgi/bformmail.cgi
|
|
DB[8] = ''; // email address to which results should be sent
|
|
// e.g. gordon@kanazawa-gu.ac.jp
|
|
}
|
|
|
|
// By default the quiz's question's scores will be returned.
|
|
// If you want more detailed information, set the flags below:
|
|
|
|
// ********
|
|
// JBC
|
|
// ********
|
|
|
|
if (window.JBC==null) {
|
|
JBC = new Array();
|
|
JBC[0] = true; // show separator line between answers on email
|
|
JBC[1] = true; // show number of attempts to answer question
|
|
JBC[2] = true; // show question texts
|
|
JBC[3] = true; // show right answer(s)
|
|
JBC[4] = true; // show wrong answer(s)
|
|
JBC[5] = true; // show ignored answer(s)
|
|
JBC[6] = false; // show answer as text (false) or number (true)
|
|
}
|
|
|
|
// JBC quizzes use the global variables 'I' and 'Status'
|
|
|
|
// I : an array of JBC_QUESTIONs (one for each question)
|
|
// JBC_QUESTION :
|
|
// [0] : question text
|
|
// [1] : array of JBC_ANSWERs (one for each answer)
|
|
// [2] : single/multi flag
|
|
// 0 : single answer (using 'button')
|
|
// 1 : multiple answers (using 'checkbox')
|
|
// JBC_ANSWER :
|
|
// [0] : answer text
|
|
// [1] : answer feedback
|
|
// [2] : correct answer flag
|
|
// 0 : this is NOT the correct answer
|
|
// 1 : this is the correct answer
|
|
|
|
// Status : an array of JBC_QUESTION_STATUSes
|
|
// JBC_QUESTION_STATUS:
|
|
// [0] : correctly answered yet flag
|
|
// 0 : this question has NOT been correctly answered
|
|
// 1 : this question has been correctly answered
|
|
// [1] : array of JBC_ANSWER_STATUSes (one for each answer)
|
|
// '0' : initial value
|
|
// 'R' : single answer question was answered 'R'ight
|
|
// 'W' : single answer question was answered 'W'rong
|
|
// 'C' : multiple answer question's checkbox was 'C'hecked
|
|
// 'U' : multiple answer question's checkbox was 'U'nchecked
|
|
// [2] : number of times this question has been wrongly answered
|
|
// [3] : score (out of 1) for this question (maybe undefined on HP<5.5)
|
|
// 0 : not correct yet
|
|
// 0<[3]<1 : correct but only after [2] wrong attempts
|
|
// 1 : correct first time (bravo!)
|
|
// N.B. score = (numberOfAnswers - numberofWrongTries) / numberOfAnswers
|
|
|
|
|
|
// ********
|
|
// JCloze
|
|
// ********
|
|
|
|
if (window.JCloze==null) {
|
|
JCloze = new Array();
|
|
JCloze[0] = true; // show separator line between answers on email
|
|
JCloze[1] = true; // show student's correct answer
|
|
JCloze[2] = true; // show other correct answer(s), if any
|
|
JCloze[3] = true; // show wrong answer(s), if any (NOT available for v5)
|
|
JCloze[4] = true; // show number of hints or penalties
|
|
JCloze[5] = true; // show if clue was asked for or not
|
|
JCloze[6] = true; // show clue
|
|
}
|
|
|
|
// JCloze quizzes use the global variables 'I' and 'State'
|
|
|
|
// I : array of JCLOZE_ANSWERs
|
|
// JCLOZE_ANSWER :
|
|
// [0] : (unused)
|
|
// [1] : array of JCLOZE_ANSWER_TEXTs
|
|
// [2] : clue for this answer
|
|
// JCLOZE_ANSWER_TEXT :
|
|
// [0] : array (seems unnecessary, just the text would be enough?)
|
|
// [0] : text of possible answer
|
|
|
|
// State : array of JCLOZE_ANSWER_STATEs
|
|
// JCLOZE_ANSWER_STATE (v5) :
|
|
// [0] : clue asked for or not
|
|
// [1] : number of hints (show next letter) and penalties ('check' an incorrect answer)
|
|
// [2] : length of answer matched
|
|
// [3] : score for this item
|
|
// [4] : already answered correctly
|
|
// [5] : answer entered in text box (right or not)
|
|
|
|
// JCLOZE_ANSWER_STATE (v6)
|
|
// this.ClueGiven = false;
|
|
// this.HintsAndChecks = 0;
|
|
// this.MatchedAnswerLength = 0;
|
|
// this.ItemScore = 0;
|
|
// this.AnsweredCorrectly = false;
|
|
// this.Guesses = new Array(); last guess is correct answer
|
|
|
|
|
|
// ********
|
|
// JCross
|
|
// ********
|
|
|
|
if (window.JCross==null) {
|
|
JCross = new Array();
|
|
JCross[0] = true; // show separator line between answers on email
|
|
JCross[1] = true; // show number of penalties (hints or checks before complete)
|
|
JCross[2] = true; // show number of letters
|
|
JCross[3] = true; // show answers
|
|
JCross[4] = true; // show clues
|
|
}
|
|
|
|
// JCross quizzes use the following global variables:
|
|
// L : letters (of correct answers)
|
|
// C : clue numbers (CL in v6)
|
|
// G : guesses
|
|
// 'L', 'C' ('CL') and 'G' are all 2-dimensional arrays (rows x cols)
|
|
//
|
|
// v5 quizzes additionally use the following single-dimension arrays
|
|
// A : clues for across (horizontal) words
|
|
// D : clues for down (vertical) words
|
|
|
|
// N.B. form is only sent when all answers are correct so
|
|
// you can't find out what 'wrong' answers were entered
|
|
|
|
|
|
// ********
|
|
// JMatch
|
|
// ********
|
|
|
|
if (window.JMatch==null) {
|
|
JMatch = new Array();
|
|
JMatch[0] = true; // show separator line between answers on email
|
|
JMatch[1] = true; // show number of attempts for each match
|
|
JMatch[2] = true; // show LHS texts
|
|
JMatch[3] = true; // show RHS texts
|
|
}
|
|
|
|
// v5 JMatch quizzes use the global variables 'I' and 'Status' (and 'RItems')
|
|
// v6 JMatch quizzes use only 'Status'
|
|
// v6+ JMatch quizzes use 'F' and 'D' (see below)
|
|
|
|
// I : an array of JMATCH_PAIRs (one for each pair)
|
|
// JMATCH_PAIR :
|
|
// [0] : LHS text
|
|
// [1] : RHS text
|
|
// [2] : fixed (=not jumbled) flag
|
|
// 0 : not fixed
|
|
// 1 : fixed
|
|
// [3] : index in drop down list selection
|
|
|
|
// Status : an array of JMATCH_PAIR_STATUSes
|
|
// JMATCH_PAIR_STATUS:
|
|
// [0] : correctly matched yet flag
|
|
// 0 : this pair has NOT been correctly matched
|
|
// 1 : this pair has been correctly matched
|
|
// [1] : number of times this item has been wrongly matched
|
|
|
|
// v6 quizzes only
|
|
// [2] : id of original SELECT element containing possible matches
|
|
// Note that after matching, this SELECT is removed, so don't try looking for it :-)
|
|
|
|
// v6+ JMatch quizzes use the global variables 'F' and 'D'
|
|
|
|
// F : array of JMATCH_FIXED_ITEMs
|
|
// JMATCH_FIXED_ITEM:
|
|
// [0] : text
|
|
// [1] : tag
|
|
|
|
// D : array of JMATCH_DRAGGABLE_ITEMs
|
|
// JMATCH_DRAGGABLE_ITEM
|
|
// [0] : text
|
|
// [1] : tag of the F item to which it SHOULD be dragged
|
|
// [2] : tag of the F item to which it was dragged (initally 0)
|
|
|
|
// N.B. form is only sent when all answers are correct so
|
|
// you can't find out what 'wrong' answers were entered
|
|
|
|
// ********
|
|
// JMix
|
|
// ********
|
|
|
|
if (window.JMix==null) {
|
|
JMix = new Array();
|
|
JMix[0] = true; // show separator line between answers on email
|
|
JMix[1] = true; // show number of wrong guesses
|
|
JMix[2] = true; // show right answer
|
|
JMix[3] = true; // show wrong answer, if any
|
|
JMix[4] = false; // show answer as text (false) or number (true)
|
|
}
|
|
|
|
// JMix quizzes use the global variables
|
|
// 'Segments', 'GuessSequence' and 'Penalties'
|
|
|
|
// Segments : array of JMix_QUESTIONs
|
|
// JMix_QUESTION:
|
|
// [0] : text
|
|
// [1] : order in sequence
|
|
// [2] : used flag
|
|
// GuessSequence : array of 'order in sequence' numbers
|
|
// Penalties : number of incorrect guesses
|
|
|
|
// ********
|
|
// JQuiz
|
|
// ********
|
|
|
|
if (window.JQuiz==null) {
|
|
JQuiz = new Array();
|
|
JQuiz[0] = true; // show separator line between answers on email
|
|
JQuiz[1] = true; // show question text
|
|
JQuiz[2] = true; // show student's correct answer(s)
|
|
JQuiz[3] = true; // show wrong and ignored answer(s)
|
|
JQuiz[4] = true; // show number of hints requested
|
|
JQuiz[5] = true; // show number of checks of incorrect answers
|
|
|
|
// HP6 v6 quizzes only
|
|
JQuiz[6] = false; // show answer value (false) or A,B,C... index (true)
|
|
JQuiz[7] = true; // show all students answers
|
|
JQuiz[8] = true; // show student's wrong answers
|
|
JQuiz[9] = true; // show ignored answers (not relevant for multi-select questions)
|
|
JQuiz[10] = true; // show score weightings
|
|
JQuiz[11] = true; // show question type
|
|
}
|
|
|
|
// v5 JQuiz quizzes use the global variables 'I' and 'Status'
|
|
|
|
// I : array of JQUIZ_ANSWERs
|
|
// JQUIZ_ANSWER :
|
|
// [0] : question text
|
|
// [1] : array of JQUIZ_ANSWER_TEXTs (one for each answer)
|
|
// JQUIZ_ANSWER_TEXT :
|
|
// [0] : array (seems unnecessary, just the text would be enough?)
|
|
// [0] : text of possible answer
|
|
|
|
// Status : array of JQUIZ_ANSWER_STATEs
|
|
// JQUIZ_ANSWER_STATE :
|
|
// [0] : question done or not
|
|
// [1] : number of wrong checks
|
|
// [2] : number of hints asked for
|
|
// [3] : student's answer
|
|
// [4] : score for this question
|
|
|
|
|
|
// v6 JQuiz quizzes use the global variables 'I' and 'State'
|
|
|
|
// I : array of JQUIZ_QUESTIONs
|
|
// JQUIZ_QUESTION :
|
|
// [0] : weighting
|
|
// [1] : ?? (always set to '')
|
|
// [2] : question type
|
|
// '0'=multiple-choice, '1'=short-answer, '2'=hybrid, '3'=multi-select
|
|
// [3] : array of JQUIZ_ANSWERSs (one for each possible answer)
|
|
// JQUIZ_ANSWER :
|
|
// [0] : answer value
|
|
// [1] : feedback text
|
|
// [2] : correct answer flag (1=a correct answer, 0=a wrong answer)
|
|
// [3] : weighted score (as percentage) if correct
|
|
// [4] : flag (usually set to 1, but for hybrid answers that are not
|
|
// to be included in multiple choice options, it is set to 0)
|
|
|
|
// State : array of JQUIZ_QUESTION_STATEs
|
|
// JQUIZ_QUESTION_STATE :
|
|
// [0] : score (-1 shows not done yet)
|
|
// [1] : array showing on which number try each JQUIZ_ANSWER was selected
|
|
// [2] : number of attempts at this question
|
|
// [3] : total of weighted scores of correct answers that were selected
|
|
// i.e. each time a correct answer is selected,
|
|
// its JQUIZ_ANSWER[3] weighting is added to this total
|
|
// so when all the correct answers have been selected, this will be 100
|
|
// [4] : penalties incurred for hints (score is set to zero if >= 1)
|
|
// [5] : - for multiple choice, short-answer and hybrid questions, this is a
|
|
// comma-delimited list showing order in which answers were chosen
|
|
// - for multi-select fields, this is bar-delimted ('|') list of settings
|
|
// showing whether each checkbox was selected ('Y') on not ('N') when the
|
|
// 'Check' button was clicked. The final item in the list will be the
|
|
// settings for the correct answer.
|
|
|
|
// N.B. JBC, JMatch(v5) and JQuiz(v5) all use global variables 'I' and 'Status'
|
|
// JBC : I[0].length==3 && !window.RItems
|
|
// JQuiz(v5) : I[0].length==2
|
|
// JMatch(v5) : I[0].length==4 && window.RItems
|
|
|
|
// N.B. JCloze(v5+6) and JQuiz(v6) both use global variables 'I' and 'State'
|
|
// JCloze (v5) : I[0].length==3 && State[0].Guesses==null
|
|
// JCloze (v6) : I[0].length==3 && State[0].Guesses!=null
|
|
// JQuiz (v6) : I[0].length==4
|
|
|
|
// **********
|
|
// Rhubarb
|
|
// **********
|
|
|
|
if (window.Rhubarb==null) {
|
|
Rhubarb = new Array();
|
|
Rhubarb[0] = true; // show correct words
|
|
Rhubarb[1] = true; // show incorrect words
|
|
}
|
|
|
|
// **********
|
|
// Sequitur
|
|
// **********
|
|
|
|
if (window.Sequitur==null) {
|
|
Sequitur = new Array();
|
|
}
|
|
|
|
// **********
|
|
// Messages
|
|
// **********
|
|
|
|
if (window.MSG==null) {
|
|
MSG = new Array();
|
|
|
|
// Login prompts
|
|
MSG[0] = 'Name';
|
|
MSG[1] = 'ID';
|
|
MSG[2] = 'Email';
|
|
MSG[3] = 'Password';
|
|
MSG[4] = 'Cookies';
|
|
|
|
// Login buttons
|
|
MSG[5] = 'Start the Quiz';
|
|
MSG[6] = 'Cancel';
|
|
|
|
// Cookie menu options (only used if Login[4] is true)
|
|
MSG[7] = 'keep for this session only';
|
|
MSG[8] = 'keep for one day';
|
|
MSG[9] = 'keep for one month';
|
|
MSG[10] = 'do NOT keep cookies';
|
|
|
|
// Login error messages
|
|
MSG[11] = 'Sorry, you were unable to login. Please try again later.';
|
|
MSG[12] = 'Please fill in all the information.';
|
|
MSG[13] = 'Incorrect Password. Please try again.';
|
|
MSG[14] = 'Incorrect ID. Please try again.';
|
|
MSG[15] = 'Email address does not appear to be valid.';
|
|
|
|
// day and month names (used in Start_Time and End_Time)
|
|
MSG[16] = new Array('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
|
|
MSG[17] = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
|
|
|
|
// enable popups
|
|
MSG[18] = 'Please enable pop-up windows on your browser.';
|
|
|
|
// browser specific instuctions on how to enable popup windows
|
|
var n = navigator;
|
|
var s = n.userAgent.toLowerCase();
|
|
if (n.appName=='Netscape' && s.indexOf('gecko')>=0) {
|
|
// Netscape 6 and 7
|
|
MSG[18] += '\n\n' + 'Edit->Preferences, ' + (s.indexOf('mac')>=0 ? 'Advanced->Scripts & Plugins' : 'Privacy & Security->Popup Window Controls');
|
|
} else if (s.indexOf('safari')>=0) {
|
|
// Safari
|
|
MSG[18] += '\n\n' + 'on Safari menu, uncheck "Block Pop-Up Windows"';
|
|
} else if (s.indexOf('firebird')>=0) {
|
|
// Firebird
|
|
MSG[18] += '\n\n' + 'Preferences->Web Features, uncheck "Block Pop-Up Windows"';
|
|
} else if (s.indexOf('msie 6')>=0) {
|
|
// IE 6 (WinXP.SP2)
|
|
MSG[18] += '\n\n' + 'Tools->Pop-up Blocker->Turn Off Pop-up Blocker';
|
|
}
|
|
}
|
|
|
|
// *************
|
|
// Server Fields
|
|
// *************
|
|
|
|
if (window.ServerFields==null) {
|
|
ServerFields = new Array();
|
|
|
|
// these fields will be added to the ResultForm and submitted to the CGI script on the server.
|
|
// 'Sort', 'return_link_title', 'return_link_url' and 'print_blank_fields' are useful for formmail
|
|
|
|
// override the HP setting of sort fields (forces ALL fields to be displayed)
|
|
ServerFields[0] = new Array('sort', '');
|
|
|
|
// add link to close pop-up results window
|
|
ServerFields[1] = new Array('return_link_title', 'Close this window');
|
|
ServerFields[2] = new Array('return_link_url', 'javascript:self.close()');
|
|
|
|
// make sure zero values are printed
|
|
ServerFields[3] = new Array('print_blank_fields', 'yes');
|
|
|
|
// you can also set other fields for your customized CGI script
|
|
// e.g. adding a server defined start time (instead of a client defined start time)
|
|
// ServerFields[4] = new Array('serverStartTime', '<?php echo date("Y-m-d H:i:s") ?>');
|
|
}
|
|
|
|
// *********************
|
|
// Login screen
|
|
// (not required by LMS)
|
|
// *********************
|
|
|
|
function QuizLogin(LoginPrompt) {
|
|
if ((Login[0] || Login[1] || Login[2] || Login[3]) && !is_LMS()) {
|
|
var html = ''
|
|
+ '<HTML>'
|
|
+ '<HEAD></HEAD>'
|
|
+ '<BODY bgColor="#cccccc" onLoad="opener.setFocus(self)">'
|
|
+ '<FORM onSubmit="'
|
|
+ 'self.ok=true;'
|
|
+ 'self.expiry=null;'
|
|
;
|
|
if (Login[4]) { // cookie expiry
|
|
html += "opener.checkOK(self,'CookieExpiry');";
|
|
}
|
|
if (Login[0]) { // user name
|
|
html += "opener.checkOK(self,'UserName');";
|
|
}
|
|
if (Login[1]) { // user ID
|
|
html += "opener.checkOK(self,'UserID');";
|
|
}
|
|
if (Login[2]) { // user email
|
|
html += "opener.checkOK(self,'UserEmail');";
|
|
}
|
|
if (Login[3]) { // quiz password
|
|
html += "opener.checkOK(self,'Password');";
|
|
}
|
|
html += 'if(ok){'
|
|
+ 'opener.StartQuiz();'
|
|
+ 'self.close();'
|
|
+ '}else{'
|
|
+ 'if(isNaN(self.tries))self.tries=0;'
|
|
+ 'self.tries++;'
|
|
+ 'if(self.tries<3){'
|
|
+ 'opener.setFocus(self);'
|
|
+ '}else{'
|
|
+ "alert(opener.MSG[11]);"
|
|
+ 'opener.goBack();'
|
|
+ 'self.close();'
|
|
+ '}'
|
|
+ '}'
|
|
+ 'return false;'
|
|
+ '">'
|
|
;
|
|
html += '<TABLE>'
|
|
+ '<CAPTION>' + LoginPrompt + '</CAPTION>';
|
|
;
|
|
if (Login[0]) { // user name
|
|
var v = getCookie(self, 'UserName');
|
|
html += '<TR>'
|
|
+ '<TH align=right nowrap>' + MSG[0] + ' :</TH>'
|
|
+ '<TD>'
|
|
;
|
|
|
|
if (typeof(Login[0])=='boolean') { // text box
|
|
html += '<INPUT type=text name=UserName value="' + v + '">';
|
|
|
|
} else { // drop down menu of names
|
|
|
|
// pattern to match commas and white space
|
|
var comma = (window.RegExp) ? new RegExp('\\s*,\\s*') : ',';
|
|
|
|
// convert list of names to array, if necessary
|
|
if (typeof(Login[0])=='string') {
|
|
Login[0] = Login[0].split(comma);
|
|
}
|
|
|
|
html += '<SELECT name=UserName size=1>'
|
|
+ '<OPTION value=""></OPTION>'
|
|
;
|
|
for(var i=0; i<Login[0].length; i++) {
|
|
// convert name details to array if necesssary
|
|
if (typeof(Login[0][i])=='string') {
|
|
Login[0][i] = Login[0][i].split(comma);
|
|
}
|
|
html += makeOption(Login[0][i][0], v, Login[0][i][1]);
|
|
}
|
|
html += '</SELECT>';
|
|
}
|
|
html += '</TD>'
|
|
+ '</TR>'
|
|
;
|
|
}
|
|
if (Login[1]) { // user ID
|
|
var v = getCookie(self, 'UserID');
|
|
html += '<TR><TH align=right nowrap>' + MSG[1] + ' :</TH><TD><INPUT type=text name=UserID value="' + v + '"></TD></TR>';
|
|
}
|
|
if (Login[2]) { // user email
|
|
var v = getCookie(self, 'UserEmail');
|
|
html += '<TR><TH align=right nowrap>' + MSG[2] +' :</TH><TD><INPUT type=text name=UserEmail value="' + v + '"></TD></TR>';
|
|
}
|
|
if (Login[3]) { // quiz password
|
|
var v = getCookie(self, 'Password');
|
|
html += '<TR><TH align=right nowrap>' + MSG[3] + ' :</TH><TD><INPUT type=password name=Password value="' + v + '"></TD></TR>';
|
|
}
|
|
if (Login[4]) { // cookie lifespan
|
|
var v = getCookie(self, 'CookieExpiry');
|
|
html += '<TR>'
|
|
+ '<TH align=right nowrap>' + MSG[4] + ' :</TH>'
|
|
+ '<TD>'
|
|
+ '<SELECT name="CookieExpiry" size=1>'
|
|
+ makeOption('session', v, MSG[7])
|
|
+ makeOption('day', v, MSG[8])
|
|
+ makeOption('month', v, MSG[9])
|
|
+ makeOption('never', v, MSG[10])
|
|
+ '</SELECT>'
|
|
+ '</TD>'
|
|
+ '</TR>'
|
|
;
|
|
}
|
|
html += '<TR>'
|
|
+ '<TH> </TH>'
|
|
+ '<TD nowrap>'
|
|
+ '<INPUT type=submit value="' + MSG[5] + '"> '
|
|
+ '<INPUT type=button value="' + MSG[6] + '" onClick="opener.goBack();self.close();">'
|
|
+ '</TD>'
|
|
+ '</TR>'
|
|
+ '</TABLE></FORM></BODY></HTML>'
|
|
;
|
|
|
|
// set height of Login Window
|
|
var m = navigator.userAgent.indexOf('Mac')>=0;
|
|
var h = (m ? 80 : 100);
|
|
for (var i=0; i<5; i++) h += (Login[i] ? (m ? 20 : 25) : 0);
|
|
|
|
// open up a new window
|
|
if (!openWindow('', '', (m ? 320 : 300), h, 'RESIZABLE', html)) {
|
|
alert(MSG[18]); // unable to open popup window
|
|
}
|
|
|
|
} else { // no Login required
|
|
window.UserName = window.UserID = window.UserEmail = window.Password = '';
|
|
window.StartQuiz();
|
|
}
|
|
return true;
|
|
}
|
|
function makeOption(value, v, txt) {
|
|
return '<OPTION value="' + value + '"' + (value==v ? ' SELECTED' : '') + '>' + (txt ? txt : value) + '</OPTION>';
|
|
}
|
|
function setFocus(w) {
|
|
w.focus(); // bring window to the front
|
|
var obj = w.document.forms[0].elements;
|
|
for(var i=0; i<obj.length; i++) {
|
|
var v = getValue(w, i);
|
|
if (v=='' || obj[i].type=='submit') {
|
|
obj[i].focus();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function checkOK(w, n){
|
|
var v = getValue(w, n, true);
|
|
if (v || (n!='UserName' && isGuest())) {
|
|
if (n=='CookieExpiry') setCookieExpiry(w, v);
|
|
setCookie(self, n, v, w.expiry);
|
|
if (n!='CookieExpiry') eval('self.' + n + '=v');
|
|
} else {
|
|
if (w.ok) alert(MSG[12]);
|
|
w.ok = false;
|
|
}
|
|
}
|
|
function getValue(w, n, flag) {
|
|
var obj = w.document.forms[0].elements[n];
|
|
var TYPE = obj.type.toUpperCase(); // required for ns4 (win)
|
|
if (obj.options && TYPE.indexOf('SELECT')>=0){
|
|
var v = obj.options[obj.selectedIndex].value;
|
|
} else {
|
|
var v = obj.value;
|
|
}
|
|
if (flag) {
|
|
var msg = '';
|
|
if (n=='Password' || (n=='UserID' && !Login[3])) {
|
|
var pwd = getPassword(w);
|
|
if (pwd && v!=pwd) msg = MSG[n=='Password' ? 13 : 14];
|
|
}
|
|
if (n=='Email' && window.RegExp) {
|
|
var r = '(\\w|-)+';
|
|
r = r + '(\\.' + r + ')';
|
|
r = new RegExp('^(' + r + '*)@(' + r + '+)$');
|
|
if (v.match(r)==null) msg = MSG[15];
|
|
}
|
|
if (msg) {
|
|
obj.value = v = '';
|
|
if (w.ok) alert(msg);
|
|
w.ok = false;
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
function getPassword(w) {
|
|
var pwd = '';
|
|
if (Login[3] && typeof(Login[3])=='string') {
|
|
pwd = Login[3];
|
|
} else if ((Login[3] || Login[1]) && typeof(Login[0])=='object') {
|
|
var username = getValue(w, 'UserName');
|
|
for(var i=0; i<Login[0].length; i++) {
|
|
if (username==Login[0][i][0]) {
|
|
pwd = Login[0][i][2];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pwd;
|
|
}
|
|
function setCookieExpiry(w, v) {
|
|
if (v=='never'){
|
|
w.expiry = new Date('Thu, 01-Jan-70 00:00:01 GMT');
|
|
} else if (v=='day' || v=='month') {
|
|
var ms = (v=='month' ? 31 : 1) * 60 * 60 * 24 * 1000;
|
|
w.expiry = new Date((new Date()).getTime() + ms);
|
|
}
|
|
}
|
|
function setCookie(w, name, value, expires, path, domain, secure) {
|
|
if (name) w.document.cookie = ''
|
|
+ 'HP_' + name + "=" + escape(value)
|
|
+ (expires ? "; expires=" + expires.toGMTString() : "")
|
|
+ (path ? "; path=" + path : "")
|
|
+ (domain ? "; domain=" + domain : "")
|
|
+ (secure ? "; secure" : "")
|
|
;
|
|
}
|
|
function getCookie(w, n) {
|
|
var c = w.document.cookie;
|
|
var i = c.indexOf('HP_' + n + '=');
|
|
var j = (i<0) ? -1 : c.indexOf(';', (i += n.length + 4));
|
|
return (i<0) ? '' : unescape(c.substring(i, ((j<0) ? c.length : j)));
|
|
}
|
|
function goBack(w) {
|
|
if (w==null) w = self; // default
|
|
if (w.history.length) w.history.back();
|
|
}
|
|
function openWindow(url, name, width, height, attributes, html) {
|
|
if (window.screen && width && height) {
|
|
var W = screen.availWidth;
|
|
var H = screen.availHeight;
|
|
width = Math.min(width, W);
|
|
height = Math.min(height, H);
|
|
attributes = ''
|
|
+ (attributes ? (attributes+',') : '')
|
|
+ 'WIDTH='+width+',HEIGHT='+height
|
|
;
|
|
}
|
|
|
|
// workaround for "Access is denied" errors in IE when offline
|
|
// based on an idea seen at http://www.devshed.com/Client_Side/JavaScript/Mini_FAQ
|
|
var ie_offline = (document.all && location.protocol=='file:');
|
|
|
|
// open the window
|
|
var w = window.open((ie_offline ? '' : url), name, attributes);
|
|
|
|
// check window opened OK (user may have prevented popups)
|
|
if (w) {
|
|
// center the window
|
|
if (window.screen && width && height) {
|
|
w.moveTo((W-width)/2, (H-height)/2);
|
|
}
|
|
|
|
// add content, if required
|
|
if (html) {
|
|
with (w.document) {
|
|
clear();
|
|
open();
|
|
write(html);
|
|
close();
|
|
}
|
|
} else {
|
|
if (ie_offline && url) w.location = url;
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
// *********************
|
|
// Send results by email
|
|
// (not required by LMS)
|
|
// *********************
|
|
|
|
function SendAllResults(Score) {
|
|
// check this quiz is not generated by a LMS
|
|
if (!is_LMS()) {
|
|
|
|
// add flat file database details to the results form
|
|
AddDatabaseDetailsToResultForm();
|
|
|
|
// add student details to the results form
|
|
AddStudentDetailsToResultForm();
|
|
|
|
// add question details to the results form
|
|
AddQuestionDetailsToResultForm();
|
|
|
|
// add server fields, if any, to results form
|
|
AddServerFieldsToResultForm();
|
|
|
|
// change "method" of form, because "get" only allows 512 byts of data
|
|
ResultForm = replaceLast('method="get"', 'method="post"', ResultForm);
|
|
|
|
// create results window and form
|
|
var w = openWindow('', '', 500, 400, 'RESIZABLE,SCROLLBARS,HOTPOT_LOCATION', ResultForm);
|
|
|
|
// check window opened OK (user may have prevented popups)
|
|
if (w) {
|
|
|
|
// get shortcut to form object
|
|
var form = w.document.forms[0];
|
|
|
|
// update some important field values
|
|
form.Score.value = Score + '%';
|
|
form.realname.value = UserName;
|
|
form.Start_Time.value = getTime(Start_Time);
|
|
form.End_Time.value = getTime();
|
|
|
|
// force email subject and Exercise title
|
|
form.subject.value = document.title;
|
|
form. Exercise.value = document.title;
|
|
|
|
// update DB fields, if required
|
|
if (DB[0] && !isGuest()) set_db_fields(form);
|
|
if (DB[7]) form.action = DB[7];
|
|
if (DB[8]) form.recipient.value = DB[8];
|
|
|
|
// if this is a Netscape browser, check if the referer will be set OK
|
|
if (navigator.appName=='Netscape' && (location.protocol=='file:' || navigator.userAgent.indexOf('Netscape6')>=0)) {
|
|
|
|
// ns4 and ns7 set referer to 'file:// ...' when running a quiz offline
|
|
// ns6.2 (at least) always sets referer to 'about:blank'
|
|
|
|
// Netscape's setting of referer can cause BFormMail
|
|
// to reject the form, so encode the form data as a URL
|
|
|
|
var url = form.action;
|
|
var obj = form.elements;
|
|
for (var i=0; i<obj.length; i++) {
|
|
var v = escape(obj[i].value);
|
|
v = v.replace((new RegExp('\\+', 'g')), '%2B');
|
|
url += (i==0 ? '?' : '&') + obj[i].name + '=' + v;
|
|
}
|
|
w.location.href = url;
|
|
|
|
} else { // browser can POST form ok
|
|
form.submit();
|
|
}
|
|
|
|
} else { // unable to open popup window
|
|
alert(MSG[18]);
|
|
}
|
|
|
|
} // end if LMS
|
|
}
|
|
function isGuest() {
|
|
// check username is not a "guest" user
|
|
var flag = false;
|
|
var n = getCookie(self, 'UserName').toLowerCase();
|
|
if (n) {
|
|
// convert list of user names to array, if necessary
|
|
if(typeof(Login[5])=='string') {
|
|
Login[5] = Login[5].split(',');
|
|
}
|
|
for(var i=0; i<Login[5].length; i++) {
|
|
if (n==Login[5][i].toLowerCase()) {
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return flag;
|
|
}
|
|
function set_db_fields(form) {
|
|
// update list of DB fields, if required
|
|
if (DB[4]=='' && window.RegExp) {
|
|
// add administration fields
|
|
var db_fields = ''
|
|
+ 'subject,realname'
|
|
+ (Login[1] ? ',ID' : '')
|
|
+ (Login[2] ? ',email' : '')
|
|
+ (Login[3] ? ',password' : '')
|
|
+ ',Score,Start_Time,End_Time'
|
|
;
|
|
// add answer fields (except separators)
|
|
var r = new RegExp('^[^_]+_q\\d\\d_\\w+$');
|
|
for(var i=0; i<form.elements.length; i++) {
|
|
var n = form.elements[i].name;
|
|
if (r.test(n)) db_fields += ',' + n;
|
|
}
|
|
form.db_fields.value = db_fields;
|
|
}
|
|
// make sure delimiter is set (NS6+ requires this be set here, not any earlier)
|
|
form.db_delimiter.value = (DB[5] ? DB[5] : '\t');
|
|
}
|
|
function AddStudentDetailsToResultForm() {
|
|
var sDetails = '';
|
|
if (Login[0]) { // user name
|
|
// use 'realname' instead of a separate 'Name' field
|
|
// sDetails += makeHiddenField('Name', window.UserName);
|
|
}
|
|
if (Login[1]) { // user ID
|
|
sDetails += makeHiddenField('ID', window.UserID);
|
|
}
|
|
if (Login[2]) { // user email
|
|
sDetails += makeHiddenField('email', window.UserEmail);
|
|
}
|
|
if (sDetails && window.RegExp) {
|
|
// insert sDetails before '<input...Score...></input>'
|
|
var r = new RegExp('<input[^>]*Score[^>]*><\\/input>', 'i');
|
|
var m = r.exec(ResultForm);
|
|
if (m) {
|
|
ResultForm = ResultForm.replace(m[0], sDetails + m[0] + makeSeparator('Time_'));
|
|
sDetails = '';
|
|
}
|
|
}
|
|
if (Login[3]) { // quiz password
|
|
sDetails += makeHiddenField('Password', window.Password);
|
|
ResultForm = replaceLast('</form>', sDetails + '</form>', ResultForm);
|
|
}
|
|
}
|
|
function AddQuestionDetailsToResultForm() {
|
|
var qDetails = GetQuestionDetails();
|
|
if (qDetails) {
|
|
// insert qDetails before the final </form> tag in the ResultForm
|
|
ResultForm = replaceLast('</form>', qDetails + '</form>', ResultForm);
|
|
}
|
|
}
|
|
function AddDatabaseDetailsToResultForm() {
|
|
if (window.DB && DB[0] && !isGuest()) {
|
|
var dbDetails = '';
|
|
|
|
var folder = DB[1];
|
|
if (folder && folder.charAt(folder.length-1)!='/') folder += '/';
|
|
|
|
var file = DB[2];
|
|
if (file=='') {
|
|
file = location.href;
|
|
file= file.substring(file.lastIndexOf('/')+1);
|
|
var i = file.indexOf('?');
|
|
if (i >= 0) file = file.substring(0, i);
|
|
var i = file.lastIndexOf('.');
|
|
if (i >= 0) file = file.substring(0, i);
|
|
}
|
|
|
|
var ext = (DB[3] ? DB[3] : 'txt');
|
|
if (ext.charAt(0)!='.') ext = '.' + ext;
|
|
|
|
dbDetails += makeHiddenField('append_db', folder + file + ext);
|
|
dbDetails += makeHiddenField('db_fields', DB[4]);
|
|
dbDetails += makeHiddenField('db_delimiter', ''); // NS6+ requires this be set later
|
|
if (DB[6]) dbDetails += makeHiddenField('env_report', DB[6]);
|
|
|
|
// insert dbDetails before the final </form> tag in the ResultForm
|
|
ResultForm = replaceLast('</form>', dbDetails + '</form>', ResultForm);
|
|
}
|
|
}
|
|
function AddServerFieldsToResultForm() {
|
|
if (window.ServerFields) {
|
|
var s = ''; // input tags for s(erver fields)
|
|
for (var i=0; i<ServerFields.length; i++) {
|
|
if (ServerFields[i][0] && window.RegExp) {
|
|
// remove previous field value, if any
|
|
var r = new RegExp('<input[^>]*name\\s*=\\s*["\']\\s*' + ServerFields[i][0] + '[^>]*>(\\s*<\\/input>)?', 'i');
|
|
if (r.test(ResultForm)) {
|
|
ResultForm = ResultForm.replace(r, '');
|
|
}
|
|
}
|
|
if (ServerFields[i][1]) {
|
|
s += makeHiddenField(ServerFields[i][0], ServerFields[i][1]);
|
|
}
|
|
} // end for
|
|
if (s) ResultForm = replaceLast('</form>', s + '</form>', ResultForm);
|
|
}
|
|
}
|
|
function replaceLast(a, b, c) {
|
|
// replace last occurrence of 'a' in 'c' with 'b'
|
|
var l = a.length;
|
|
var i = c.lastIndexOf(a);
|
|
return (i<0 || l==0) ? c : (c.substring(0, i) + b + c.substring(i+l));
|
|
}
|
|
|
|
// *************************
|
|
// Extract question details
|
|
// *************************
|
|
|
|
function GetQuestionDetails() {
|
|
var t = get_quiz_type();
|
|
var v = get_quiz_version();
|
|
|
|
return (t==1) ? GetJbcQuestionDetails(v) :
|
|
(t==2) ? GetJClozeQuestionDetails(v) :
|
|
(t==3) ? GetJCrossQuestionDetails(v) :
|
|
(t==4) ? GetJMatchQuestionDetails(v) :
|
|
(t==5) ? GetJMixQuestionDetails(v) :
|
|
(t==6) ? GetJQuizQuestionDetails(v) :
|
|
(t==7) ? GetRhubarbDetails(v) :
|
|
(t==8) ? GetSequiturDetails(v) : '';
|
|
}
|
|
function GetJbcQuestionDetails(v) {
|
|
qDetails = '';
|
|
|
|
// check the quiz version
|
|
if (v==5 || v==6) {
|
|
|
|
// get question details
|
|
for(var q=0; q<I.length; q++) {
|
|
|
|
// initialize strings to hold answer details
|
|
var aDetails = new Array();
|
|
aDetails[0] = new Array(); // right
|
|
aDetails[1] = new Array(); // wrong
|
|
aDetails[2] = new Array(); // ignored
|
|
|
|
// get answer details
|
|
for(var a=0; a<I[q][1].length; a++) {
|
|
var i = (Status[q][1][a]=='R') ? 0 : (Status[q][1][a]=='0') ? 2 : 1;
|
|
aDetails[i][aDetails[i].length] = (JBC[6] ? a : I[q][1][a][0]);
|
|
}
|
|
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JBC', q);
|
|
|
|
// add separator, if required
|
|
if (JBC[0]) qDetails += makeSeparator(Q);
|
|
|
|
if (JBC[1]) { // number of attempts to answer question
|
|
qDetails += makeHiddenField(Q+'attempts', Status[q][2] + (Status[q][0]==1 ? 1 : 0));
|
|
}
|
|
if (JBC[2]) { // question text
|
|
qDetails += makeHiddenField(Q+'text', I[q][0]);
|
|
}
|
|
if (JBC[3] && (DB[0] || aDetails[0].length>0)) { // right
|
|
qDetails += makeHiddenField(Q+'right', aDetails[0]);
|
|
}
|
|
if (JBC[4] && (DB[0] || aDetails[1].length>0)) { // wrong
|
|
qDetails += makeHiddenField(Q+'wrong', aDetails[1]);
|
|
}
|
|
if (JBC[5] && (DB[0] || aDetails[2].length>0)) { // ignored
|
|
qDetails += makeHiddenField(Q+'ignored', aDetails[2]);
|
|
}
|
|
// calculate score for this question, if required (for HP version < 5.5)
|
|
if (isNaN(Status[q][3])) {
|
|
var a1 = Status[q][1].length; // answers
|
|
var a2 = Status[q][2]; // attempts
|
|
Status[q][3] = (a1<1 || a1<(a2-1)) ? 0 : ((a1 - (a2-1)) / a1);
|
|
}
|
|
// add 'score' for this question
|
|
qDetails += makeHiddenField(Q+'score', Math.floor(Status[q][3]*100)+'%');
|
|
} // end for
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetJClozeQuestionDetails(v) {
|
|
var qDetails = '';
|
|
|
|
// check the quiz version
|
|
if (v==5 || v==6) {
|
|
|
|
var hp5 = (State[0].Guesses==null);
|
|
|
|
// get details for each question
|
|
for (var q=0; q<State.length; q++) {
|
|
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JCloze', q);
|
|
|
|
// add separator, if required
|
|
if (JCloze[0]) qDetails += makeSeparator(Q);
|
|
|
|
// score (as %)
|
|
var x = (hp5 ? State[q][3] : State[q].ItemScore);
|
|
qDetails += makeHiddenField(Q+'score', Math.floor(x*100)+'%');
|
|
|
|
// shortcut to students correct answer
|
|
var correct = (hp5 ? State[q][5] : State[q].Guesses[State[q].Guesses.length-1]);
|
|
|
|
if (JCloze[1]) { // student's correct answer
|
|
qDetails += makeHiddenField(Q+'correct', correct);
|
|
}
|
|
if (JCloze[2]) { // other correct answers
|
|
var ignored = new Array();
|
|
for (var i=0, ii=0; i<I[q][1].length; i++) {
|
|
if (I[q][1][i][0] && (I[q][1][i][0].toUpperCase() != correct.toUpperCase())) {
|
|
ignored[ii++] = I[q][1][i][0];
|
|
}
|
|
}
|
|
if (DB[0] || ignored.length>0) qDetails += makeHiddenField(Q+'ignored', ignored);
|
|
}
|
|
if (JCloze[3] && State[q].Guesses) {
|
|
var wrong = new Array();
|
|
for (var i=0, ii=0; i<State[q].Guesses.length-1; i++) {
|
|
wrong[ii++] = State[q].Guesses[i];
|
|
}
|
|
if (DB[0] || ii>0) qDetails += makeHiddenField(Q+'wrong', wrong);
|
|
}
|
|
if (JCloze[4]) { // number of penalties
|
|
var x = (hp5 ? State[q][1] : State[q].HintsAndChecks);
|
|
qDetails += makeHiddenField(Q+'penalties', x);
|
|
}
|
|
if (JCloze[5]) { // clue shown?
|
|
var x = (hp5 ? State[q][0] : State[q].ClueGiven);
|
|
qDetails += makeHiddenField(Q+'clue_shown', (x ? 'HOTPOT_YES' : 'HOTPOT_NO'));
|
|
}
|
|
if (JCloze[6]) { // clue text
|
|
qDetails += makeHiddenField(Q+'clue_text', I[q][2]);
|
|
}
|
|
} // end for
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetJCrossQuestionDetails(v) {
|
|
var qDetails = '';
|
|
|
|
// check the quiz version
|
|
if (v==5 || v==6) {
|
|
|
|
// inialize letter count
|
|
var letters = 0;
|
|
|
|
// get details for each question
|
|
for (var row=0; row<L.length; row++) {
|
|
for (var col=0; col<L[row].length; col++) {
|
|
|
|
// increment letter count, if required
|
|
if (L[row][col]) letters++;
|
|
|
|
// show answers and clues, if required
|
|
var q = (v==5) ? C[row][col] : CL[row][col];
|
|
if (q) {
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JCross', q);
|
|
|
|
var clue_A = (v==5) ? A[q] : GetJCrossClue('Clue_A_' + q);
|
|
var clue_D = (v==5) ? D[q] : GetJCrossClue('Clue_D_' + q);
|
|
|
|
// add separator, if required
|
|
if (JCross[0] && (clue_A || clue_D)) {
|
|
qDetails += makeSeparator(Q);
|
|
}
|
|
|
|
if (clue_A) { // across question
|
|
if (JCross[3]) qDetails += makeHiddenField(Q+'across', GetJCrossWord(G, row, col));
|
|
if (JCross[4]) qDetails += makeHiddenField(Q+'across_clue', clue_A);
|
|
}
|
|
if (clue_D) { // down question
|
|
if (JCross[3]) qDetails += makeHiddenField(Q+'down', GetJCrossWord(G, row, col, true));
|
|
if (JCross[4]) qDetails += makeHiddenField(Q+'down_clue', clue_D);
|
|
}
|
|
} // end if q
|
|
} // end for col
|
|
} // end for row
|
|
|
|
if (JCross[2]) { // show number of letters
|
|
qDetails = makeHiddenField('JCross_letters', letters) + qDetails;
|
|
}
|
|
if (JCross[1]) { // show penalties
|
|
qDetails = makeHiddenField('JCross_penalties', window.Penalties) + qDetails;
|
|
}
|
|
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetJCrossClue(id) {
|
|
var obj = (document.getElementById) ? document.getElementById(id) : null;
|
|
return (obj) ? GetChildNodesText(obj, 'Clue') : '';
|
|
}
|
|
function GetJCrossWord(a, r, c, goDown) {
|
|
// a is a 2-dimensional array of letters, r is a row number, c is a column number
|
|
var s = '';
|
|
while (r<a.length && c<a[r].length && a[r][c]) {
|
|
s += a[r][c];
|
|
if (goDown) {
|
|
r++;
|
|
} else {
|
|
c++;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
function GetJMatchQuestionDetails(v) {
|
|
var qDetails = '';
|
|
|
|
// HP5.5 uses "I" for v5 and v6 JMatch quizzes
|
|
var hp5 = (window.I) ? true : false;
|
|
|
|
// check the quiz version
|
|
if (hp5 || v==6 || v==6.1) {
|
|
|
|
if (JMatch[1] && v==6.1) { // attempts
|
|
qDetails += makeHiddenField('JMatch_attempts', Penalties+1);
|
|
}
|
|
|
|
// get number of questions
|
|
var max_q = (hp5 || v==6) ? Status.length : F.length;
|
|
|
|
// get details for each question
|
|
for (var q=0; q<max_q; q++) {
|
|
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JMatch', q);
|
|
|
|
// add separator, if required
|
|
if (JMatch[0] && (JMatch[1] || JMatch[2] || JMatch[3])) {
|
|
qDetails += makeSeparator(Q);
|
|
}
|
|
if (JMatch[1] && (hp5 || v==6)) { // attempts
|
|
qDetails += makeHiddenField(Q+'attempts', Status[q][1]);
|
|
}
|
|
if (JMatch[2]) { // LHS text
|
|
var x = (hp5) ? I[q][0] : (v==6) ? GetJMatchText(q, 'LeftItem') : F[q][0];
|
|
qDetails += makeHiddenField(Q+'lhs', x);
|
|
}
|
|
if (JMatch[3]) { // RHS text
|
|
var x = (hp5) ? I[q][1] : (v==6) ? GetJMatchText(q, 'RightItem') : GetJMatchRHS(q);
|
|
qDetails += makeHiddenField(Q+'rhs', x);
|
|
}
|
|
} // end for
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetJMatchText(q, className) {
|
|
var obj = (document.getElementById) ? document.getElementById('Questions') : null;
|
|
return (obj) ? GetChildNodesText(obj.childNodes[q], className) : '';
|
|
}
|
|
function GetJMatchRHS(q) { // Drag-and-drop only (v==6.1)
|
|
var max_i = (window.F && window.D) ? F.length : 0;
|
|
for (var i=0; i<max_i; i++) {
|
|
if (D[i][2]==F[q][1]) break;
|
|
}
|
|
return (i<max_i) ? D[i][0] : '';
|
|
}
|
|
function GetJMixQuestionDetails(v) {
|
|
qDetails = '';
|
|
|
|
// check the quiz version
|
|
if (v==5 || v==6 || v==6.1) {
|
|
|
|
var A = Answers.length;
|
|
for (var a=0; a<A; a++) {
|
|
var G = Answers[a].length;
|
|
for (var g=0; g<G; g++) {
|
|
if (Answers[a][g] != GuessSequence[g]) break;
|
|
}
|
|
if (g>=G) break;
|
|
}
|
|
var isWrong = (a>=A);
|
|
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JMix', 0);
|
|
|
|
// add separator, if required
|
|
if (JMix[0]) qDetails += makeSeparator(Q);
|
|
|
|
// add 'score' for this question
|
|
var score = isWrong ? 0 : ((Segments.length-Penalties)/Segments.length);
|
|
qDetails += makeHiddenField(Q+'score', Math.floor(score*100)+'%');
|
|
|
|
if (JMix[1]) { // number of wrong guesses
|
|
qDetails += makeHiddenField(Q+'wrongGuesses', Penalties);
|
|
}
|
|
if (JMix[2]) { // right answer
|
|
qDetails += makeHiddenField(Q+'right', GetJMixSequence(Answers[isWrong ? 0 : a]));
|
|
}
|
|
if (JMix[3] && isWrong) { // wrong answer
|
|
qDetails += makeHiddenField(Q+'wrong', GetJMixSequence(GuessSequence));
|
|
}
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetJMixSequence(indexes) {
|
|
var s = new Array();
|
|
for (var i=0; i<indexes.length; i++) {
|
|
s[i] = JMix[4] ? indexes[i] : GetJMixSegmentText(indexes[i]);
|
|
}
|
|
return s;
|
|
}
|
|
function GetJMixSegmentText(index){
|
|
var i_max = Segments.length;
|
|
for (var i=0; i<i_max; i++) {
|
|
if (Segments[i][1] == index) break;
|
|
}
|
|
return (i<i_max) ? Segments[i][0] : '';
|
|
}
|
|
function GetJQuizQuestionDetails(v) {
|
|
var qDetails = '';
|
|
|
|
// HP5.5 uses "Status" for v5 and v6 JMatch quizzes (HP6 uses "State")
|
|
var hp = (window.Status) ? 5 : (window.State) ? 6 : 0;
|
|
|
|
// check the quiz version
|
|
if (hp) {
|
|
|
|
// get details for each question
|
|
var max_q = (hp==5) ? Status.length : State.length;
|
|
for (var q=0; q<max_q; q++) {
|
|
|
|
// skip this question if it was not used (HP6 v6 only)
|
|
if (hp==6 && !State[q]) continue;
|
|
|
|
// format 'Q' (a padded, two-digit version of 'q')
|
|
var Q = getQ('JQuiz', q);
|
|
|
|
// add separator
|
|
if (JQuiz[0]) qDetails += makeSeparator(Q);
|
|
|
|
if (hp==6 && JQuiz[11]) { // question type
|
|
var x = parseInt(I[q][2]);
|
|
x = (x==0) ? 'multiple-choice' : (x==1) ? 'short-answer' : (x==2) ? 'hybrid' : (x==3) ? 'multi-select' : 'n/a';
|
|
qDetails += makeHiddenField(Q+'type', x);
|
|
}
|
|
|
|
// score (as %)
|
|
var x = (hp==5) ? Status[q][4]*10 : I[q][0]*State[q][0];
|
|
qDetails += makeHiddenField(Q+'score', Math.floor(x)+'%');
|
|
|
|
if (hp==6 && JQuiz[10]) { // weighting
|
|
qDetails += makeHiddenField(Q+'weighting', I[q][0]);
|
|
}
|
|
if (JQuiz[1]) { // question text
|
|
var x = (hp==5) ? I[q][0] : (document.getElementById) ? GetChildNodesText(document.getElementById('Q_'+q), 'QuestionText') : '';
|
|
qDetails += makeHiddenField(Q+'question', x);
|
|
}
|
|
if (JQuiz[2]) { // student's correct answers
|
|
var x = (hp==5) ? Status[q][3] : GetJQuizAnswerDetails(q, 2);
|
|
qDetails += makeHiddenField(Q+'correct', x);
|
|
}
|
|
if (JQuiz[3]) { // ignored and wrong answers
|
|
var x = (hp==5) ? '' : GetJQuizAnswerDetails(q, 1);
|
|
if (hp==5) {
|
|
for (var i=0; i<I[q][1].length; i++) {
|
|
if (I[q][1][i][0] && (I[q][1][i][0].toUpperCase() != Status[q][3].toUpperCase())) {
|
|
x += ((x ? ',' : '') + I[q][1][i][0]);
|
|
}
|
|
}
|
|
}
|
|
if (DB[0] || x) qDetails += makeHiddenField(Q+'other', x);
|
|
}
|
|
if (hp==6 && JQuiz[7]) { // all selected answers
|
|
var x = GetJQuizAnswerDetails(q, 0);
|
|
qDetails += makeHiddenField(Q+'selected', x);
|
|
}
|
|
if (hp==6 && JQuiz[8]) { // wrong answers
|
|
var x = GetJQuizAnswerDetails(q, 3);
|
|
qDetails += makeHiddenField(Q+'wrong', x);
|
|
}
|
|
if (hp==6 && JQuiz[9]) { // ignored answers
|
|
var x = GetJQuizAnswerDetails(q, 4);
|
|
qDetails += makeHiddenField(Q+'ignored', x);
|
|
}
|
|
if (JQuiz[4]) { // number of hints
|
|
var x = (hp==5) ? Status[q][2] : State[q][4];
|
|
qDetails += makeHiddenField(Q+'hints', x);
|
|
}
|
|
if (JQuiz[5]) { // number of checks of incorrect answers
|
|
var x = (hp==5) ? Status[q][1] : (State[q][2]-1);
|
|
qDetails += makeHiddenField(Q+'checks', x);
|
|
}
|
|
} // end for
|
|
} // end if
|
|
|
|
return qDetails;
|
|
}
|
|
function GetChildNodesText(obj, className) {
|
|
// search this node (obj) and its child nodes and
|
|
// return all text under node with required classname
|
|
var txt = '';
|
|
if (obj) {
|
|
if (className && obj.className==className) {
|
|
className = '';
|
|
}
|
|
if (className=='' && obj.nodeType==3) { // text node
|
|
txt = obj.nodeValue + ' '; // html entities
|
|
}
|
|
if (obj.childNodes) {
|
|
for (var i=0; i<obj.childNodes.length; i++) {
|
|
txt += GetChildNodesText(obj.childNodes[i], className);
|
|
}
|
|
|
|
}
|
|
}
|
|
return txt;
|
|
}
|
|
function GetJQuizAnswerDetails(q, flag) {
|
|
// flag : the type of information required about the student's answers
|
|
// 0 : all student's answers
|
|
// 1 : student's wrong and ignored answers
|
|
// 2 : student's correct answers
|
|
// 3 : student's wrong answers
|
|
// 4 : ignored answers
|
|
|
|
var x = State[q][5];
|
|
|
|
if (I[q][2]=='3') { // multi-select
|
|
|
|
if (flag==4) {
|
|
var x = new Array();
|
|
} else {
|
|
// get required part of 'x' and convert to array
|
|
var i = x.lastIndexOf('|');
|
|
var x = x.substring((flag==2 ? (i+1) : 1), ((flag==0 || flag==2) ? x.length : i)).split('|');
|
|
}
|
|
for (var i=0; i<x.length; i++) {
|
|
var a = new Array();
|
|
for (var ii=0; ii<x[i].length; ii++) {
|
|
if (x[i].charAt(ii)=='Y') {
|
|
var s = JQuiz[6] ? String.fromCharCode(97+ii) : I[q][3][ii][0];
|
|
if (s && s.replace && window.RegExp) {
|
|
s = s.replace(new RegExp('\\+', 'g'), '+');
|
|
}
|
|
a.push(s);
|
|
}
|
|
}
|
|
x[i] = a.join('+');
|
|
}
|
|
|
|
} else if (x) { // multiple-choice, short-answer and hybrid
|
|
|
|
// remove trailing comma and convert to array
|
|
x = x.substring(0, x.length-1).split(',');
|
|
|
|
if (flag) {
|
|
var a = new Array();
|
|
if (flag==1 || flag==2 || flag==3) {
|
|
for (var i=0; i<x.length; i++) {
|
|
var ii = I[q][3][(x[i].charCodeAt(0)-65)][2];
|
|
if(((flag==1 || flag==2) && ii==1) || (flag==3 && ii==0)) a.push(x[i]);
|
|
}
|
|
}
|
|
if (flag==1) {
|
|
x = a;
|
|
a = new Array();
|
|
}
|
|
if (flag==1 || flag==4) {
|
|
for (var i=0; i<I[q][3].length; i++) {
|
|
var s = String.fromCharCode(65+i);
|
|
for (var ii=0; ii<x.length; ii++) {
|
|
if (x[ii]==s) break;
|
|
}
|
|
if (ii==x.length) a.push(s);
|
|
}
|
|
}
|
|
x = a;
|
|
}
|
|
|
|
// convert answer indexes to values, if required
|
|
if (JQuiz[6]==false) {
|
|
for (var i=0; i<x.length; i++) {
|
|
var ii = x[i].charCodeAt(0) - 65;
|
|
x[i] = I[q][3][ii][0];
|
|
}
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
function GetRhubarbDetails(v) {
|
|
qDetails = '';
|
|
if (v==6) {
|
|
var Q = getQ('Rhubarb', 0);
|
|
if (document.title) { // use quiz title as question name
|
|
qDetails += makeHiddenField(Q+'name', document.title);
|
|
}
|
|
if (Rhubarb[0]) { // correct words
|
|
qDetails += makeHiddenField(Q+'correct', Words.length+' words');
|
|
}
|
|
if (Rhubarb[1]) { // incorrect words
|
|
// remove leading 'Wrong guesses: ' from Detail
|
|
var x = Detail.substring(15).split(' ');
|
|
qDetails += makeHiddenField(Q+'wrong', x);
|
|
}
|
|
}
|
|
return qDetails;
|
|
}
|
|
function GetSequiturDetails(v) {
|
|
qDetails = '';
|
|
// there is no information available ... at the moment
|
|
return qDetails;
|
|
}
|
|
|
|
// *********************
|
|
// library functions
|
|
// *********************
|
|
|
|
function pad(i, l) {
|
|
var s = (i+'');
|
|
while (s.length<l) s = '0' + s;
|
|
return s;
|
|
}
|
|
function getQ(section, q) {
|
|
// Q is a padded, two-digit version of the question number, 'q', prefixed by 'section'
|
|
return section + '_q' + (q<9 ? '0' : '') + (q+1) + '_';
|
|
}
|
|
function makeSeparator(Q) {
|
|
return is_LMS() ? '' : makeHiddenField(Q.substring(0, Q.length-1), '---------------------------------');
|
|
}
|
|
function makeHiddenField(name, value) {
|
|
var field = '';
|
|
var t = typeof(value);
|
|
if (t=='string') {
|
|
value = encode_entities(value);
|
|
} else if (t=='object') {
|
|
var values = value;
|
|
var i_max = values.length;
|
|
value = '';
|
|
for (var i=0; i<i_max; i++) {
|
|
values[i] = trim(values[i]);
|
|
if (values[i]!='') {
|
|
value += (i==0 ? '' : ',') + encode_entities(values[i]);
|
|
}
|
|
}
|
|
}
|
|
if (is_LMS()) {
|
|
if (value && value.indexOf && value.indexOf('<')>=0 && value.indexOf('>')>=0) {
|
|
value = '<![CDATA[' + value + ']]>';
|
|
}
|
|
field = '<field><fieldname>' + name + '</fieldname><fielddata>' + value + '</fielddata></field>';
|
|
} else {
|
|
field = '<INPUT type=hidden name="' + name + '" value="' + value + '">';
|
|
}
|
|
return field;
|
|
}
|
|
function trim(s) {
|
|
var i = 0;
|
|
var ii = s.length;
|
|
while (i<ii && s.charAt(i)==' ') {
|
|
i++;
|
|
}
|
|
while (ii>i && s.charAt(ii-1)==' ') {
|
|
ii--;
|
|
}
|
|
return s.substring(i, ii);
|
|
}
|
|
function encode_entities(s_in) {
|
|
var i_max = (s_in) ? s_in.length : 0;
|
|
var s_out = '';
|
|
for (var i=0; i<i_max; i++) {
|
|
var c = s_in.charCodeAt(i);
|
|
// 34 : double quote .......["] &
|
|
// 38 : single quote .......['] '
|
|
// 44 : comma ..............[,]
|
|
// 60 : left angle bracket .[<] <
|
|
// 62 : right angle bracket [>] >
|
|
// >=128 multibyte character
|
|
s_out += (c<128) ? s_in.charAt(i) : ('&#x' + pad(c.toString(16), 4) + ';');
|
|
}
|
|
return s_out;
|
|
}
|
|
|
|
// *********************
|
|
// initialization
|
|
// functions
|
|
// *********************
|
|
|
|
function getTime(obj) {
|
|
obj = obj ? obj : new Date();
|
|
|
|
// get year, month and day
|
|
// for an LMS : yyyy-mm-dd
|
|
// for email : DayName MonthName dd yyyy
|
|
var s = is_LMS() ?
|
|
obj.getFullYear() + '-' + pad(obj.getMonth()+1, 2) + '-' + pad(obj.getDate(), 2) :
|
|
MSG[16][obj.getDay()] + ' ' + MSG[17][obj.getMonth()] + ' ' + pad(obj.getDate(), 2) + ' ' + obj.getFullYear()
|
|
;
|
|
|
|
// get hours, minutes and seconds (hh:mm:ss)
|
|
s += ' ' + pad(obj.getHours(), 2) + ':' + pad(obj.getMinutes(), 2) + ':' + pad(obj.getSeconds(), 2);
|
|
|
|
// get time difference
|
|
// for an LMS : +xxxx
|
|
// for email : GMT+xxxx
|
|
var x = obj.getTimezoneOffset(); // e.g. -540
|
|
if (!isNaN(x)) {
|
|
s += ' ' + (is_LMS() ? '' : 'GMT') + (x<0 ? '+' : '-');
|
|
x = Math.abs(x);
|
|
s += pad(parseInt(x/60), 2) + pad(x - (parseInt(x/60)*60), 2);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
function getFunction(fn) {
|
|
if (typeof(fn)=='string') {
|
|
fn = eval('window.' + fn);
|
|
}
|
|
return (typeof(fn)=='function') ? fn : null;
|
|
}
|
|
function getFunctionCode(fn, extra) {
|
|
var s = '';
|
|
var obj = getFunction(fn);
|
|
if (obj) {
|
|
s = obj.toString();
|
|
var i1 = s.indexOf('{')+1;
|
|
var i2 = s.lastIndexOf('}');
|
|
if (i1>0 && i1<i2) {
|
|
s = s.substring(i1, i2);
|
|
}
|
|
}
|
|
return s + (extra ? extra : '');
|
|
}
|
|
function getFunctionArgs(fn) {
|
|
var a = new Array();
|
|
var obj = getFunction(fn);
|
|
if (obj) {
|
|
var s = obj.toString();
|
|
var i1 = s.indexOf('(')+1;
|
|
var i2 = s.indexOf(')');
|
|
if (i1>0 && i1<i2) {
|
|
a = s.substring(i1, i2).split(',');
|
|
}
|
|
}
|
|
return (a.length) ? ('"'+ a.join('","') + '",') : '';
|
|
}
|
|
function getPrompt(fn) {
|
|
// the LoginPrompt is the text string in the first prompt(...) statement
|
|
// v5 : in the StartUp function
|
|
// v6 : in the GetUserName function
|
|
// Note: netscape uses double-quote as delimiter, others use single quote
|
|
var s = getFunctionCode(fn);
|
|
var i1 = s.indexOf('prompt') + 8;
|
|
var i2 = s.indexOf(s.charAt(i1-1), i1);
|
|
|
|
var p = (i1>=8 && i2>i1) ? s.substring(i1, i2) : '';
|
|
|
|
// make sure browser has decoded the unicode prompt properly
|
|
// this check is mainly for ns4, but there may be others
|
|
if (window.RegExp) {
|
|
var r = new RegExp('u([0-9A-F]{4})');
|
|
var m = r.exec(p);
|
|
while (m) {
|
|
p = p.replace(m[0], '&#' + parseInt(m[1], 16) + ';');
|
|
m = r.exec(p);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
function getStartUpCode(fn) {
|
|
// the main initialization code comes from the StartUp function
|
|
// v5 : the code before "UserName", if any,
|
|
// and the code after the 2nd subsequent '}'
|
|
// v6 : the code before and after 'GetUserName();'
|
|
// i.e. all the code except the call to 'GetUserName();'
|
|
var s = getFunctionCode(fn);
|
|
var i1 = s.indexOf('GetUserName();');
|
|
if (i1>=0) { // v6
|
|
var i2 = i1 + 14;
|
|
} else { // v5
|
|
var i1 = s.indexOf('UserName');
|
|
var i2 = s.indexOf('}', s.indexOf('}', i1+8)+1)+1;
|
|
}
|
|
return (0<i1 && i1<i2) ? s.substring(0, i1) + s.substring(i2) : '';
|
|
}
|
|
function is_LMS() {
|
|
return document.forms['store'] ? true : false;
|
|
}
|
|
|
|
|
|
// ***************
|
|
// fix IE5 and NS6
|
|
// ***************
|
|
|
|
// add Array.push if required (allows v6 quizzes to run on ie5win)
|
|
if (Array.prototype && Array.prototype.push==null) {
|
|
Array.prototype.push = new Function("x", "this[this.length]=x");
|
|
}
|
|
|
|
// add attachEvent function, if required (allows HP5 v6 quizzes to run on ie5mac)
|
|
// NOTE: to allow v6 quizzes on ie5mac, the following code
|
|
// needs to be inserted BEFORE the Hot Potatoes javascript
|
|
if (window.attachEvent==null) {
|
|
window.attachEvent = new Function('evt', 'fn', 'eval("window."+evt+"="+fn)');
|
|
}
|
|
if (document.attachEvent==null) {
|
|
document.attachEvent = new Function('evt', 'fn', 'eval("document."+evt+"="+fn)');
|
|
}
|
|
|
|
// fix the ShowMessage function for NS6
|
|
// by removing calls to a button's "focus()" method
|
|
if (navigator.userAgent.indexOf("Netscape6")>=0 && window.ShowMessage) {
|
|
var s = ShowMessage.toString();
|
|
var r = new RegExp('document\\.getElementById\\((\'|")FeedbackOKButton(\'|")\\)\\.focus\\(\\);', 'gi');
|
|
s = s.substring(s.indexOf('{')+1, s.lastIndexOf('}')).replace(r, '');
|
|
window.ShowMessage = new Function('Feedback', s);
|
|
}
|
|
|
|
// ns6.0 (in JMix at least) has an error in the FocusAButton function too
|
|
// this could be fixed as follows ...
|
|
//if (window.FocusAButton) {
|
|
// window.FocusAButton = new Function('return true');
|
|
//}
|
|
// however, ns6.0 then crashes completely when the mouse moves over a link, so don't bother
|
|
|
|
// Hot Potatoes quiz sniffing
|
|
|
|
// === v3 ===
|
|
// JBC uses "QuizForm", which contains elements called "Q*_**" (* and ** start at 1)
|
|
// JCloze uses "Cloze" form
|
|
// JCross uses "Crossword" form
|
|
// JMatch uses "QuizForm", which contains elements called "1,2,3..x" and "x+1,x+2...",
|
|
// and "CheckForm" form, which contains an element called "ScoreBox"
|
|
// it is also the only HP quiz type to use an array called "CorrectAnswers"
|
|
// JQuiz uses "QForm*" forms (* starts at 1), which each contain an element called "Guess"
|
|
|
|
// === v4 ===
|
|
// JBC uses "QForm" form in "QuestionDiv", which contains elements called "FB* (* starts at 0)"
|
|
// JCloze uses "Cloze" form in "QuestionDiv"
|
|
// JCross uses "Crossword" form in "CWDiv"
|
|
// JMatch uses "ExCheck" form in "TitleDiv"
|
|
// (no JMix in hp4)
|
|
// JQuiz uses "QForm" form in "QuestionDiv", which contains an element called "Answer"
|
|
|
|
// === v5 ===
|
|
// JBC uses "QForm" form, which contains elements called "FB_*_**" (* and ** start at 0)
|
|
// JCloze uses "Cloze" form
|
|
// JCross writes out "AnswerForm" from a variable called "GetAnswerOpener"
|
|
// HP5.3: uses "AnswerForm" in "BottomFrame"
|
|
// HP5.5: uses "AnswerForm" in "TopFrame", but it is only there when an answer is being input
|
|
// JMatch uses "QForm" form, which contains elements called "sel*" (which disappear by the time the quiz is finished)
|
|
// JMix uses "ButtonForm"
|
|
// JQuiz uses "QForm*" and "Buttons*" (one for each question)
|
|
|
|
// === v6 ===
|
|
// JBC uses "QForm" form (elements have no name or id)
|
|
// JCloze uses "Cloze" form (elements have no name or id)
|
|
// JCross does not use any forms,
|
|
// HP5: has "GridDiv" in "MainDiv"
|
|
// HP6: has "Clues" table in "MainDiv"
|
|
// JMatch has "MatchDiv" in "MainDiv"
|
|
// HP5: uses "QForm" form, which contains elements called "sel*"
|
|
// HP6: uses "QForm" form, which contains elements called "s*_**"
|
|
// JMix does not use any forms, but has "SegmentDiv" in "MainDiv"
|
|
// JQuiz
|
|
// HP5: uses "QForm" form, which contains an element called "Guess"
|
|
// HP6: has "Questions" ordered list in "MainDiv"
|
|
|
|
// === v6+ ===
|
|
// JMatch has DIVs called "D*" and "F*" (* starts at 0)
|
|
// JMix has DIVs called "D*" and "Drop*" (* starts at 0)
|
|
|
|
// useful sniffing tools (Cut and Paste to browser address box)
|
|
//javascript:var s="";var x=new quiz_obj();for(X in x)s+=","+X+"="+x[X];alert(s.substring(1));
|
|
//javascript:var s="";var x=document.layers;for(var i=0;i<x.length;i++)s+=","+x[i].name;alert(s.substring(1))
|
|
//javascript:var s="";var x=document.forms;for(var i=0;i<x.length;i++)s+=","+x[i].id;alert(s.substring(1))
|
|
//javascript:var s="";var x=document.forms;for(var i=0;i<x.length;i++)s+=","+x[i].name;alert(s.substring(1))
|
|
//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))
|
|
//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))
|
|
|
|
function sniff_quiz() {
|
|
// "sniff" (=detect) the quiz's type and intended browser version
|
|
// and cache the values in a global variable caleld "quiz"
|
|
|
|
// quiz type
|
|
// 0 : unknown
|
|
// 1 : jbc
|
|
// 2 : jcloze
|
|
// 3 : jcross
|
|
// 4 : jmatch
|
|
// 5 : jmix
|
|
// 6 : jquiz
|
|
// 7 : rhubarb (TexToys)
|
|
// 8 : Sequitur (TexToys)
|
|
|
|
// intended browser version
|
|
// 3 : ns3, ie3 (frames)
|
|
// 4 : ns4, ie4 (cross browser dhtml)
|
|
// 5 : ie5 (frames, send results via CGI)
|
|
// 6 : ie6, op7, gecko (w3 standards)
|
|
// 6.1 : "drag and drop" versions of JMatch and JMix v6
|
|
|
|
// create the global "quiz" object, if necessary
|
|
if (!window.quiz) window.quiz = new Object();
|
|
|
|
// check the version and type are not already set
|
|
if (!quiz.v || !quiz.t) {
|
|
|
|
// initialize version and type
|
|
var v = 0;
|
|
var t = 0;
|
|
|
|
// set shortcuts to DOM objects
|
|
var d = document;
|
|
var f = d.forms;
|
|
|
|
if (f.QuizForm && f.CheckForm && self.CorrectAnswers) {
|
|
v = 3;
|
|
t = 4; // jmatch
|
|
|
|
} else if (self.FeedbackFrame && self.CodeFrame) {
|
|
v = 3;
|
|
f = CodeFrame.document.forms;
|
|
t = (f.QuizForm) ? 1 : (f.Cloze) ? 2 : (f.Crossword) ? 3 : (f.QForm1) ? 6 : 0;
|
|
|
|
} else if (self.DynLayer) {
|
|
v = 4;
|
|
if (d.layers) {
|
|
// for NS4, adjust "f" to point to a forms object in a layer
|
|
var lyr = d.QuestionDiv || d.CWDiv || d.TitleDiv || null;
|
|
if (lyr) f = lyr.document.forms;
|
|
}
|
|
t = (f.QForm && f.QForm.FB0) ? 1 : (f.Cloze) ? 2 : (f.Crossword) ? 3 : (f.ExCheck) ? 4 : (f.QForm && f.QForm.Answer) ? 6 : 0;
|
|
|
|
} else if (self.TopFrame && self.BottomFrame) {
|
|
v = 5;
|
|
f = BottomFrame.document.forms;
|
|
t = (f.QForm && f.QForm.elements[0].name.substring(0,3)=='FB_') ? 1 : (f.Cloze) ? 2 : (self.GetAnswerOpener && GetAnswerOpener.indexOf('AnswerForm')>=0) ? 3 : (f.QForm && self.RItems) ? 4 : (f.ButtonForm) ? 5 : (f.QForm0 && f.Buttons0) ? 6 : 0;
|
|
|
|
} else if (GetObj(d, 'MainDiv')) {
|
|
v = 6;
|
|
var obj = (f.QForm) ? f.QForm.elements : null;
|
|
t = (obj && obj.length>0 && obj[0].id=='') ? 1 : (f.Cloze) ? 2 : (GetObj(d, 'GridDiv') || GetObj(d, 'Clues')) ? 3 : GetObj(d, 'MatchDiv') ? 4 : GetObj(d, 'SegmentDiv') ? 5 : ((f.QForm && f.QForm.Guess) || GetObj(d, 'Questions')) ? 6 : 0;
|
|
|
|
} else if (GetObj(d, 'D0')) {
|
|
v = 6.1; // drag and drop (HP5 and HP6)
|
|
t = (GetObj(d, 'F0')) ? 4 : (GetObj(d, 'Drop0')) ? 5 : 0;
|
|
|
|
} else if (window.Words && f.Rhubarb) {
|
|
v = 6;
|
|
t = 7; // rhubarb (TexToys)
|
|
|
|
} else if (window.Segments && GetObj(d, 'Story')) {
|
|
v = 6;
|
|
t = 8; // sequitur (TexToys)
|
|
|
|
}
|
|
|
|
if (v) quiz.v = v; // intended browser version
|
|
if (t) quiz.t = t; // quiz type
|
|
}
|
|
}
|
|
function get_quiz_type() {
|
|
sniff_quiz();
|
|
return quiz.t;
|
|
}
|
|
function get_quiz_version() {
|
|
sniff_quiz();
|
|
return quiz.v;
|
|
}
|
|
|
|
function all_finished(a, s, aa, ss) {
|
|
// determine whether or not all quistions in a quiz are finished
|
|
|
|
// a : outer array
|
|
// s : condition, if any, on outer array
|
|
// aa : inner array, if any
|
|
// ss : condition, if any, on inner array
|
|
|
|
// the arrays "a" and "aa" may be passed as arrays or strings to be eval(uated)
|
|
// the conditions "s" and "ss" are specified as strings to be eval(uated)
|
|
|
|
// assume a positive result
|
|
var r = true;
|
|
|
|
// set length of outer array. if any
|
|
var l = (typeof(a)=="string") ? eval(a + ".length") : a ? a.length : 0;
|
|
|
|
// loop through outer array
|
|
for (var i=0; i<l; i++) {
|
|
|
|
// do outer condition, if any
|
|
if (s && eval(s)) r = false;
|
|
|
|
// set length of inner array, if any
|
|
var ll = (typeof(aa)=="string") ? eval(aa + ".length") : aa ? aa.length : 0;
|
|
|
|
// loop through inner array. checking inner condition
|
|
for (var ii=0; ii<ll; ii++) {
|
|
if (ss && eval(ss)) r = false;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function is_finished() {
|
|
|
|
// assume false result
|
|
var r = false;
|
|
|
|
var t = get_quiz_type();
|
|
var v = get_quiz_version();
|
|
|
|
if (t==1) { // jbc
|
|
|
|
if (v==3) r = all_finished(DoneStatus, "i>0 && a[i]=='0'");
|
|
else if (v==4) r = all_finished(DoneStatus, "a[i]==0");
|
|
else if (v==5 || v==6) r = all_finished(Status, "a[i][0]==0");
|
|
|
|
|
|
} else if (t==2) { // jcloze
|
|
|
|
if (v==3 || v==4 || v==5 || v==6) r = all_finished(I, "CheckAnswer(i)==-1");
|
|
// also: else if (v==5 || v==6) r = all_finished(State, "a[i][4]!=1")
|
|
|
|
} else if (t==3) { // jcross
|
|
|
|
if (v==3) r = all_finished(document.Crossword.elements, "ConvertCase(is.mac?unescape(MacStringToWin(a[i].value)):a[i].value,1)!=Letters[i]");
|
|
else if (v==4) r = all_finished(WinLetters, "ConvertCase(GetBoxValue(i),1).charAt(0) != a[i].charAt(0)");
|
|
else if (v==5) r = all_finished(L, "", "L[i]", "L[i][ii] && L[i][ii]!=G[i][ii]");
|
|
|
|
} else if (t==4) { // jmatch
|
|
|
|
if (v==3) r = all_finished(CorrectAnswers, "document.QuizForm.elements[i*2].selectedIndex != a[i]");
|
|
else if (v==4) r = all_finished(Draggables, "a[i].correct!='1'");
|
|
else if (v==5) r = all_finished(I, "I[i][2]<1 && I[i][0].length>0 && Status[i][0]<1 && GetAnswer(i)!=I[i][3]");
|
|
else if (v==6) r = all_finished(D, "D[i][2]==0 || D[i][2]!=D[i][1]");
|
|
|
|
} else if (t==5) { // jmix
|
|
|
|
// there was no v3 or v4 of JMix
|
|
if (v==5 || v==6) r = !all_finished(Answers, "a[i].join(',')=='" + GuessSequence.join(',') + "'");
|
|
|
|
} else if (t==6) { // jquiz
|
|
|
|
if (v==3 || v==4) r = all_finished(State, "a[i][0]==0");
|
|
else if (v==5 || v==6) r = all_finished(State, "a[i] && a[i][0]<0");
|
|
|
|
} else if (t==7) { // rhubarb
|
|
if (v==6) r = all_finished(DoneList, "a[i]==1");
|
|
|
|
} else if (t==8) { // sequitur
|
|
if (v==6) r = (CurrentNumber==TotalSegments || AllDone);
|
|
}
|
|
|
|
return r; // result
|
|
}
|
|
|
|
function GetObj(d, id) {
|
|
return d.getElementById ? d.getElementById(id) : d.all ? d.all[id] : d[id];
|
|
}
|
|
|
|
// **************
|
|
// initialization
|
|
// **************
|
|
|
|
if (window.Finish==null) { // v3, v4 and v5
|
|
// modify the function which writes feedback to call Finish() if the quiz is finished
|
|
// usually this is the WriteFeedback()
|
|
// but v3 of JMatch uses CheckAnswer()
|
|
var f = window.WriteFeedback ? 'WriteFeedback' : 'CheckAnswer';
|
|
var s = getFunctionCode(f, 'if(is_finished())Finish();');
|
|
var a = getFunctionArgs(f);
|
|
eval('window.' + f + '=new Function(' + a + 's)');
|
|
}
|
|
|
|
// the standard Finish() function
|
|
// for v6, this overwrites the original function
|
|
function Finish(){
|
|
var f = document.store;
|
|
if (f) {
|
|
// hotpot use "Score", TexToys use "FinalScore"
|
|
var mark = (window.Score ? Score : window.FinalScore ? FinalScore : 0);
|
|
f.starttime.value = getTime(Start_Time);
|
|
f.endtime.value = getTime();
|
|
f.mark.value = mark;
|
|
f.detail.value = '<?xml version="1.0"?><hpjsresult><fields>'+GetQuestionDetails()+'</fields></hpjsresult>';
|
|
f.submit();
|
|
}
|
|
}
|
|
|
|
// create form to send results
|
|
if (DB[7] && DB[8] && !is_LMS()) {
|
|
ResultForm = ''
|
|
+ '<html><body>'
|
|
+ '<form name="Results" action="" method="post" enctype="x-www-form-encoded">'
|
|
+ makeHiddenField('recipient', '')
|
|
+ makeHiddenField('subject', '')
|
|
+ makeHiddenField('Exercise', '')
|
|
+ makeHiddenField('realname', '')
|
|
+ makeHiddenField('Score', '')
|
|
+ makeHiddenField('Start_Time', '')
|
|
+ makeHiddenField('End_Time', '')
|
|
+ makeHiddenField('title', 'Thanks!')
|
|
+ '</form>'
|
|
+ '</body></html>'
|
|
;
|
|
}
|
|
// reassign the StartUp function
|
|
var p = getPrompt(window.GetUserName || window.StartUp);
|
|
var c = getStartUpCode(window.StartUp);
|
|
if (p && c) {
|
|
window.StartUp = new Function('QuizLogin("' + p + '")');
|
|
window.StartQuiz = new Function('if(!is_LMS()){' + c + '}');
|
|
}
|
|
|
|
// reassign the SendResults function
|
|
window.SendResults = SendAllResults;
|
|
|
|
// set start time
|
|
var Start_Time = new Date();
|
|
|
|
//-->
|