/* ****** WORKBOOK CHECKER *********************************************
 *
 * Checks the question submissions in the Work Books
 *
 * CAPDM Ltd. June 2007, Last updated 24th December 2007 (KWC)
 *
 ************************************************************************** */

function show_props(obj, obj_name) {
   var result = "";
   for (var i in obj)
      result += obj_name + "." + i + " = " + obj[i] + "\n";
   return result;
}

var wb_err = "";
var wb_original = "";
var wb_occurrence = 0;
var wb_match = ""; 
wb_selection = ""; 
wb_colour = "yellow";
// Some DandD variables
var wb_option_left = 10, wb_option_top = 75, wb_option_width = 4;  // em
var wb_option_font = 10, wb_bottom_offset = 30, wb_option_inc = 5;

var wb_add_html = false;

var wb_dd_targets = new Array();  // ALL Target boxes on the page
var _ID = 0, _BOXES = 1, _RESPONSE = 2, _CORRECT = 3;

// For multiple selection MFIB checking
var _GP_NAME = 0, _GP_LIST = 1, _GP_SEPARATOR = "|", _GP_INDEX = 1;

var wb_current_box = '';

function getElementsByClass(searchClass,tag) {
  var classElements = new Array();
  if (tag == null)
    tag = '*';
  var els = document.getElementsByTagName(tag);
  var elsLen = els.length;
  var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
  for (i = 0, j = 0; i < elsLen; i++) {
    if (pattern.test(els[i].className) ) {
      classElements[j] = els[i];
      j++;
    }
  }
  return classElements;
}

function toggleConditionalDisplay(e,container){
	var vcurrentState = document.getElementById(e).style.display;

	if (container){
		var hidethese = getElementsByClass(('conditional' + container),'div');
		for (i = 0; i < hidethese.length; i++)
		{ 
		hidethese[i].style.display = 'none'
		}
	}

	if ((e) && (document.getElementById(e) != null) && (vcurrentState != 'block')) {
			document.getElementById(e).style.display = 'block';
	}
}

function escapeInput(t){
	t = t.replace(/&(lt|gt);/g, function (strMatch, p1){
 		 	return (p1 == "lt")? "<" : ">";
 		})
 		var strTagStrippedText = t.replace(/<\/?[^>]+(>|$)/g, "");
		q = strTagStrippedText.replace(/\n/g, " ");
		//q = strTagStrippedText.replace(/'/g, "\\'");
 		//alert("Output text:\n" + t + "\n" + strTagStrippedText + "\n" + q);	
		return q;
// Use the alert below if you want to show the input and the output text
   //	
	
}

/*function aeRecordMark(e, v)
{
	alert(e + ',' + v );
}*/

function wb_updateSelectBoxState(e, container){
	if (window[e]){
	   for (var i=0; i<document.getElementById(e).length; i++)
	   	if (document.getElementById(e)[i].value == window[e]) {
	   		var x = document.getElementById(e)
	   		x.options[i].selected = true;
	   		if (container) toggleConditionalDisplay(document.getElementById(e)[i].value , container);
		}
	
	
	}


}
function wb_updateQgroupStatus(e,status){
	if ((window[e + '_started']) || (status)) {
	  // hide the 'question not started indicator'
	  if (document.getElementById(e + '_status_indicator0')) document.getElementById(e + '_status_indicator0').style.display = 'none';
	  if (document.getElementById(e + '_status_indicator1')) document.getElementById(e + '_status_indicator1').style.display = 'block';
	} else {
	  if (document.getElementById(e + '_status_indicator1')) document.getElementById(e + '_status_indicator1').style.display = 'none';
	  if (document.getElementById(e + '_status_indicator0')) document.getElementById(e + '_status_indicator0').style.display = 'block';
	}
}

function wb_saveAnswer(e, v, t, p)
// --------------------------
{
	if (typeof aeRecordMark == "function"){
			
		if ((t) && (t=="mcq" || t=="mrq" || t=="tf")){
			// multiple-choice question
			if ((t=="mrq") && !(document.getElementById(e).checked)){
				if (document.getElementById(v + '_value')) aeRecordMark((e + '_value'),'');
				if (document.getElementById(v + '_explanation')) aeRecordMark((e + '_explanation'),'');
			} else {
				aeRecordMark(e + "_type",t);
				if (document.getElementById(v + '_value')) aeRecordMark((e + '_value'),escapeInput(document.getElementById(v + '_value').value));
				if (document.getElementById(v + '_explanation')) aeRecordMark((e + '_explanation'),escapeInput(document.getElementById(v + '_explanation').value));
			}
		
		} else if ((t) && (v) && (t=="select")) {

			if ((document.getElementById(e)) && (document.getElementById(e)[(v - 1)])) {
				var selectBox = document.getElementById(e);
				var selectContent = selectBox[(v - 1)].innerHTML;
				aeRecordMark((e + '_value'),escapeInput(selectContent));
				wb_updatePreviousAnswer(e,escapeInput(selectContent));
			} else {
				var selectBoxIndex = document.getElementById(e).selectedIndex
				var selectBox = document.getElementById(e);
				var selectContent = selectBox[selectBoxIndex].innerHTML;	
				aeRecordMark((e + '_value'),escapeInput(selectContent));
				wb_updatePreviousAnswer(e,escapeInput(selectContent));
			
			//alert('aeRecordMark(' + typeof v + ':' + e + '_value,' + escapeInput(selectContent) + ')');

			}
		} else if (t) {
			aeRecordMark(e + "_type",t);
		} else if ((e) && (v)) {
			wb_updatePreviousAnswer(e,escapeInput(v));
		}
		//alert (v + '-->' + escapeInput(v));
		aeRecordMark(e,escapeInput(v));
		

		if (p){
			aeRecordMark((p + '_started'),'true');
			wb_updateQgroupStatus(p, 'true');
			
			//if (document.getElementById(p + '_status_indicator')) document.getElementById(p + '_status_indicator').style.display = 'inline';
		}

	}
}

function wb_retrieveAnswer(e)
// --------------------------
{
	if (window[e]){
		return window[e];
	}else{
		return ""
	}
	
	//return 'banan'
}

