<!-- This page defines the form to create or edit an instance of this module -->
<!-- It is used from /course/mod.php.  The whole instance is available as $form. -->

<!--
TODO: display proper message when no sequences are received
-->

<?php

include_once($CFG->dirroot.'/mod/lams/constants.php');

if (!isset($form->name)) {
    $form->name = '';
}
if (!isset($form->introduction)) {
    $form->introduction = '';
}
if (!isset($form->learning_session_id)) {
    $form->learning_session_id = '';
}

if (!isset($form->create_sequence_url)) {
    $datetime =    date("F d,Y g:i a");
    $plaintext = trim($datetime).trim($USER->username).trim($LAMSCONSTANTS->author_method).trim($CFG->lams_serverid).trim($CFG->lams_serverkey);
    $hash = sha1(strtolower($plaintext));
    $form->create_sequence_url = $CFG->lams_serverurl.$LAMSCONSTANTS->login_request.
        '?'.$LAMSCONSTANTS->param_uid.'='.$USER->username.
        '&'.$LAMSCONSTANTS->param_method.'='.$LAMSCONSTANTS->author_method.
        '&'.$LAMSCONSTANTS->param_timestamp.'='.urlencode($datetime).
        '&'.$LAMSCONSTANTS->param_serverid.'='.$CFG->lams_serverid.
        '&'.$LAMSCONSTANTS->param_hash.'='.$hash.
        '&'.$LAMSCONSTANTS->param_courseid.'='.$form->course;
}
?>

<form id="form" method="post" action="mod.php" onSubmit="disableSumbit(this);">
<center>
<span id="message"><p>Please wait .......</p></span>
<table cellpadding="5">
<tr valign=top>
<td align=right><b><?php print_string("workspace", "lams") ?>:</b></td>
<td>
<select id="workspace" name="workspace"></select>
</td>
</tr>
<tr>
<td align="right"><b><?php  print_string("sequence","lams") ?>:</b></td>
<td>
<select id="sequence" name="sequence"></select>
</td>
</tr>
<tr>
<td align="right"><b><?php  print_string("name") ?>:</b></td>
<td>
<input type="text" id="name" name="name" size="30" />
</td>
</tr>
<td align=right><p><b><?php print_string("introduction", "lams") ?>:</b></p>
<br />
<font size="1">
<?php
if ($usehtmleditor) {
    helpbutton("richtext", get_string("helprichtext"), "moodle", true, true);
} else {
    helpbutton("text", get_string("helptext"), "moodle", true, true);
    echo '<br />';
    emoticonhelpbutton("form", "introduction");
    echo '<br />';
}
?>
<br />
</font>
</td>
<td>
<?php
print_textarea($usehtmleditor, 20, 50, 680, 400, "introduction", $form->introduction);
?>
</td>
</tr>

<!-- The following line for Moodle 1.5 prints the visibility setting form element -->
<!-- <?php print_visible_setting($form); ?> -->
<!-- and if your module uses groups you would also have -->
<!-- ?php print_groupmode_setting($form); ? -->

</table>
<input type="hidden" name="learning_session_id"/>
<input type="hidden" name="create_sequence_url" id="create_sequence_url"/>
<input type="submit" id="save" name="save" value="<?php print_string("useSequence","lams") ?>" onClick="return validate();">
<input type="button" id="edit" name"edit" value="<?php print_string("editSequence","lams") ?>" onClick="openAuthor(<?php echo "'".$form->create_sequence_url."&".$LAMSCONSTANTS->param_ldid."=".$form->sequence."'";?>);">
<input type="button" id="create" name="create" value="<?php print_string("createSequence","lams") ?>" onClick="openAuthor(<?php echo "'".$form->create_sequence_url."'";?>);">
<input type="button" id="refresh" name="refresh" value="<?php print_string("refreshSequenceList","lams")?>" onclick="refreshLists();">
<input type="submit" id="cancel" name=cancel value="<?php  print_string("cancel") ?>">