function wb_outputPreviousAnswer(e){
	if ((e) && (window[e + '_type']) && ((window[e + '_type']=='mcq') || (window[e + '_type']=='mrq')) ){
		if (window[e + '_explanation']){
			document.write('<span class="previousanswer">' + wb_retrieveAnswer(e + '_explanation') + '</span>');
		} else if (window[e + '_value']){
			//alert(window[e + '_value']);
			document.write('<span class="previousanswer">' + wb_retrieveAnswer(e + '_value') + '</span>');		
		}
	} else if ((e) && (window[e + '_type']) && (window[e + '_type']=='select')){
		if (window[e + '_explanation']){
			document.write('<span class="previousanswer">' + wb_retrieveAnswer(e + '_explanation') + '</span>');
		} else if (window[e + '_value']){
			//alert(window[e + '_value']);
			document.write('<span class="previousanswer">' + wb_retrieveAnswer(e + '_value') + '</span>');		
		}	
	} else {
		if (window[e + '_value']){
			document.write('<span class="previousanswer ' + e + '_wbInserted">' + wb_retrieveAnswer(e + '_value') + '</span>');	
		} else {	
			document.write('<span class="previousanswer ' + e + '_wbInserted">' + wb_retrieveAnswer(e) + '</span>');
		}
	}
}

function wb_updatePreviousAnswer(e,v){
	// update any spans containing this value as autogenerated content
	var genboxes = getElementsByClass(e + '_wbInserted');
	for (i = 0; i < genboxes.length; i++) {
		if ((e) && (window[e + '_type']) && ((window[e + '_type']=='mcq') || (window[e + '_type']=='mrq')) ){
			if (window[e + '_explanation']){
			  genboxes[i].innerHTML = v
			} else if (window[e + '_value']){
			  genboxes[i].innerHTML = v
			}  
		} else if ((e) && (window[e + '_type']) && (window[e + '_type']=='select')){
			if (window[e + '_explanation']){
				  genboxes[i].innerHTML = v
			} else if (window[e + '_value']){
				  genboxes[i].innerHTML = v
			}	
		} else {
			if (window[e + '_value']){
				genboxes[i].innerHTML = v
			} else {	
				genboxes[i].innerHTML = v
			}
		}
	}
}


function wb_showPreviousAnswer(e)
{
	//alert('wb_showPreviousAnswer(' + e + ',' + wb_retrieveAnswer(e) + ',' + document.getElementById(e) + ')');
				//alert (e +',' + wb_retrieveAnswer(e) + ',' + window[e + '_type']);
	if ((wb_retrieveAnswer(e) != '') && (document.getElementById(e) != null))
	{

		if ((e) && (window[e + '_type']) && ((window[e + '_type']=='mcq') || (window[e + '_type']=='mrq'))){
			// show the previous answer for an mcq questions
			//alert(e + ' is of type ' + window[e + '_type']);
			var mcqOptions = document.getElementsByName(e);
			for (i = 0; i < mcqOptions.length; i++) {
				//alert(mcqOptions[i].id + ',' + wb_retrieveAnswer(e));
				if (mcqOptions[i].id == wb_retrieveAnswer(e)){
					//alert('check');
					mcqOptions[i].checked = true;
					//alert(mcqOptions[i].id.checked);
				} else {
					mcqOptions[i].checked = false
				
				}
			}
		} else if ((e) && (window[e + '_type']) && (window[e + '_type']=='select')){
			var selectBox = document.getElementById(e);
			selectBox[(wb_retrieveAnswer(e) - 1)].selected = true;
		} else {
			document.getElementById(e).value = (wb_retrieveAnswer(e));
		}
	}

}

function isInGroup(e, l)
// --------------------------
{
var sl;
// alert("IsInGroup: checking " + e + " against " + l);
  if (l == null || l == "") return false;  // Empty so not in the group

  sl = l.split(_GP_SEPARATOR);  // A string with | separators
  for (var i=0; i<sl.length; i++)
    if (e == sl[i]) return true;

  return false;
}

function wb_revealElement(e, b, qt) 
// --------------------------
{

    if (document.getElementById(e) != null) {
        if (b) {
	    if ((qt) && (qt == 'highlight')){
	        document.getElementById(e).style.display = 'block';
            }else if ((qt) && (qt == 'hint')){
				document.getElementById(e).style.display = 'block';
			}else{
				document.getElementById(e).style.display = 'inline';
			}
        }
        else {
            document.getElementById(e).style.display = 'none';
        }
    }
}

function wb_numericMatch(a, b)
// --------------------------
{
var _a = a, _b = b;

	// Strip out any commas
	_a = _a.replace(/,/ig, "");  _b = _b.replace(/,/ig, "");

	// Change (x) negatives to a -x negative
	_a = _a.replace(/\((\d*)\)/, "-$1");  _b = _b.replace(/\((\d*)\)/, "-$1");

	if (_a == _b) return true;
	else return false;
}

function wb_revealHint(id, b)
// --------------------------
{
    wb_revealElement(id + "_hint", b, "hint"); // Reveal the Addendum
}

function wb_revealHighlightAnswer(id, b, qtype)
// --------------------------
{
    wb_revealElement(id + "highlightanswer", b, qtype); // Reveal the Addendum
}

function wb_revealAddendum(id, b, qtype)
// --------------------------
{
    wb_revealElement(id + "_addendum", b, qtype); // Reveal the Addendum
    wb_revealElement(id + "_submit", !b);  // Hide the submit button (not highlight)
}

function wb_revealStatus(id, b)
// --------------------------
{
    wb_revealElement(id + "_q_0", b);  wb_revealElement(id + "_q_1", !b);
    wb_revealElement(id + "_reveal", b);
    wb_revealElement(id + "_undo", b);
}

function wb_q_reset(qtype, fid, eid, parentid) 
// ---------------------------------
{  
    var i = 0, j = 0, f = eval("document." + fid + "_form");
    f.reset();  // Reset the form

    // Hide everything, except Submit button
    wb_revealAddendum(eid, false);
    wb_revealElement(eid + "_q_0", false);  wb_revealElement(eid + "_q_1", false);
	wb_revealElement(eid + "_reveal", false);
    wb_revealElement(eid + "_undo", false);

	// record the question as not answered
	if (document.getElementById(parentid + 'total_score')){
		totalScoreBox = document.getElementById([parentid + "total_score"]);
		var unanswered_questions = 0;
		var current_score = 0;
		if ((window[parentid + "_marks"])){
			marksArray = window[parentid + "_marks"];
			marksArray[eid] = 0;
			for (key in marksArray) current_score += (marksArray[key] * 1);
			if ((window[parentid + "_answered"])){
				answeredArray = window[parentid + "_answered"];
				answeredArray[eid] = false;
				for (key in answeredArray) unanswered_questions += answeredArray[key]? 0 : 1;
			}
		}
		if (unanswered_questions <= 0){
			totalScoreBox.style.visibility="visible";
		}else{
			totalScoreBox.style.visibility="hidden";
		}	
	}
    // ... and finally hide any previous answers
    if (qtype == "flash") {
        wb_swf_doMovieReset(eid);
    }
    if (qtype == "highlight") {
        var e = document.getElementById(eid + "_highlight")
        if (wb_original != "") e.innerHTML = wb_original;

        // restore highlight buttons and Undo and Next
        wb_revealElement(eid + "_undo", true); wb_revealElement(eid + "_next", true); 
        wb_revealElement(eid + "_hl_yellow", true); 
        wb_revealElement(eid + "_hl_red", true); 
        wb_revealElement(eid + "_hl_green", true); 
        wb_revealElement(eid + "_hl_blue", true); 
    	// hide the answer to the highlight question
    	wb_revealHighlightAnswer(eid, false, qtype);
    }
    if (qtype == "sa") {
	// Nothing extra
    }
    else {
        for (i=0,j=0; i<f.elements.length; i++) {
    	    if (f.elements[i].name.indexOf(eid) == 0) {  // Make sure it is one of our from elements
    	        switch (qtype) {
    	            case "tf":
    	            case "mcq":
                        if (f.elements[i].type == "radio") {
                            wb_revealElement(eid + "_" + (j+1), false); 
    	                // wb_revealElement(eid + "_m_" + (j+1), false);
    
    		        j++;
                        }
    		        break;
                    case "mrq":
                        if (f.elements[i].type == "checkbox") {
                            wb_revealElement(eid + "_" + (j+1), false); 
    	                // wb_revealElement(eid + "_m_" + (j+1), false);
    
    		        j++;
                        }
    		        break;
                    case "mfib":
                        if (f.elements[i].type == "text") {
                            var root = f.elements[i].name;  // Pick up the name root
                            wb_revealElement(root + "_i_0", false); 
                            wb_revealElement(root + "_i_1", false); 
                        }
                        break;
                    case "select":
			var root = f.elements[i].name;
			wb_revealElement(root + "_i_0", false);
			wb_revealElement(root + "_i_1", false);
			wb_saveAnswer(root, '', qtype, parentid)
			//alert(root +','+ f.elements[i].type);
	
                        break;
                    default:
    	                wb_err = "Unknown Question Type";
                }
            }
        }
    }
}


function wb_q_reveal(qtype, fid, eid) 
// ----------------------------------
{  
    var i = 0, j = 0, f = eval("document." + fid + "_form");

    wb_revealAddendum(eid, true, qtype); 
    wb_revealElement(eid + "_q_0", false);  wb_revealElement(eid + "_q_1", false);
	wb_revealElement(eid + "_reveal", false);

    // ... and finally hide any previous answers
    if (qtype == "flash") {
        var h = eval("document." + fid + "_form." + eid + "_c");
        wb_swf_doReset(eid, h.value, h.value);  // Showing the right answers
    }
    if (qtype == "highlight") {
        // Hide highlight buttons and Undo and Next
        wb_revealElement(eid + "_undo", false); wb_revealElement(eid + "_next", false); 
        wb_revealElement(eid + "_hl_yellow", false); 
        wb_revealElement(eid + "_hl_red", false); 
        wb_revealElement(eid + "_hl_green", false); 
        wb_revealElement(eid + "_hl_blue", false); 
    	wb_revealHighlightAnswer(eid, true, qtype);
    }
    if (qtype == "sa") {
	// Nothing extra
    }
    else {
	var gpindex = -1;
	var gpa = new Array(), gpansindex = new Array();  // Group arrays used in mfibs and selects with groups.

        for (i=0,j=0; i<f.elements.length; i++) {
    	    if (f.elements[i].name.indexOf(eid) == 0) {  // Make sure it is one of our from elements
    	        switch (qtype) {
    	            case "tf":
    	            case "mcq":
                        if (f.elements[i].type == "radio") {
                            wb_revealElement(eid + "_" + (j+1), true); 
    	                    // wb_revealElement(eid + "_m_" + (j+1), true);
    
    		            j++;
                        }
    		        break;
                    case "mrq":
                        if (f.elements[i].type == "checkbox") {
                            wb_revealElement(eid + "_" + (j+1), true); 
    	                    // wb_revealElement(eid + "_m_" + (j+1), true);
    
    		            j++;
                        }
                        break;
                    case "mfib":
			// Is it in a group?  This will be depicted by the root plus an _g.  
			gpindex = -1;  // Signify not, until proven wrong.

			// The value and the group name 
                        var root = f.elements[i].name;  // Pick up the name root
			var fv = eval("document." + eid + "_form." + root + "_v_1");
			var fg = eval("document." + eid + "_form." + root + "_g");  
			if (fg != null) {
				// Start a new group and record the answers?
				var got_it = false;
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						got_it = true;  break;
					}
				}

				if (!got_it) {
					gpa.push(new Array(fg.value, fv.value));  
					// Push the group name and the split of the answers
					gpansindex.push(new Array(fg.value, 0));  // prepare answer index
				}

				// Find the group index (default is -1, but it should be there).
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						gpindex = j;  break;
					}
				}				
			}

                        if (f.elements[i].type == "text") {
                            var root = f.elements[i].name;  // Pick up the name root
                            wb_revealElement(root + "_i_0", false);  // Hide mark images
                            wb_revealElement(root + "_i_1", true); 
    
                            // Plug in the right values
			    if (gpindex == -1) {
	    			f.elements[i].value = fv.value;  // Not in a group
			    }
			    else {  // Use the n'th element of the group of values
				var ind = gpansindex[gpindex][_GP_INDEX];
				f.elements[i].value = ((gpa[gpindex][_GP_LIST]).split(_GP_SEPARATOR))[ind];				
				gpansindex[gpindex][_GP_INDEX]++;  // up the index				
			    }
                        }
                        break;

                    case "select":
                        if (f.elements[i].type == "select-one") {
			    // Is it in a group?  This will be depicted by the root plus an _g.  
			    gpindex = -1;  // Signify not, until proven wrong.

                            var root = f.elements[i].name;  // Pick up the name root
                            wb_revealElement(root + "_i_0", false);  // Hide mark images
                            wb_revealElement(root + "_i_1", true); 
    
                            // Plug in the right values (correct field)
    			    var fc = eval("document." + fid + "_form." + root + "_c");
			    var fg = eval("document." + eid + "_form." + root + "_g");  
			    if (fg != null) {
				// Start a new group and record the answers?
				var got_it = false;
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						got_it = true;  break;
					}
				}

				if (!got_it) {
					gpa.push(new Array(fg.value, fc.value));  
					// Push the group name and the split of the answers
					gpansindex.push(new Array(fg.value, 0));  // prepare answer index
				}

				// Find the group index (default is -1, but it should be there).
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						gpindex = j;  break;
					}
				}				
			    }

			    if (gpindex == -1) {
				    var val = fc.value - 1;
	    			    f.elements[i].options[val].selected = true;
			    }
			    else {  // Use the n'th element of the group of values
				var ind = gpansindex[gpindex][_GP_INDEX];  // The lookup index, then the value
				ind = ((gpa[gpindex][_GP_LIST]).split(_GP_SEPARATOR))[ind];
				f.elements[i].options[ind-1].selected = true;  // Zero based index
		
				gpansindex[gpindex][_GP_INDEX]++;  // up the index				
			    }
                        }
                        break;
                    default:
    	                wb_err = "Unknown Question Type";
                }
            }
        }
    }
}