<!-- These hidden variables are always the same -->
<input type="hidden" name=course        value="<?php  p($form->course) ?>" />
<input type="hidden" name="sesskey"     value="<?php  p($form->sesskey) ?>" />
<input type="hidden" name=coursemodule  value="<?php  p($form->coursemodule) ?>" />
<input type="hidden" name=section       value="<?php  p($form->section) ?>" />
<input type="hidden" name=module        value="<?php  p($form->module) ?>" />
<input type="hidden" name=modulename    value="<?php  p($form->modulename) ?>" />
<input type="hidden" name=instance      value="<?php  p($form->instance) ?>" />
<input type="hidden" name=mode          value="<?php  p($form->mode) ?>" />
</center>

</form>

<script type="text/javascript">
//<![CDATA[

function validate(){
    workspaceObj = document.getElementById("workspace");
    if (workspaceObj.options.length == 0){
        alert("No workspace was returned from LAMS server! You may try refreshing sequence list.\nIf you still get the same problem, probably you have to cancel this activity.");
        return false;
    }
    sequenceObj = document.getElementById("sequence");
    if(sequenceObj.options.length == 0){
        alert("No sequence was returned from LAMS server! You may try refreshing sequence list.\nIf you still get the same problem, probably you have to cancel this activity.");
        return false;
    }
    if(trim(document.getElementById("name").value).length==0){
        alert("You have to specify the activity name to proceed.");
        return false;
    }
    return true;
}

function trim(trim_value){
    if(trim_value.length < 1){
        return"";
    }
    trim_value = rTrim(trim_value);
    trim_value = lTrim(trim_value);
    return trim_value;
} //End Function

function rTrim(trim_value){
    var w_space = String.fromCharCode(32);
    var v_length = trim_value.length;
    var strTemp = "";
    if(v_length < 0){
        return"";
    }
    var iTemp = v_length -1;

    while(iTemp > -1){
        if(trim_value.charAt(iTemp) != w_space){
            strTemp = trim_value.substring(0,iTemp +1);
            break;
        }
        iTemp = iTemp-1;
    } //End While
    return strTemp;
} //End Function

function lTrim(trim_value){
    var w_space = String.fromCharCode(32);
    if(v_length < 1){
        return"";
    }
    var v_length = trim_value.length;
    var strTemp = "";

    var iTemp = 0;

    while(iTemp < v_length){
        if(trim_value.charAt(iTemp) != w_space){
            strTemp = trim_value.substring(iTemp,v_length);
            break;
        }
        iTemp = iTemp + 1;
    } //End While
    return strTemp;
} //End Function

function disableSubmit(dform) {
    if (document.getElementById) {
        for (var sch = 0; sch < dform.length; sch++) {
            if (dform.elements[sch].type.toLowerCase() == "submit") dform.elements[sch].disabled = true;
        }
    }
    return true;
}

function enableSubmit(dform) {
    if (document.getElementById) {
        for (var sch = 0; sch < dform.length; sch++) {
            if (dform.elements[sch].type.toLowerCase() == "submit") dform.elements[sch].disabled = false;
        }
    }
    return true;
}

var authorWin = null;
function openAuthor(url){
    //alert(url);
    if(authorWin && authorWin.open && !authorWin.closed){
        authorWin.document.location = url;
        authorWin.focus();
    }else{
        authorWin = window.open(url);
        authorWin.focus();
    }
}


var req;
/**
 * This function is to initialize elements' status and refresh workspace and sequence lists
 */
function refreshLists(){
    document.getElementById("message").innerHTML = "<p>Please wait .......</p>";
    disableSubmit(document.forms[0]);
    document.getElementById("create").disabled = true;
    document.getElementById("edit").disabled = true;
    document.getElementById("refresh").disabled = true;

    wsSelectObj = document.getElementById("workspace");
    wsSelectObj.options.length = 0; //remove all options
    wsSelectObj.options[0] = new Option("Loading...", "Loading...");
    wsSelectObj.disabled = true;

    seqSelectObj = document.getElementById("sequence");
    seqSelectObj.options.length = 0; //remove all options

    //clear the DynamicOptionList
    dol = new DynamicOptionList("workspace","sequence");
    dol.setFormName("form");

    url = "../mod/lams/list.php?courseid="+document.getElementById('form').course.value;
    if (window.XMLHttpRequest) { // Non-IE browsers
        req = new XMLHttpRequest();
        req.onreadystatechange = processStateChange;
        try {
            req.open("GET", url, true);
        } catch (e) {
            alert(e);
        }
        req.send(null);
    } else if (window.ActiveXObject) { // IE
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if (req) {
            req.onreadystatechange = processStateChange;
            req.open("GET", url, true);
            req.send();
        }
    }
}