/*
Finds the appropriate question type and question and checks the given answer with that provided.
*/
function wb_q_checker(qtype, fid, eid, parentid) 
// -----------------------------------
{  
    var val = "";
    var ok = false;
    var f;  // The form
    var i = 0, j = 0, elCount = 0, checkRightCount = 0;

    wb_err = "";  f = eval("document." + fid + "_form");

	

    switch (qtype) {
        case "tf":
        case "mcq":
            // Find the answers supplied, if any
            for (i=0; i<f.elements.length; i++) {
		if (f.elements[i].name.indexOf(eid) == 0) {
		    // Make sure it is one of our from elements
                    if (f.elements[i].type == "radio" && f.elements[i].checked) {
		        ok = true;  // We have some answer
                        val = f.elements[i].value;
                    }

                    if (f.elements[i].type == "radio") elCount++;
                }
	    }

            if (ok) {  // There is an answer
                // This is an MCQ.  THere may be more than one right answer but we don't really care
	        var fc = eval("document." + fid + "_form." + eid + "_c_" + val);

                // Finally reveal the Addendum and the answers, if right
                for (i=0; i<elCount; i++) {            // Show right/wrong and markval 
			if (val == (i+1)) {  // Only reveal your attempt
               if (!document.getElementById(parentid + 'total_score')) wb_revealElement(eid + "_" + (i+1), true); 
			   var mark_value = 0;
			   var mark_element = document.getElementById(eid + "_m_" + (i+1));
			   if (mark_element) {
				//alert('"' + mark_element.innerHTML + '"');
				mark_value = (mark_element.innerHTML == '')? 0 : mark_element.innerHTML;
			}

				// save the marks for the question on the question_marks array
				var current_score = 0;
				var unanswered_questions = 0;

				if (document.getElementById(parentid + 'total_score')){
					// the page has a Total Score box
					if ((window[parentid + "_marks"])){
						marksArray = window[parentid + "_marks"];
						marksArray[eid] = mark_value;
						for (key in marksArray) current_score += (marksArray[key] * 1);
						if ((window[parentid + "_answered"])){
							answeredArray = window[parentid + "_answered"];
							answeredArray[eid] = true;
							for (key in answeredArray) unanswered_questions += answeredArray[key]? 0 : 1;
						}
					}
					totalScoreBox = document.getElementById([parentid + "total_score"]);
					totalScoreBox.innerHTML = current_score;

					if (unanswered_questions <= 0){
						totalScoreBox.style.visibility="visible";
					}else{
						totalScoreBox.style.visibility="hidden";
					}
					if (typeof aeRecordMark == 'function')aeRecordMark(parentid, current_score);
				}
			}
			else {
                    		wb_revealElement(eid + "_" + (i+1), false); 
                                // wb_revealElement(eid + "_m_" + (i+1), false);
			}
	        }

		if (fc.value == "Y") {  // Is our answer correct?
                	wb_revealAddendum(eid, true);  wb_revealStatus(eid, false);
		}
		else {
			wb_revealStatus(eid, true);
		}
            }
            else {
                wb_err = "Please provide an answer.";
            }

            break

        case "mrq":
	    var myRights = 0;

            // Find the answers supplied, if any
            for (i=0,j=0; i<f.elements.length; i++) {
		if (f.elements[i].name.indexOf(eid) == 0) {
		    // Make sure it is one of our from elements

                    if (f.elements[i].type == "checkbox" && f.elements[i].checked) {
		        ok = true;  // We have some answer

                        if (val != "") val = val + ",";  // Build up the answer string (for MRQ).
                        val = val + f.elements[i].value;
                    }

                    if (f.elements[i].type == "checkbox") elCount++;
                    if (f.elements[i].value == "Y") checkRightCount++;
                }
	    }

            if (ok) {  // There is an answer
                for (i=0; i<elCount; i++) {            // Show right/wrong and markval 
                    wb_revealStatus(eid, false);
	        }

                // Cycle through all answers
		var vs = val, val1 = "";
		while (vs.length > 0) {		
			// Pick up the next value
			if (vs.indexOf(",") == -1) {  
				val1 = vs;  vs = "";
			}
			else {
				val1 = vs.substring(0, vs.indexOf(","));  vs = vs.substring(vs.indexOf(",")+1);
			}

	        	var fc = eval("document." + fid + "_form." + eid + "_c_" + val1);
			if (fc.value == "Y") myRights++; else myRights--;

                	// Finally the answers, if right
                    	wb_revealElement(eid + "_" + val1, true); 
                        // wb_revealElement(eid + "_m_" + val1, true);
	        }

		if (myRights == checkRightCount) {  // Is our answer correct?
                	wb_revealAddendum(eid, true);	wb_revealStatus(eid, false);
		}
		else {
			wb_revealStatus(eid, true);
		}
            }
            else {
                wb_err = "Please provide an answer.";
            }

            break;

        case "mfib":
	    var myRights = 0, gpindex = -1;
	    var gpa = new Array(), gpans = new Array();  // Group arrays
            // Find the answers supplied, if any
            for (i=0; i<f.elements.length; i++) {
		if (f.elements[i].name.indexOf(eid) == 0) {
                    if (f.elements[i].type == "text") elCount++;


		    // Make sure it is one of our from elements
                    if (f.elements[i].type == "text" && f.elements[i].value != "") {
		        ok = true;  // We have some answer

                        var root = f.elements[i].name;  // Pick up the name root
			var ft = eval("document." + eid + "_form." + root + "_t");
			var fv = eval("document." + eid + "_form." + root + "_v_1");
			// var fm = eval("document." + eid + "_form." + root + "_m_1");

			// Is it in a group?  This will be depicted by the root plus an _g.  
			gpindex = -1;  // Signify not, until proven wrong.

			// The value is the group name 
			var fg = eval("document." + eid + "_form." + root + "_g");  
			if (fg != null) {
				// Start a new group and record the answers?
				var got_it = false;
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						got_it = true;  break;
					}
				}

				if (!got_it) {
					gpa.push(new Array(fg.value, fv.value));  // Push the group name and answers
					gpans.push(new Array(fg.value, ""));  // prepare answers
				}

				// Find the group index (default is -1, but it should be there).
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						gpindex = j;  break;
					}
				}				
			}

		        if (ft.value == "numeric") {
				// Could be in a group
				if (wb_numericMatch(f.elements[i].value, fv.value) ||
					(gpindex != -1 && wb_numericMatchGroup(f.elements[i].value, gpa[gpindex][_GP_LIST]))) {
					if (gpindex != -1) {
						if (!isInGroup(f.elements[i].value, gpans[gpindex][_GP_LIST])) {
							myRights++;  // Right
							wb_revealElement(root + "_i_1", true);  
							wb_revealElement(root + "_i_0", false);
							// Now add this entry to the list
							if (gpans[gpindex][_GP_LIST] != "") 
								gpans[gpindex][_GP_LIST] += _GP_SEPARATOR;
							gpans[gpindex][_GP_LIST] += f.elements[i].value;
					 	}
						else {  // Not right -- repeated
							wb_revealElement(root + "_i_0", true);  
							wb_revealElement(root + "_i_1", false);
						}
					}
					else {
						myRights++;
						wb_revealElement(root + "_i_1", true);  
						wb_revealElement(root + "_i_0", false);
					}
				}
				else {
					wb_revealElement(root + "_i_0", true);  wb_revealElement(root + "_i_1", false);
				}
			}
			else {  // Simple text
				// Could be in a group
				if (f.elements[i].value.toLowerCase() == fv.value.toLowerCase() || 
					(gpindex != -1 && isInGroup(f.elements[i].value, gpa[gpindex][_GP_LIST]))) {  // Insensitive match
					if (gpindex != -1) {
						if (!isInGroup(f.elements[i].value, gpans[gpindex][_GP_LIST])) {
							myRights++;  // Right
							wb_revealElement(root + "_i_1", true);  
							wb_revealElement(root + "_i_0", false);
							// Now add this entry to the list
							if (gpans[gpindex][_GP_LIST] != "") 
								gpans[gpindex][_GP_LIST] += _GP_SEPARATOR;
							gpans[gpindex][_GP_LIST] += f.elements[i].value;
					 	}
						else {  // Not right -- repeated
							wb_revealElement(root + "_i_0", true);  
							wb_revealElement(root + "_i_1", false);
						}
					}
					else { // It's not repeated, it's OK
						myRights++;
						wb_revealElement(root + "_i_1", true);  
						wb_revealElement(root + "_i_0", false);
					}
				}
				else {
					wb_revealElement(root + "_i_0", true);  wb_revealElement(root + "_i_1", false);
				}
			}		
                    }
                }
	    }

	    if (ok) {
	        if (myRights == elCount) {  // Is our answer correct?
               	    wb_revealAddendum(eid, true);	wb_revealStatus(eid, false);
	        }
	        else {
		    wb_revealStatus(eid, true);
	        }
            }
            else {
                wb_err = "Please provide an answer.";
            }

            break;

        case "select":
	    var myRights = 0, gpindex = -1;
	    var gpa = new Array(), gpans = new Array();  // Group arrays
            var root = "", fc;

	    ok = true;  // Assume all correct for now

            // There is always an answer; cycle thru the Selects and check them
            for (i=0; i<f.elements.length; i++) {
		if (f.elements[i].name.indexOf(eid) == 0) {
		    // Make sure it is one of our from elements
                    if (f.elements[i].type == "select-one") {
                        val = f.elements[i].selectedIndex;  // The 0-based index of the selection
                        root = f.elements[i].name;          // Pick up the name root
			fc = eval("document." + fid + "_form." + root + "_c");  // Pick up the Select correct answer

			// Is it in a group?  This will be depicted by the root plus an _g.  
			gpindex = -1;  // Signify not, until proven wrong.

			// The value is the group name 
			var fg = eval("document." + eid + "_form." + root + "_g");  
			if (fg != null) {
				// Start a new group and record the answers?
				var got_it = false;
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						got_it = true;  break;
					}
				}

				if (!got_it) {
					gpa.push(new Array(fg.value, fc.value));  // Push the group name and answers
					gpans.push(new Array(fg.value, ""));  // prepare answers
				}

				// Find the group index (default is -1, but it should be there).
				for (var j=0; j<gpa.length; j++) {
					if (gpa[j][_GP_NAME] == fg.value) {
						gpindex = j;  break;
					}
				}				
			}
//alert("Checking " + f.elements[i].options[val].value + " against " + fc.value);
			if (f.elements[i].options[val].value == fc.value ||
				(gpindex != -1 && isInGroup(f.elements[i].options[val].value, gpa[gpindex][_GP_LIST]))) {  
				// It's could be Right, but is it repeated within a group
				if (gpindex != -1) {
					if (!isInGroup(f.elements[i].options[val].value, gpans[gpindex][_GP_LIST])) {
						wb_revealElement(root + "_i_1", true);  
						wb_revealElement(root + "_i_0", false);
						// Now add this entry to the list
						if (gpans[gpindex][_GP_LIST] != "") 
							gpans[gpindex][_GP_LIST] += _GP_SEPARATOR;
						gpans[gpindex][_GP_LIST] += f.elements[i].options[val].value;
				 	}
					else {  // Not right -- repeated
						wb_revealElement(root + "_i_0", true);  
						wb_revealElement(root + "_i_1", false);
					}

				}
				else {  // Not right -- repeated
					wb_revealElement(root + "_i_1", true);	wb_revealElement(root + "_i_0", false);
				}
			}
   			else {  // Wrong
				wb_revealElement(root + "_i_0", true);	wb_revealElement(root + "_i_1", false);

				ok = false;  /// Oops, one wrong at least.
			}
                    }
                }
	    }

            if (ok) {  // Got them all right
                wb_revealAddendum(eid, true);  wb_revealStatus(eid, false);
	    }
	    else {
		wb_revealStatus(eid, true);
            }

	    ok = true;  // Set the return status to say all was well.
            break

        case "flash":
            var h = eval("document." + fid + "_form." + eid + "_c");
            ok = true;
	    // Mark the animation
	    wb_swf_doReset(eid, h.value, wb_swf_getCurrentValues(eid));

	    if (h.value == wb_swf_getCurrentValues(eid)) {  // Answer is right
               	wb_revealAddendum(eid, true);	wb_revealStatus(eid, false);  // Assume wrong
            }
            else {
                wb_revealStatus(eid, true);
            }

            break;
        case "sa":
            ok = true;
            wb_revealAddendum(eid, true);
            break;
        default:
	    wb_err = "Unknown Question Type";
    }

    return ok;
}

function wb_hl_checker(col, eid)
//------------------------------
{
    var ie = document.getElementById(eid + "_highlight")

    if (wb_original == "" && ie != null) {
        wb_original = ie.innerHTML;  // Save the pristine state
    }

    // Picks up the first occurrence of the string, not necessarily the right one!!!
    if (wb_getSelection() != "") {
        wb_selection = String(wb_getSelection());  wb_colour = col;
        wb_revealStatus(eid, true);
        wb_setSelectionRange(col);
    }

    return true;
}

function wb_hl_undo(eid)
//----------------------
{
	if (wb_original != "") {
 		var e = document.getElementById(eid + "_highlight");
		e.innerHTML = wb_original;  // Should have been set by hl_checker

		wb_original = "";  // Can only do this once
	}
	else {
		alert("Nothing to undo!");
	}
}

function wb_hl_next(eid)
//----------------------
{
    if (wb_match == "") {
        alert("No previous selection.");  return;
    }

    // We have the previous match result.  Look for the next occurence, if any
    // Look for the selection in the second half of the string
    // Restore the original text
    var e = document.getElementById(eid + "_highlight");
    e.innerHTML = wb_match[1] + wb_match[2] + wb_match[3];
    var si = e.innerHTML;  // Reread this out

    // Can't use this in IE as it changes the underlying text by u/c tags and adding \n chars
    // after <p>s.  Annoying, as I can't use this to do a pre-match.
    //regex = new RegExp("(" + wb_match[1] + wb_selection + "(?:.|\n)*?)(" + wb_selection + ")((?:.|\n)*)", "i");

    // Check that there is another selection to come
    if (wb_match[3].indexOf(wb_selection) != -1) {
        regex = new RegExp("((?:(?:.|\n)*?" + wb_selection + "(?:.|\n)*?){" + wb_occurrence + "})(" + wb_selection + ")((?:.|\n)*)", "i");
        wb_match = si.match(regex);  // Holds the three bits of the match

        if (wb_match != null) {
  	    // Now do a replace in the second section
	    e.innerHTML = wb_match[1] + 
                          "<span class=\"wb_hl_" + wb_colour + "\">" + wb_selection + "</span>" +
                          wb_match[3];
            wb_occurrence++;  // Looking for the next match
        }
    }
    else {
        alert("No more occurrences.");  // Wrap around
        e = document.getElementById(eid + "_highlight")
        si = e.innerHTML;

        regex = new RegExp("((?:.|\n)*?)(" + wb_selection + ")((?:.|\n)*)", "i");   
        // A non-greedy match to pick up the first bit
	wb_match = si.match(regex);  // Holds the two halfs of the match
	wb_occurrence = 1;

        // Isolate the innerHTML and replace the selection
	e.innerHTML = wb_match[1] +
                      "<span class=\"wb_hl_" + wb_colour + "\">" + wb_match[2] + "</span>" +
                      wb_match[3];
    }
}