function processStateChange() {
    if (req.readyState == 4) { // Complete
        var wsSelectObj = document.getElementById("workspace");
        wsSelectObj.options.length = 0; //clear the workspace list
        wsSelectObj.disabled = false;
        enableSubmit(document.forms[0]);
        document.getElementById("create").disabled = false;
        document.getElementById("edit").disabled = false;
        document.getElementById("refresh").disabled = false;
        if (req.status == 200) { // OK response
            var res = req.responseText.replace(/^\s*|\s*$/g,""); //get trimed result
            var seqlistStr = res.split(':'); //seperate each set of {sid, workspace,sequence}
            var seqlist = new Array();

            var workspaceList = new Object();

            //pass the response and construct the list again
            for(var i=0; i<seqlistStr.length; i++){
                var sws =  seqlistStr[i].split(',');
                if(sws.length == 3){
                    var sid = sws[0];
                    var workspace = sws[1];
                    var sequence = sws[2];

                    if(workspaceList[workspace] == null){
                        //set workspace as keys in workspaceList (kind of like a map)
                        //therefore duplicated workspaces will be ignored
                        workspaceList[workspace] = 1; //insert workspace as a key

                        //insert workspace into workspace list
                        wsSelectObj.options[wsSelectObj.length] = new Option(workspace,workspace);
                    }

                    //insert sequence dependency into DynamicOptionList
                    dol.forValue(workspace).addOptionsTextValue(sequence,sid);
                }
            }
            initDynamicOptionLists(); //construct the list
            document.getElementById("message").innerHTML =
                "<p>Select an existing sequence or create a new sequence.</p>";
        }else if(req.status == 504){//gateway timeout. probabaly LAMS server is not available.
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">It seems the LAMS server is not available!<br/> Please contact your administrator.</h4></td></tr></table>';
        }else if(req.status == 401){//AuthenticationException thrown by LAMS server.
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">This moodle server is not authenticated by LAMS!<br/> Please contact your administrator.</h4></td></tr></table>';
        }else if(req.status == 502){//Unknow exception thrown by LAMS server
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">An unexpected error returned from LAMS server:'+req.responseText+'! Please contact your administrator.</h4></td></tr></table>';
        }else if(req.status == 417){//ServerNotFound exception thrown by LAMS server
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">This moodle server has not registered in LAMS!<br/> Please contact your administrator.</h4></td></tr></table>';
        }else if(req.status == 402){//All LAMS module settings have not not been set up
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">All the LAMS module settings have not been set up!<br/> Please contact your administrator.</h4></td></tr></table>';
        }else{//this error should come from moodle server itself
            document.getElementById("message").innerHTML =
                '<table align="center" width="57%"  class="noticebox" border="0" cellpadding="15" cellspacing="0"><tr><td bgcolor="#FFAAAA" class="noticeboxcontent"><h4   class="main">It should be a moodle server error:'+req.status + ' ' + req.statusText+'! Please contact your administrator.</h4></td></tr></table>';
        }
    }
}

var dynamicOptionListCount=0;
var dynamicOptionListObjects = new Array();