function wb_swf_getCurrentValues(eid)
//---------------------------------
{
    return window.document[eid].GetVariable("appletValues");  // User state
}

function wb_swf_doMovieReset(eid)
//----------------------------
{
    window.document[eid].doMovieReset();
    window.document[eid].initialise();
}

function wb_swf_doReset(eid, c, r)
// ------------------------------
{
    window.document[eid].SetVariable("correctValues", c);  
    window.document[eid].SetVariable("responseValues", r);  
    window.document[eid].initialise();
}

// ========================= Selection Methods =============================


function wb_getSelection()
// -----------------------
{

    if (window.getSelection) { // Firefox, Opera
        return window.getSelection();
    }
    else if (document.getSelection) {
        return document.getSelection();
    }
    else if (document.selection) {  // IE
        return document.selection.createRange().text;
    }

    return "";
}

function wb_setSelectionRange(col)
// -------------------------------
{
var e = document.createElement("span");

    e.setAttribute("class", "wb_hl_" + col);

    if (window.getSelection) { // Firefox, Mozilla
        window.getSelection().getRangeAt(0).surroundContents(e);
    }
    else if (document.getSelection) {  // Netscape
        document.getSelection().getRangeAt(0).surroundContents(e);
    }
    else if (document.selection) {  // IE
        document.selection.createRange().pasteHTML("<span class=\"wb_hl_" + col + "\">"
                                        + document.selection.createRange().text
+ "</span>");
    }
}

// ===================== Drag and Drop ===========================


function isNotInObjList(e, l)
// --------------------------
{
  for (var i=0; i<l.length; i++)  // Element 0 not used.
    if (e == l[i]) return false;

  return true;
}

function wb_setDandD(id)  // Reveal
// ---------------------
{
var resp, correct, box, boxes, objList = new Array(wb_dd_targets.length);

  // wb_resetDandD(id);  // Start afresh

  for (var i=0; i<wb_dd_targets.length; i++) {  // Look for our box set
    if (wb_dd_targets[i][_ID] == id) {
      boxes = wb_dd_targets[i][_BOXES];  correct = wb_dd_targets[i][_CORRECT]; 
      break;
    }
  }

  for (var i=0; i<correct.length; i++) {  // Should have the same no of boxes & responses
    box = boxes[i];  resp = id + "_" + correct[i];

    // The argument dictates whatgoes in this box
    for (var j=0; j< dd.elements.length; j++) {
      if (dd.elements[j].grp == id) {  // Is it part of our group
        if (dd.elements[j].name.indexOf(resp) == 0 && isNotInObjList(dd.elements[j].name, objList)) {
          dd.obj = dd.elements[j];   objList[i] = dd.elements[j].name;

          dd.obj.moveTo((box[1]+box[3]-dd.obj.w)/2, (box[2]+box[4]-dd.obj.h)/2);
          break;  // From the inner loop
        }
      }
    }

    wb_revealElement(box[0] + "_i_0", true);  wb_revealElement(box[0] + "_i_1", false);  // Correct
  }
}

function wb_checkDandD(id)
// -----------------------
{
var box, boxes, resp, response;

  for (var i=0; i<wb_dd_targets.length; i++) {  // Look for our box set
    if (wb_dd_targets[i][_ID] == id) {

      boxes = wb_dd_targets[i][_BOXES]; 
      response = wb_dd_targets[i][_RESPONSE]; correct = wb_dd_targets[i][_CORRECT];
      break; 
    }
  }

  for (var i=0; i < correct.length; i++) {  // Should have the same no of boxes & responses ** KWC **
    box = boxes[i];  resp = id + "_" + correct[i];

    if (response[i] != null && response[i].indexOf(resp) == 0) {  // Fine if we're not checking op1 with op11
      wb_revealElement(box[0] + "_i_0", true);  // Hide mark images
      wb_revealElement(box[0] + "_i_1", false);
    }
    else {
      wb_revealElement(box[0] + "_i_0", false);  // Hide mark images
      wb_revealElement(box[0] + "_i_1", true);
    }
  }
  
  wb_revealElement(id + "_reveal", true);
}

function wb_resetDandD(id)
// -----------------------
{
  for (var j=0; j< dd.elements.length; j++) {
    if (dd.elements[j].grp == id) {  // Limit the rest to our group
      if (dd.elements[j].myValueSet != undefined) {
        dd.elements[j].myValueSet = undefined;
      }

      dd.obj = dd.elements[j];    dd.obj.moveTo(dd.obj.defx, dd.obj.defy);  // Back to defined start position
      wb_revealElement(dd.elements[j].id + "_i_0", false);   
      wb_revealElement(dd.elements[j].id + "_i_1", false);  // Hide mark images
    }
  }

  wb_revealElement(id + "_reveal", false);
  for (var i=0; i<wb_dd_targets.length; i++) {  // Look for our box set
    if (wb_dd_targets[i][_ID] == id)  wb_dd_targets[i][_RESPONSE] = "";   // Wipe clean the memory
  }
}

function wb_dd_build_targets()
// -----------------------------
{
var s, args = arguments;

  // The set of hot areas in a 4 part array: 
  // (i) id, (ii) array of boxes, (iii) array of responses, (iv) correct answer

  s = "wb_dd_targets.push(new Array('" + args[0] + "', [";
  for (var i=1; i<=args[args.length-1]; i++) {  // The last arg is the number of boxes
    s += "['" + args[0] + "_box" + i + "', ";
    s += wb_where_x(args[0] + "_box" + i) + ", ";
    s += wb_where_y(args[0] + "_box" + i) + ", ";
    s += (wb_where_x(args[0] + "_box" + i) + wb_where_right(args[0] + "_box" + i)) + ", ";
    s += (wb_where_y(args[0] + "_box" + i) + wb_where_bottom(args[0] + "_box" + i));
    s += "]";
    if (i < args.length-2) s += ",";
  }

  s += "], new Array(" + (args.length-2) + "), new Array("
  for (var i=1; i<args.length-1; i++) {
    s += "'" + args[i] + "'";
    if (i < args.length-2) s += ",";
  }
  s += ")));";
	  return s;
}
function wb_writeDandDOptions() 
// ----------------------------
{
var args = arguments;  // First arg is the Question id, or group id
var len = args.length, clone = 0;
var  wb_option_list = "";

  for (var i=1; i<len; i++) {
    if (clone == 0) {
      document.write("<div id='" + args[0] + "_" + args[i] + "' style='display:inline;position:relative;left:" + wb_option_left + ";width:" + wb_option_width + "em;text-align:center;background:lightgreen;border:solid black 1px;z-index:3'>" + args[i] + "</div>&nbsp;&nbsp;");
    }
    else {
      document.write("<div id='" + args[0] + "_" + args[i] + clone + "' style='display:inline;position:relative;left:" + (-clone*wb_option_width*8 + wb_option_inc) + ";width:" + wb_option_width + "em;text-align:center;background:lightgreen;border:solid black 1px;z-index:3'>" + args[i] + "</div>&nbsp;&nbsp;");
    }

    // Note the lack of the final comma and the addition of a clone number
    wb_option_list += "'" + args[0] + "_" + args[i] + ((clone == 0)? '' : clone) + "'";  
    if (i < len-1) {  // Are we at the last parameter
        wb_option_list += ",";

    	// Are they are the same word
	clone = (args[i+1] == args[i]) ? clone+1 : 0;
    }
  }

//  eval("ADD_DHTML(" + wb_option_list + ");");  // Just add, a div is already set
  dd.grp = args[0];
  if (wb_add_html == false){
    eval("SET_DHTML(" + wb_option_list + ");");  wb_add_html = true;
  }
  else {
    eval("ADD_DHTML(" + wb_option_list + ");");
  }
}