// Init call to setup lists after page load. One call to this function sets up all lists.
function initDynamicOptionLists() {
    // init each DynamicOptionList object
    for (var i=0; i<dynamicOptionListObjects.length; i++) {
        var dol = dynamicOptionListObjects[i];

        // Find the form associated with this list
        if (dol.formName!=null) {
            dol.form = document.forms[dol.formName];
        }
        else if (dol.formIndex!=null) {
            dol.form = document.forms[dol.formIndex];
        }
        else {
            // Form wasn't set manually, so go find it!
            // Search for the first form element name in the lists
            var name = dol.fieldNames[0][0];
            for (var f=0; f<document.forms.length; f++) {
                if (typeof(document.forms[f][name])!="undefined") {
                    dol.form = document.forms[f];
                    break;
                }
            }
            if (dol.form==null) {
                alert("ERROR: Couldn't find form element "+name+" in any form on the page! Init aborted"); return;
            }
        }

        // Form is found, now set the onchange attributes of each dependent select box
        for (var j=0; j<dol.fieldNames.length; j++) {
            // For each set of field names...
            for (var k=0; k<dol.fieldNames[j].length-1; k++) {
                // For each field in the set...
                var selObj = dol.form[dol.fieldNames[j][k]];
                if (typeof(selObj)=="undefined") { alert("Select box named "+dol.fieldNames[j][k]+" could not be found in the form. Init aborted"); return; }
                // Map the HTML options in the first select into the options we created
                if (k==0) {
                    if (selObj.options!=null) {
                        for (l=0; l<selObj.options.length; l++) {
                            var sopt = selObj.options[l];
                            var m = dol.findMatchingOptionInArray(dol.options,sopt.text,sopt.value,false);
                            if (m!=null) {
                                var reselectForNN6 = sopt.selected;
                                var m2 = new Option(sopt.text, sopt.value, sopt.defaultSelected, sopt.selected);
                                m2.selected = sopt.selected; // For some reason I need to do this to make NN4 happy
                                m2.defaultSelected = sopt.defaultSelected;
                                m2.DOLOption = m;
                                selObj.options[l] = m2;
                                selObj.options[l].selected = reselectForNN6; // Reselect this option for NN6 to be happy. Yuck.
                            }
                        }
                    }
                }
                if (selObj.onchange==null) {
                    // We only modify the onChange attribute if it's empty! Otherwise do it yourself in your source!
                    selObj.onchange = new Function("dynamicOptionListObjects["+dol.index+"].change(this)");
                }
            }
        }
    }
    // Set the preselectd options on page load
    resetDynamicOptionLists();
}

// This function populates lists with the preselected values.
// It's pulled out into a separate function so it can be hooked into a 'reset' button on a form
// Optionally passed a form object which should be the only form reset
function resetDynamicOptionLists(theform) {
    // reset each DynamicOptionList object
    for (var i=0; i<dynamicOptionListObjects.length; i++) {
        var dol = dynamicOptionListObjects[i];
        if (typeof(theform)=="undefined" || theform==null || theform==dol.form) {
            for (var j=0; j<dol.fieldNames.length; j++) {
                dol.change(dol.form[dol.fieldNames[j][0]],true); // Second argument says to use preselected values rather than default values
            }
        }
    }
}

// An object to represent an Option() but just for data-holding
function DOLOption(text,value,defaultSelected,selected) {
    this.text = text;
    this.value = value;
    this.defaultSelected = defaultSelected;
    this.selected = selected;
    this.options = new Array(); // To hold sub-options
    return this;
}

// DynamicOptionList CONSTRUCTOR
function DynamicOptionList() {
    this.form = null;// The form this list belongs to
    this.options = new Array();// Holds the options of dependent lists
    this.longestString = new Array();// Longest string that is currently a potential option (for Netscape)
    this.numberOfOptions = new Array();// The total number of options that might be displayed, to build dummy options (for Netscape)
    this.currentNode = null;// The current node that has been selected with forValue() or forText()
    this.currentField = null;// The current field that is selected to be used for setValue()
    this.currentNodeDepth = 0;// How far down the tree the currentNode is
    this.fieldNames = new Array();// Lists of dependent fields which use this object
    this.formIndex = null;// The index of the form to associate with this list
    this.formName = null;// The name of the form to associate with this list
    this.fieldListIndexes = new Object();// Hold the field lists index where fields exist
    this.fieldIndexes = new Object();// Hold the index within the list where fields exist
    this.selectFirstOption = true;// Whether or not to select the first option by default if no options are default or preselected, otherwise set the selectedIndex = -1
    this.numberOfOptions = new Array();// Store the max number of options for a given option list
    this.longestString = new Array();// Store the longest possible string
    this.values = new Object(); // Will hold the preselected values for fields, by field name

    // Method mappings
    this.forValue = DOL_forValue;
    this.forText = DOL_forText;
    this.forField = DOL_forField;
    this.forX = DOL_forX;
    this.addOptions = DOL_addOptions;
    this.addOptionsTextValue = DOL_addOptionsTextValue;
    this.setDefaultOptions = DOL_setDefaultOptions;
    this.setValues = DOL_setValues;
    this.setValue = DOL_setValues;
    this.setFormIndex = DOL_setFormIndex;
    this.setFormName = DOL_setFormName;
    this.printOptions = DOL_printOptions;
    this.addDependentFields = DOL_addDependentFields;
    this.change = DOL_change;
    this.child = DOL_child;
    this.selectChildOptions = DOL_selectChildOptions;
    this.populateChild = DOL_populateChild;
    this.change = DOL_change;
    this.addNewOptionToList = DOL_addNewOptionToList;
    this.findMatchingOptionInArray = DOL_findMatchingOptionInArray;

    // Optionally pass in the dependent field names
    if (arguments.length > 0) {
        // Process arguments and add dependency groups
        for (var i=0; i<arguments.length; i++) {
            this.fieldListIndexes[arguments[i].toString()] = this.fieldNames.length;
            this.fieldIndexes[arguments[i].toString()] = i;
        }
        this.fieldNames[this.fieldNames.length] = arguments;
    }

    // Add this object to the global array of dynamicoptionlist objects
    this.index = window.dynamicOptionListCount++;
    window["dynamicOptionListObjects"][this.index] = this;
}