function wb_where_y(obj)
// -------------------
{
var y = 0, e = document.getElementById(obj);

  // Reset and do the same for the Y

  if (e && e.getBoundingClientRect) { // IE - KWC expanded this due to IE problems with margins
    var box = e.getBoundingClientRect();
    y = box.top + Math.max(document.documentElement.scrollTop, document.body.scrollTop);
  }
  else if (e.offsetParent) {
    do { y += e.offsetTop; } while (e == e.offsetParent);
  }
  else if (e.y) { y += e.y; }

  return y;
}

function wb_where_x(obj)
// -------------------
{
var x = 0, e = document.getElementById(obj);

  // Find the X
  if (e && e.getBoundingClientRect) { // IE - KWC expanded this due to IE problems with margins
    var box = e.getBoundingClientRect();
    x = box.left + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
  }
  else if (e.offsetParent) {
    do { x += e.offsetLeft; } while (e == e.offsetParent);
  }
  else if (e.x) { x += e.x; }

  return x;
}

function wb_where_right(obj)
// --------------------------
{
var e = document.getElementById(obj);

  return e.offsetWidth;
}

function wb_where_bottom(obj)
// --------------------------
{
var e = document.getElementById(obj);

  return e.offsetHeight;
}

<!-- ====================== the Drag and Drop functions =========================== -->

// Called on releasing a dragged object
// -------------------
function my_DropFunc()
// -------------------
{
  var target, boxes, found = 0;


  // midpoint of dragged object relative to static image
  var relx = dd.obj.x + (dd.obj.w/2);
  var rely = dd.obj.y + (dd.obj.h/2);

  for (target=0; target<wb_dd_targets.length; target++) {  // Look for our box set
    if (wb_dd_targets[target][_ID] == dd.obj.grp) {
      boxes = wb_dd_targets[target][_BOXES];

      break;  // Target is set for use elsewhere.
    }
  }

  for (var i = 0; i < boxes.length; i++) {
    var box = boxes[i];
    if (relx >= box[1] && relx <= box[3] && rely >= box[2] && rely <= box[4]) {

      // Found the hotspot that we are dropping on.  Centre
      dd.obj.moveTo((box[1]+box[3]-dd.obj.w)/2, (box[2]+box[4]-dd.obj.h)/2);

      // Check whether we have moved ontop of an existing element, in which
      // case move the old one back to its original position
      var boxobj = dd.elements[box[0]];  // box[0] holds the name
      if (boxobj.myValueSet != undefined) {
        boxobj.myValueSet.moveTo(boxobj.myValueSet.defx,boxobj.myValueSet.defy);
      }

      boxobj.myValueSet = dd.obj;  // Defines myValueSet
      wb_dd_targets[target][_RESPONSE][i] = dd.obj.name;  found = 1;    //build up the answer

      // Check that this name doesn't appear in an earlier box
      for (var j=0; j<boxes.length; j++) {
        if (j != i && wb_dd_targets[target][_RESPONSE][j] == dd.obj.name) {
	  // It does, so remove the earlier trace.
          dd.elements[boxes[j][0]].myValueSet = undefined;
          wb_dd_targets[target][_RESPONSE][j] = "";
          break
        }
      }

      break;  // Look no further
    }
  }

  if (found != 1) { // Bounce randomly dropped image back to starting pos
    dd.obj.moveTo(dd.obj.defx, dd.obj.defy);

    for (var i = 0; i < wb_dd_targets[target][_RESPONSE].length; i++) {
      // If this option was removed from a target box, de-register it
      if (wb_dd_targets[target][_RESPONSE][i] == dd.obj.name) {
        dd.elements[boxes[i][0]].myValueSet = undefined;
        wb_dd_targets[target][_RESPONSE][i] = "";
      }
    }
  }

  if (wb_current_box != '') {
      wb_current_box.bkgdim();  wb_current_box = '';
  }
}


// Called whilst dragging
//--------------------
function my_DragFunc()
//--------------------
{
  // midpoint of dragged object relative to static image
  var relx = dd.obj.x + (dd.obj.w/2);
  var rely = dd.obj.y + (dd.obj.h/2);

  var boxes, found = 0;
  for (var i=0; i<wb_dd_targets.length; i++) {  // Look for our box set
    if (wb_dd_targets[i][_ID] == dd.obj.grp) {
      boxes = wb_dd_targets[i][_BOXES];  break;
    }
  }

  // Locate if over a hot zone
  for (var i = 0; i < boxes.length; i++) {
    var box = boxes[i];
    if (relx >= box[1] && relx <= box[3] && rely >= box[2] && rely <= box[4]) {
      // Found the hotspot that we are over
      if (wb_current_box == box) break; // nothing to do

      if (wb_current_box != '') wb_current_box.bkgdim();

      wb_current_box = dd.elements[box[0]];    wb_current_box.bkgbright();
      found = 1;

      break;
    }
  }

  if (found == 0) {
    if (wb_current_box != '') {
      wb_current_box.bkgdim();    wb_current_box = '';
    }
  }
}




/*
History:

30-Jun-2007: MSS wanted all marks suppressed.

To do:

FLASH: Might need to add in Flash support
FIB: They can have multiple right answers.  Currently only support one

*/