// Given an array of Option objects, search for an existing option that matches value, text, or both
function DOL_findMatchingOptionInArray(a,text,value,exactMatchRequired) {
    if (a==null || typeof(a)=="undefined") { return null; }
    var value_match = null; // Whether or not a value has been matched
    var text_match = null; // Whether or not a text has been matched
    for (var i=0; i<a.length; i++) {
        var opt = a[i];
        // If both value and text match, return it right away
        if (opt.value==value && opt.text==text) { return opt; }
        if (!exactMatchRequired) {
            // If value matches, store it until we complete scanning the list
            if (value_match==null && value!=null && opt.value==value) {
                value_match = opt;
            }
            // If text matches, store it for later
            if (text_match==null && text!=null && opt.text==text) {
                text_match = opt;
            }
        }
    }
    return (value_match!=null)?value_match:text_match;
}

// Util function used by forValue and forText
function DOL_forX(s,type) {
    if (this.currentNode==null) { this.currentNodeDepth=0; }
    var useNode = (this.currentNode==null)?this:this.currentNode;
    var o = this.findMatchingOptionInArray(useNode["options"],(type=="text")?s:null,(type=="value")?s:null,false);
    if (o==null) {
        o = new DOLOption(null,null,false,false);
        o[type] = s;
        useNode.options[useNode.options.length] = o;
    }
    this.currentNode = o;
    this.currentNodeDepth++;
    return this;
}

// Set the portion of the list structure that is to be used by a later operation like addOptions
function DOL_forValue(s) { return this.forX(s,"value"); }

// Set the portion of the list structure that is to be used by a later operation like addOptions
function DOL_forText(s) { return this.forX(s,"text"); }

// Set the field to be used for setValue() calls
function DOL_forField(f) { this.currentField = f; return this; }

// Create and add an option to a list, avoiding duplicates
function DOL_addNewOptionToList(a, text, value, defaultSelected) {
    var o = new DOLOption(text,value,defaultSelected,false);
    // Add the option to the array
    if (a==null) { a = new Array(); }
    for (var i=0; i<a.length; i++) {
        if (a[i].text==o.text && a[i].value==o.value) {
            if (o.selected) {
                a[i].selected=true;
            }
            if (o.defaultSelected) {
                a[i].defaultSelected = true;
            }
            return a;
        }
    }
    a[a.length] = o;
}

// Add sub-options to the currently-selected node, with the same text and value for each option
function DOL_addOptions() {
    if (this.currentNode==null) { this.currentNode = this; }
    if (this.currentNode["options"] == null) { this.currentNode["options"] = new Array(); }
    for (var i=0; i<arguments.length; i++) {
        var text = arguments[i];
        this.addNewOptionToList(this.currentNode.options,text,text,false);
        if (typeof(this.numberOfOptions[this.currentNodeDepth])=="undefined") {
            this.numberOfOptions[this.currentNodeDepth]=0;
        }
        if (this.currentNode.options.length > this.numberOfOptions[this.currentNodeDepth]) {
            this.numberOfOptions[this.currentNodeDepth] = this.currentNode.options.length;
        }
        if (typeof(this.longestString[this.currentNodeDepth])=="undefined" || (text.length > this.longestString[this.currentNodeDepth].length)) {
            this.longestString[this.currentNodeDepth] = text;
        }
    }
    this.currentNode = null;
    this.currentNodeDepth = 0;
}

// Add sub-options to the currently-selected node, specifying separate text and values for each option
function DOL_addOptionsTextValue() {
    if (this.currentNode==null) { this.currentNode = this; }
    if (this.currentNode["options"] == null) { this.currentNode["options"] = new Array(); }
    for (var i=0; i<arguments.length; i++) {
        var text = arguments[i++];
        var value = arguments[i];
        this.addNewOptionToList(this.currentNode.options,text,value,false);
        if (typeof(this.numberOfOptions[this.currentNodeDepth])=="undefined") {
            this.numberOfOptions[this.currentNodeDepth]=0;
        }
        if (this.currentNode.options.length > this.numberOfOptions[this.currentNodeDepth]) {
            this.numberOfOptions[this.currentNodeDepth] = this.currentNode.options.length;
        }
        if (typeof(this.longestString[this.currentNodeDepth])=="undefined" || (text.length > this.longestString[this.currentNodeDepth].length)) {
            this.longestString[this.currentNodeDepth] = text;
        }
    }
    this.currentNode = null;
    this.currentNodeDepth = 0;
}

// Find the first dependent list of a select box
// If it's the last list in a chain, return null because there are no children
function DOL_child(obj) {
    var listIndex = this.fieldListIndexes[obj.name];
    var index = this.fieldIndexes[obj.name];
    if (index < (this.fieldNames[listIndex].length-1)) {
        return this.form[this.fieldNames[listIndex][index+1]];
    }
    return null;
}

// Set the options which should be selected by default for a certain value in the parent
function DOL_setDefaultOptions() {
    if (this.currentNode==null) { this.currentNode = this; }
    for (var i=0; i<arguments.length; i++) {
        var o = this.findMatchingOptionInArray(this.currentNode.options,null,arguments[i],false);
        if (o!=null) {
            o.defaultSelected = true;
        }
    }
    this.currentNode = null;
}

// Set the options which should be selected when the page loads. This is different than the default value and ONLY applies when the page LOADS
function DOL_setValues() {
    if (this.currentField==null) {
        alert("Can't call setValues() without using forField() first!");
        return;
    }
    if (typeof(this.values[this.currentField])=="undefined") {
        this.values[this.currentField] = new Object();
    }
    for (var i=0; i<arguments.length; i++) {
        this.values[this.currentField][arguments[i]] = true;
    }
    this.currentField = null;
}

// Manually set the form for the object using an index
function DOL_setFormIndex(i) {
    this.formIndex = i;
}

// Manually set the form for the object using a form name
function DOL_setFormName(n) {
    this.formName = n;
}

// Print blank <option> objects for Netscape4, since it refuses to grow or shrink select boxes for new options
function DOL_printOptions(name) {
    // Only need to write out "dummy" options for Netscape4
    if ((navigator.appName == 'Netscape') && (parseInt(navigator.appVersion) <= 4)){
        var index = this.fieldIndexes[name];
        var ret = "";
        if (typeof(this.numberOfOptions[index])!="undefined") {
            for (var i=0; i<this.numberOfOptions[index]; i++) {
                ret += "<OPTION>";
            }
        }
        ret += "<OPTION>";
        if (typeof(this.longestString[index])!="undefined") {
            for (var i=0; i<this.longestString[index].length; i++) {
                ret += "_";
            }
        }
        document.writeln(ret);
    }
}

// Add a list of field names which use this option-mapping object.
// A single mapping object may be used by multiple sets of fields
function DOL_addDependentFields() {
    for (var i=0; i<arguments.length; i++) {
        this.fieldListIndexes[arguments[i].toString()] = this.fieldNames.length;
        this.fieldIndexes[arguments[i].toString()] = i;
    }
    this.fieldNames[this.fieldNames.length] = arguments;
}

// Called when a parent select box is changed. It populates its direct child, then calls change on the child object to continue the population.
function DOL_change(obj, usePreselected) {
    if (usePreselected==null || typeof(usePreselected)=="undefined") { usePreselected = false; }
    var changedListIndex = this.fieldListIndexes[obj.name];
    var changedIndex = this.fieldIndexes[obj.name];
    var child = this.child(obj);
    if (child == null) { return; } // No child, no need to continue
    if (obj.type == "select-one") {
        // Treat single-select differently so we don't have to scan the entire select list, which could potentially speed things up
        if (child.options!=null) {
            child.options.length=0; // Erase all the options from the child so we can re-populate
        }
        if (obj.options!=null && obj.options.length>0 && obj.selectedIndex>=0) {
            var o = obj.options[obj.selectedIndex];
            this.populateChild(o.DOLOption,child,usePreselected);
            this.selectChildOptions(child,usePreselected);
        }
    }
    else if (obj.type == "select-multiple") {
        // For each selected value in the parent, find the options to fill in for this list
        // Loop through the child list and keep track of options that are currently selected
        var currentlySelectedOptions = new Array();
        if (!usePreselected) {
            for (var i=0; i<child.options.length; i++) {
                var co = child.options[i];
                if (co.selected) {
                    this.addNewOptionToList(currentlySelectedOptions, co.text, co.value, co.defaultSelected);
                }
            }
        }
        child.options.length=0;
        if (obj.options!=null) {
            var obj_o = obj.options;
            // For each selected option in the parent...
            for (var i=0; i<obj_o.length; i++) {
                if (obj_o[i].selected) {
                    // if option is selected, add its children to the list
                    this.populateChild(obj_o[i].DOLOption,child,usePreselected);
                }
            }
            // Now go through and re-select any options which were selected before
            var atLeastOneSelected = false;
            if (!usePreselected) {
                for (var i=0; i<child.options.length; i++) {
                    var m = this.findMatchingOptionInArray(currentlySelectedOptions,child.options[i].text,child.options[i].value,true);
                    if (m!=null) {
                        child.options[i].selected = true;
                        atLeastOneSelected = true;
                    }
                }
            }
            if (!atLeastOneSelected) {
                this.selectChildOptions(child,usePreselected);
            }
        }
    }
    // Change all the way down the chain
    this.change(child,usePreselected);
}
function DOL_populateChild(dolOption,childSelectObj,usePreselected) {
    // If this opton has sub-options, populate the child list with them
    if (dolOption!=null && dolOption.options!=null) {
        for (var j=0; j<dolOption.options.length; j++) {
            var srcOpt = dolOption.options[j];
            if (childSelectObj.options==null) { childSelectObj.options = new Array(); }
            // Put option into select list
            var duplicate = false;
            var preSelectedExists = false;
            for (var k=0; k<childSelectObj.options.length; k++) {
                var csi = childSelectObj.options[k];
                if (csi.text==srcOpt.text && csi.value==srcOpt.value) {
                    duplicate = true;
                    break;
                }
            }
            if (!duplicate) {
                var newopt = new Option(srcOpt.text, srcOpt.value, false, false);
                newopt.selected = false; // Again, we have to do these two statements for NN4 to work
                newopt.defaultSelected = false;
                newopt.DOLOption = srcOpt;
                childSelectObj.options[childSelectObj.options.length] = newopt;
            }
        }
    }
}

// Once a child select is populated, go back over it to select options which should be selected
function DOL_selectChildOptions(obj,usePreselected) {
    // Look to see if any options are preselected=true. If so, then set then selected if usePreselected=true, otherwise set defaults
    var values = this.values[obj.name];
    var preselectedExists = false;
    if (usePreselected && values!=null && typeof(values)!="undefined") {
        for (var i=0; i<obj.options.length; i++) {
            var v = obj.options[i].value;
            if (v!=null && values[v]!=null && typeof(values[v])!="undefined") {
                preselectedExists = true;
                break;
            }
        }
    }
    // Go back over all the options to do the selection
    var atLeastOneSelected = false;
    for (var i=0; i<obj.options.length; i++) {
        var o = obj.options[i];
        if (preselectedExists && o.value!=null && values[o.value]!=null && typeof(values[o.value])!="undefined") {
            o.selected = true;
            atLeastOneSelected = true;
        }
        else if (!preselectedExists && o.DOLOption!=null && o.DOLOption.defaultSelected) {
            o.selected = true;
            atLeastOneSelected = true;
        }
        else {
            o.selected = false;
        }
    }
    // If nothing else was selected, select the first one by default
    if (this.selectFirstOption && !atLeastOneSelected && obj.options.length>0) {
        obj.options[0].selected = true;
    }
    else if (!atLeastOneSelected &&  obj.type=="select-one") {
        obj.selectedIndex = -1;
    }
}

refreshLists();//refresh the lists when form is displayed.
//]]>
</script>
