/* functions used in control panels (for dSurvey sessions
   It uses functions in svyInputs2.js and svylibE2.js    (and indirectly, in xmlRequest2b.js)
   
  
Functions: 
                                                                         
Note: many of these functions use other arguments internally (ie; on recursive calls).  Some of them have arguments that can be (but are rarely) 
used to tweak display.

Main functions:  
 doStuff(useName)                               the <DO> processor. useName is the name of the <DO> to process
 doForm(useName)                                Display and process the with name useName
      storeFormVal(e)                             subfunction of doForm -- called on a user action (a click, etc)
      submitForm(e)                                subfunction of doForm -- called on a submit action
 assignRefID(reset)                                  Assign a "reference id" to all current participants. Used by the RESOLVE action of <DO>>

 makeClientParametersXML(anode)                 Returns  a string that contains an client-accessible Genera/Users/Reference parameter file (a string version of an  xml file)
     makeGUparametersXMLGeneral(gnode,doall)    make xml string, using parameter file syntax, for GENERAL variables 
     makeGUparametersXMLMessage(gnode,doall)    make xml string, using parameter file syntax, for MESSAGE variables 
     makeGUparametersXMLUsers(gnode,doall)      make xml string, using "escaped" parameter file syntax, for USER variables (as read from userData)
     makeGUparametersXMLcurrentParticipants(gnode,doall)      make xml string, using parameter file syntax, for CURRENT USER variables (as read from currentUses)
     makeGUparametersXMLReference(gnode,doall)  make xml string, using "escaped" parameter file syntax, for REFERNCE variables (as read from refData)

 makePostDataGeneral(useround,vars)                  Return a string that contains XML suitable for posting to server-side archive: of the general vars
 makePostDataUsers(useround,vars)                    Return a string that contains XML suitable for posting to server-side archive: of the USER vars for all clients
 makePostDataResults(useround,vars)                  Return a string that contains XML suitable for posting to server-side archive: of the RESULTS vars for all clients
 makePostDataParticipants(useround,vars)             Return a string that contains XML suitable for posting to server-side archive: of the currentParticipants
 makePostDataHistory(useround,vars)                  Return a string that contains XML suitable for posting to server-side archive: from all control panel databases

 parseVarNodeGeneral(gnode,unesc)                     Parse a <GENERAL> node from a parameters file, save vars to generalVars
 parseVarNodeUR(gnode,reftype,doUnescape)       Parse a reftype (<USERS> or <REFERENCE>  or <currentParticipants>) node from a parameters file, save vars to  userData, refeData, or currentParrticipants

 parseUserListNode(xmlNode,reset)               Parse an XML file (stored in xmlNode) containing a list of participants. Store in currentParticipants. Optionally resets current values.
 toArray_userListNode(xmlNode)                  Parse an XML file (stored in xmlNode) containing a list of particiapnts. Returns an array of arrays (1 row per participant)

 parseSubmittedData(xmlnode,reset)              Parse an xmlnode  (a node containing an XML file) into the submittedData object. Reset=1 to delete current submittedData

 doSaveSnapshot()                              Save the current general and control parameters (for possible later reading) into  a  snapshot file

 readLocalXmlFile(adiv) {                       Read an XML file on the local harddrive. Adiv is the div id of where a <INPUT type="file"> form will be written


View sets of variables:
 viewControlVars(towrite,doappend)              Display the current controlVars, using towrite div. Doappend=1 means do NOT clear prior contents of towrite
 viewGeneralVars(towrite,doappend,descObj)              Display the current generalVars
 viewServerVars(towrite,doappend)               Display the current serverVars  (the ones that have been read by administrative actions)
 viewReferenceVars(reftype)                     Display the refData (reftype=REFERENCE) or userData (reftype='USER') control panel database
 viewWorkData(awhere,savebutton)             Display the "workData"
 viewResultsData(awhere,savebutton)             Display "resultsData"

 reviewUsersCurrent()                           Display  current list of users (does NOT read from sever), and reference data. Does not save results to variables.
   reviewUsersServer()                            Obtain current list of users, and display.
   reviewUsersBoth()                            Obtain current list of participatns, and compare/display to current control panel list (calls toArray_userListNode)
 getAllServerVars(ido,showbutton)               Retrieve all the "server" variables from the server, and display them in _controlStatus div
 viewSubmittedData(retainedVars,showbutton)     View the submittedData -- sorted by round. retainedVars, if specified, is the list of variables that may be displayed.
     viewSubmittedDataId(retainedVars)          View the submittedData -- sorted by ID.
     viewSubmittedDataVar(avar)                 View the submittedData -- just the avar variable
     saveSubmittedDataAsXML(filename)           View the submitted data as an XML file in new window, or use server hack to save it to local hard drive as filename

 viewHistory(varlist,ido)                       View the current history archive 

 saveParticipantsAsXML(xmlNode,filename)        View the list of participants as an XML file in new window, or use server hack to save it to local hard drive as filename

 saveDataAsXML(type,filename,rounds,message)            Save the retrieved general, users, participants, or results "archive" data to local hard drive

Save and read variables
 getVarInfo(avar,rtype,deftype,elemok  )        Return info on a variable named in the avar string.
 setControlVar(avar,aval)               Set value of a control variable. 
 readGeneralVar(aname,adefault)         Return value of aname general variable -- possibly an array element (x[1]). Use adefault if no such general variable
 saveGeneralVar(aname,aval,isarray)     Save aval value of aname general variable. If isarray=1, then aval is an array
 readUserVar(clientd,aname,adefault     Read user var of client clientid, return adefault if var or clientid not exists
 saveUserVar(clientid,aname,aval)       Save aval to the aname user variable, for client clientid
 readResultsVar(clientd,aname,adefault     Read results var of client clientid, return adefault if var or clientid not exists
 saveResultsVar(clientid,aname,aval)    : Save aval to aname results variable, for client clientid
 saveDOResultsVar(z,idVar,varlist,noast)      : Save values in the entries of the z dataobject
saveRefVar(aname,refid,aval)           Save aval to the aname reference variable, for reference-row refid

 saveStringToFile(fileName,stuff)      save string (in stuff) to local file (using server hack), fileName is suggested name to use
 displayStringInWindow(stuff,cvt,astext)      display a string in a new window. if cvt=1, convert < and > to &lt and &gt

 arrayToCheckObject(vlist)              from vlist array (of words) create an object suitable for "checking for existence"

 expandReads(bnode)                     Look for <read> elements in a subtree, and replace their innerhtmls with appropriate values
 expandReadsString(bnode)               Return a string with <read> replaced by appropriate value

Replacement functions for math computation:
 doMathVarExists_control(avar,Dnode)     alternate variable lookup functions for svyMath
 doMathVar_control(avar,anode)          alternate variable lookup functions for svyMath

Display info:
 writeDoStatus(message,messageHeader,aoptions)   Write a message to the _controlStatus and _controlStatusHistory divs
 writeLastAction(this,astyle)                   Write, to the lastAction div, the contents of the summary attribute of this element (used in onClick)
 writeDataMessage(amess,lifespan)                 Write a "data message" to a fixed div, click on the div and it goes away. If lifespan>0, # of seconds for message to linger

  initScrollValues(elementList)           : initialize "scrollValue" input field/scroll-bars
  doScrollValueInit(ispot,whichScroll,athis)   :  called initScrollValues (and onMouseOver) : create a scroll bar, clicking on it changes value field in an element.  
  doScrollValue(ispot,whichScroll,athis)   :  called initScrollValues (and onMouseOver) : create a scroll bar, clicking on it changes value field in an element.  
  scrollValueFill(ispot,whichScroll)       : onClick function that changes a value (used by doScrollValue)

Some details:
  getVarInfo(avar,rtype,deftype,elemok  )          return info on a variable named in the avar string.
                                           an object is returned (x) with attributes: x.name, x.value, x.type, x.defined. x.array 
                                           rtype, if not '', means return just the rtype attribute as a string
                                           deftype sets what the default type is (no prepended character, no .xxx). Possible values: CONTROL, SERVER, GENERAL
                                           elemok is optional. If 1, then x[i] syntax is supported
                                           note: @xxx means "xxx server variable"
                                                %xxx means "xxx control varaible"
                                                #xxx means "xxx general variable"

 writeDoStatus(message,messageHeader,aoptions)   Write a message to the _controlStatus and _controlStatusHistory divs
                                         message: message to write to current results, and to save to status history
                                         messageHeader: a header to add to message when writing to current results (but NOT used when saveing to status history)
                                         aoptions: space delimited list of options (case insensitive):
                                                    NOWRITE : do NOT write message to whereWrite 
                                                     RESET : reset: clear whereWrite, and add a <HR> to whereWriteHistory
                                                     NOSAVE : do NOT save to status history 

 makeClientParametersXML(anode,ispost)     Returns  a string that contains an client-accessible Genera/Users/Message/Reference/Participants parameter file (an xml file)
                                    For General, Users, and Reference; values are escaped and then stored. For particpants, values are stored as is
                                    anode contains instructions on what variables to use (the contents of a POSTFILE in a <DO>)
                                       makeGUParamtersXML calls other makeGUParamters...XML procedures
                                       makeGUParamtersXML is currently only used in POSTFILE action of <DO>.
                                       the other makeGUParameters...XML procedures are used in other locations
                                     ispost =1 if called from postcap (effects error messages)

 toArray_userListNode(xmlNode)            parse an XML file (stored in xmlNode) containing a pariticipant list (as returned by server).
                                          Return an array, 1 row per "user". Each row is an array, containing userid, name, contact, and possibly other info.
                                          Row 0 of the array contains a list of variables (column headers0
                                          Column 0 of each "subarray" is the clientid

 viewSubmittedData(retainedVars)                View the submittedData. retainedVars, if specified, is the list of variables that may be displayed.
                                                 Default is the variables specified in the session descriptor

 saveSubmittedDataAsXML(filename,rndlist,idlist,varlist,doAllr)      
                                  View the submitted data as an XML file in new window, or use server hack to save it to local hard drive
                                  This requires that the submitted data be downloadd from the server first, and be stored in submittedData
                                          filename : if '', view in new window. Else, use server script to invoke a save file dialog, to save contents to filename on local hard drive
                                          roundlist: if not specified, or *, save all rounds. Else, space delmited list of rounds to save
                                          idist: if not specified, or *, save all ids (in all rounds). Else, space delmited list of numeric ids (typically, as generated when client joined a session) to save
                                          varlist: if not specified, or *, save all variables (for all ids in all rounds). Else, space delmited list of variables to save
                                          doAllr  : If 1, write all entries (intermediate and final). Else, just write final

saveParticipantsAsXML(xmlNode,filename)    View the list of participants (stored in an "XML file node")  as an XML file in new window, or use server hack to save it to local hard drive as filename
                                          filename : if '', view in new window. Else, use server script to invoke a save file dialog, to save contents to filename on local hard drive

*/


/* === Global variables === */

doNodes=[];                             // list of nodes in a <DO> or several <DO>s
doNodesRemaining=0;                     // number of do nodes not yet processed 

doActions=' IF GETPARAMS CHECKSCENARIOLIST LOADSCENARIO POSTCAP RECOVERPARAMS POSTPARAMS CHECKCLIENTS  GETDATA UPDATEDIV UPLOADDATA SAVEDATA DOWNLOADDATA POSTVAR JOIN GETVAR RESOLVEREFERENCE RESOLVE ADDPARTICIPANTDATA GETPARTICIPANTS GETP RESET DISPLAY ASSIGNREFID ASSIGNID FOCUS FROMRESULTS SET TAKESNAPSHOT LOADSNAPSHOT ALERT CONFIRM READ  UPDATESUBMITTED GETSUBMITTED READLOCAL  STOP NEXT_TASK  MONITOR ';
var postFileTypes=' OTHER SESSION ROUND '; 

var submitFormList=[];              // list of variables modified by the FORM submit. Re-initialited by makeclientparametersXML

actionDiv='_controlStatus';                 // where to write status (maybe reset by doStuff)
actionDivHistory='_controlStatusHistory';                 // where to write status history


generalVars={};               // variables read from <GENERAL> section of session specific files
generalVars['$VARS']=[] ;                  // array  variables defined in generalvars
generalVars['$lastSaved']={};               // values of general variables that were written to a postcap

userData={} ;                  // client specific variables read from <USER> section of session specific files
userData['$IDS']=[] ;                      // array of IDS defined in userData
userData['$VARS']=[] ;                  // array of user variable names
userData['$VARLOOKUP']={};                  // object of user variable names

resultsData={} ;                  // created directly, or a conversion from workingdata: current round results
resultsData['$IDS']=[] ;
resultsData['$VARS']=[] ;
resultsData['$VARLOOKUP']={} ;
resultsData['$ROUND']='';
resultsData['*']={};
resultsData['*']['$VARS']=[] ;
resultsData['*']['$VARLOOKUP']={} ;

currentParticipants=new Object() ;             //  each object is an ID of a participant currently enrolled in this session -- 
                                               //   currentParticipants[anid] can contain clientId, referenceId, contact
currentParticipants['$IDS']=[] ;                      // array of IDS defined in
currentParticipants['$VARS']=[] ;                  // array of user variable names
currentParticipants['$VARLOOKUP']={};                  // object of user variable names
currentParticipants['$TIME']='' ;                      // getTime time currentParticipants last updated

submittedData=new Object() ;                     // data submitted by clients
  submittedData['$records']=0 ;
  submittedData['$idList']=[] ;                  // array of ids with data.
  submittedData['$ids']={} ;           // object of ids. Each object is an array of objects in submittedData that belong to this id
  submittedData['$roundList']=[] ;               // array of rounds that have one or more data rows
  submittedData['$rounds']={} ;        // object of rounds. Each object is an array of objects in submittedData that belong to this round
  submittedData['$varList']=[] ;                 // array of variable names mentioned in any data record
  submittedData['$vars']={} ;          // object of variable names mentioned in any data record. Object value=1 (flag that variable exists)


serverVars=new Object();                   // variables retrieved from server

refData=new Object() ;                  // reference variables
refData['$IDS']=[] ;                      // array of IDS defined in refData
refData['$MAXREFID']=0 ;

refData['$VARS']=[] ;                  // array of reference variable names
refData['$VARLOOKUP']=new Object();                  // object of reference variable names

workData=new Object() ;                         // used to stored "working data" (read from local XML files)

formVals=[];                    // each element will be a 3x1 array : type, name, value
radioList=new Object() ;                // list of <radio> nodes, by nodename (each nodename element is an array of RADIO nodes with this name
arrayList=new Object() ;                // list of input nodes created for an array
stepsList=new Object() ;                  // list of <steps> nodes

// styles used to display RADIO buttons
 styleNoDefault="background-Color:#fdfdfd;padding: 2px; border:1px dotted yellow; color:#999999 "  ;
 styleYesDefault="background-Color:#fdfdcd;border: 1px solid black;padding: 2px;font-weight:600 " ;
styleClickDefault="background-Color:#dbdbab;border: 1px solid yellow;padding: 2px;font-weight:400 " ;

// styls used to show data-messages box
styleDataMessage='position: fixed; bottom:10%; padding:10px;margin-left: 10%; margin-right: 10%;  width:80%;  font-size:16px;  font-weight:500; background-color:#ecfcac;border:2px dashed blue ';

// status of the DescDiv "scenario variable description" divs
doViewDescDivStat=0;

/* -------------------
      Notes on storage  (note the uppercase is always used in object references):

           general variables are saved in the generalVars object. Hence, a "costFactor" variable would be saved as generalVars['COSTFACTOR']
            generalVars['$VARS'] contains a list of all the general variables.
            Actually, a 3 element array is saved to generalVars['VARNAME']:
                        generalVars['VARNAME'][0] =  1 if this should be treated as an array
                        generalVars['VARNAME'][1] =   value. If an array, this will be an array
                        generalVars['VARNAME'][2] =   uncapitalized version of the varname
            You can use readGeneralVar() and  getVarInfo() to work with generalVars.

           user variables are saved in the userData object, using a seperate element for all variables belonging to a given client
               Hence, for clientId of 31, a "income" variable would be saved as userData['31']['INCOME']
           
                userData['$VARLOOKUP'] is an object used to identify if an variable exists: i.e.; userData['$VARLOOKUP']['COST']=1 if "COST" variable exists
                                       Otherwise, undefined
                userData['$IDS'] contains an array of all the ids.
                userData['$VARS'] is an array, with each element one of the variables (thus, $VARS and $VARLOOKUP contain the same variable names, one as an
                object, one as an array)      

           reference variables are saved in the refData object. A record is stored under a 'record-id' (sometimed referred to as a  'row-id').
                Hence, for record-id of 12, a "group1" variable would be saved as refData['12']['GROUP1']
                refData['$VARLOOKUP'] is an object used to identify if an variable exists: i.e.; refData['$VARLOOKUP']['COST']=1 if "COST" variable exists
                                      Otherwise, undefined
                refData['$IDS'] contains a list of all the ids.
                refData['$VARS'] is an array, with each element one of the variables (thus, $VARS and $VARLOOKUP contain the same variable names, one as an
                object, one as an array)      

           The list of current participants is saved in the currentParticipants object, using a seperate element for
            all variables belonging to a given      client. The syntax is the same as userData. 
           In particular, the clientID used in userData is associated with the clientID used
           in currentParticipants. And this clientID is ultimately supplied by the server.
               Each client-specific element of currentParticipants will have a NAME, variable; and may have a
                NICKNAME and CONTACT variable (for each element).
               If needed  (depending on the HOW_ASSIGN instrument descriptor variable) a REFID variable is added.
               REFID points to a record-id in the reference variables.
               In fact, userData are often generated by copying the variables from the appropriate reference record -- 
               from the reference record with a record-id that matches the REFID assigned to this client (and stored
               in the partticipants database).


 -------------------*/

/* ========================= End of global variables ========================*/


/* ========================= Begin functions  ========================*/

/* =========================== */
/* read session-specific xml files and save variables, post server variables, post client-accessible parameter files
   useName is the name of the <DO> to process. Or, an array of names (to be called sequentially)
    Other arguments are used on recursive calls
*/

function doStuff(useName0,ido,iat,bvar,adelim) {

var snodes,gotNode,aname,m,anode,adelim,tavar,aval,gnode,avar2,toPost,saveVars,mm,bval,avar;

var refid,dovars,uadds,uadds2,uid,useref,rvar1,aval1,avalue,dafile,anid ;
var qq,daif,qnode,amm,foo,asCSV;
var isubtask,goo,iil,ldone,lstill,useName,amess,ashow;
var rvar,vv2,vv,newname,newval;
var t0,t1,t2,afile,astuff,aval0,unesc;
var  dovars2=[] ;
var gresults,gsaves,cvar,tmp2,dtmp,tmp1,tmp,m0,rlist,doAllr,lug;
var dahow,atype,around,useround,jj,astuff,doshow,awork;
var adiv,vvid,aclear,isFirst;
var showtype,nodeList,nowUseName,apopup,gexpand ;
var ms,avals,isStrong;
var doNow;
var dastyles,dastyles2,astyle,localFile,usediv,afocus;
var ic,uvar,pvar,iic,aid;

var sround,subround,sroundSkip ;
var goon,adefault;
var useNameRef,around ;

if (arguments.length<2) ido=1;
if (arguments.length<3) iat=-1;

useNameRef=useName0 ;


//=== an internal function for setting some counters
// write some status information to several spots:
//   _subTaskStatus : the 3rd column in the task history' purple bar. It counts from first to last sub-task, and dispalys done when done
//   _subTaskStatusFixed: the "fixed" block that counts the number of subtasks (green while counting, purple when done)
//  Note: the _lastAction area (the middle columun of the task history bar) is written to by writeLastAction. TYpically,
//        writing a "summary" attribute of an element (such as a form)

function resetCounters(iat) {
 var isubtask,goo,amm;

  isubtask=document.getElementById('_subTaskStatus');
  if (isubtask) {
        goo='<span style="color:green">'+iat+'</span> of '+doNodes.length ;
        if (iat==doNodes.length) goo='<span style="color:green;background-color:yellow">Done</span> ('+goo+')';
        amm=getAttributes(doNodes[0],'message',' ');
        goo='<span title="rrTask description: '+amm+'">'+goo+'</span>';
        isubtask.innerHTML=goo;
  }   
  isubtask=document.getElementById('_subTaskStatusFixed');
  if (isubtask) {
        if (iat==doNodes.length) {
                goo='<span style="font-size:90%;background-color:#e2e3e4;color:purple">'+iat+'</span> ';
        } else {
                goo='<span style="font-size:250%;background-color:yellow;color:green;border:3px groove green">'+iat+'</span> '; 
        }
        isubtask.innerHTML=goo;
  }
  return 1;
}


//===
 
if (ido==1) {                                   // first, find the DO node (or use node in usename)

  nodeList=[];
  if (typeof(useName0)!=='object') {
     useName=[];
     useName[0]=useName0;
  } else {
     useName=useName0;
  }

  deFocusNextTask(0) ;                  // defocus the next task results?

  writeDoStatus('','','RESET',' Start task: '+useName0);
  snodes=E2_getNodes(controlNode,'do');
  if (snodes.length<0) {
           alert('Error: no DO nodes in control file ');
           doNodes=[];
           return 1;
  }
  for (mm=0;mm<useName.length;mm++) {
      nowUseName=strip(useName[mm]).toUpperCase();
      gotNode=0;
      for (var m=0;m<snodes.length;m++) {
         aname=getAttributes(snodes[m],'name',' ');
         aname=strip(aname.toUpperCase());
         if (aname==nowUseName) {
             gotNode=snodes[m];
             break;
         }
      }
      if (gotNode===0) {
          alert('Unable to find DO node with name= '+useName[mm]);
          doNodes=[];
          return 1;
      }
      nodeList=nodeList.concat(gotNode);  
  }
  if (nodeList.length==1) {            // just one do
        gotNode=nodeList[0];
  } else {
        gotNode=E2_xmlToDom(nodeList[0]);
        for (var mm=1;mm<nodeList.length;mm++) {                // array of dos -- create one big temporary do with all of contents of this array of dos
           E2_xmlToDom(nodeList[mm],gotNode,'do') ;             // append children of nodeList[mm] to gotNode
        }
   }

 
// check if a <do> is  currently running
   if (doNodes.length>0) {
        tmp=confirm('Warning: a <do> ('+getAttributes(doNodes[0],'name','')+') is active. \n Do you want to start this ('+getAttributes(gotNode,'name','')+') <do> ? ');
        if (!tmp) return 0;
        alert(' The prior task will be aborted, and the just selected one will now start. ');
    }

// gotnode points to the <do> .. </do> section that contains instructions. Read them!
   doNodes=[]; 
   doNodes[0]=gotNode;          // the first do is used for messages, etc


   for (var m=0;m<gotNode.childNodes.length;m++) {
        anode=gotNode.childNodes[m];
        if (anode.nodeType==1) doNodes=doNodes.concat(anode);
    }
    if (doNodes.length==0) {
         alert('Sorry: no actions specified in a <DO> ');
         doNodes=[];
         return 1;
      }

// change style (clickStyle?)
  dastyles=strip(getAttributes(doStuffNode,'styleclick clickstyle ','-1'));
  if (dastyles!=='-1' && dastyles!=='') {
        if (dastyles=='1' || dastyles=='3' ) {
                doStuffNode.style.color="green" ;
        } else {
            dastyles2=getWord(dastyles);
            if (dastyles2.length==1) {
              doStuffNode.style.cssText=doStuffNode.style.cssText+';'+dastyles;
            } else {
             doStuffNode.style.cssText=doStuffNode.style.cssText+';'+dastyles2[0];
            }        
        }
  }


// process each of the doNodes
   doNodesRemaining=doNodes.length ;        // this is a global that can be monitored
   doStuff(useNameRef,2,1)     ;                       // start with the first node in doNodes (node 0 reserved array of original node names)
   return 1 ;
}                              //ido=1  (first call to doStuff
 
/* ---  IDO=2 "starts processing" of an action
        Other value of IDO are after processing an action, and followup is needed. Tyically, these are actions that
        talk to the server, and the followup does something with the server's response
        The IDO=2 code is after all the "followup" code

        Note on advancing to next instruction:
            doStuff is "sequentially recursive"
            To go to next step, doStuff is recalled with a pointer to the next step to execute (the iat variable)
            The steps are stored in the doNodes global, which is created on the first call to doStuff.
            Thus,
                 when an action is done, do stuff should be re-called using
                        iat++ ;
                        doStuff(useName,2,iat);                         
                        return 1 ;
                if you add a followup (for a non IDO=2 action), a short cut (that avoids the recursive overhead )
                        iat++ ;
                        ido=2 ;
                   (and supporting the shortcut is why ido=2 follows all the other idos! )
*/
 
if (ido==21) {                                   // sevrver var posted response: note if error 
   if (setServerVarResults.substr(0,1)=='-') {
        alert('Error: unable to post variable:' + bvar);
        doNodes=[];
        return 1;
    }
    writeDoStatus('Variables saved to server: <tt>'+bvar+'</tt>',iat,'INTER');
    iat++ ;
    ido=2;
}

if (ido==23) {                                   //GetVar: sevrver var retreived: save results to serverVars and controlVars
    gresults=getWord(getServerVarResults,0,adelim);
    gsaves=getWord(bvar);
    aarg='<ul > \n';
    for (var m=0;m<gsaves.length;m++){
         t1=parseAt(gsaves[m],'=');
         if (m>gresults.length-1) {
              aval='';
         } else {
             aval=gresults[m];
         }
         if (t1.length==1) {
                avar=strip(t1[0].toUpperCase());
                serverVars[avar]=aval ;
         } else {
                avar=strip(t1[1].toUpperCase());
                serverVars[avar]=aval ;
                cvar=strip(t1[0].toUpperCase());
                if (avar.indexOf('$')>-1) {
                      alert('Bad  control variable names in GETVAR: $ can NOT be used in saveAs: '+bvar);
                     doNodes=[];
                     return 1;
                }
                controlVars[cvar]=aval;
         }
        a1=gresults[m];
        if (a1.length>80) a1=a1.substr(a1,80);
        a1=a1.replace(/>/g,'&gt;');
        a1=a1.replace(/</g,'&lt;');
        aarg=aarg+' \n <li> '+avar+' = ' +a1;
    }
    aarg=aarg+'\n </ul>'
    writeDoStatus('Variables retrieved from  server: '+aarg,iat);
    iat++ ;
    ido=2;
}

if (ido==22 || ido==29) {                                   // response from reading parameter file (22), or loadSnapshotFile (29)
  noGet='' ; unesc=0;  

  if (ido==22) {                                //
    noGet=getAttributes(doNodes[iat],'noGet','');
    noGet=' '+noGet.toUpperCase()+' ';
  } else {
      unesc=1;                     // reading loadSnapshot, so unescape values
      bvar='SnapShot file ';
  }

  if (ido==29) {
     processXMLParamsFile(getFileNode,1,noGet,unesc,iat);
  } else {
     processXMLParamsFile(getFileNode,0,noGet,unesc,iat);
  }

  if (ido==29) {
       tmp='SNAPSHOT file (for this session) read:  # general vars: '+readGeneralVar().length;
   } else {
      tmp='File read: '+bvar+'. # general vars: '+readGeneralVar().length;
   }
   tmp=tmp+'; # currentParticipants ='+currentParticipants['$IDS'].length+' (with '+userData['$IDS'].length+' userData entries), '+userData['$VARS'].length+' user variables';
   tmp=tmp+'; # reference ='+refData['$IDS'].length+', '+refData['$VARS'].length+' reference variables';
   writeDoStatus(tmp,iat,'INTER');

  iat++ ;
  ido=2 ;
}                       // end of 22/29 processing (GETFILE and TAKESNAPSHOT) 

if (ido==24) {                                   // response from posting client-accessible file to server

   if (postFileResponse.substr(0,1)=='-') {
        alert('Error: unable to post file (' + bvar+') = '+postFileResponse);
        doNodes=[];
        return 1;
    }
    writeDoStatus(bvar+' <em>client accessible parameter</em> file posted to server ('+adelim+'): '+ postFileResponse,iat,'INTER');
    iat++ ;
    ido=2;
}

if (ido==28) {                                   // response from posting status (current parameters) file
   if (postStatusResponse.substr(0,1)=='-') { 
        alert('Error: unable to post status file: '+postStatusResponse);
        doNodes=[];
        return 1;
    }
    writeDoStatus('SNAPSHOT:  status file for session '+sessName+ ' (this session\'s current parameters) successfully posted to server ( '+ postStatusResponse,iat,' ');
    iat++ ;
    ido=2;
}

if (ido==35) {                                   // done with loadScenario
    writeDoStatus(bvar,'','RESET','Done with load scenario');
    iat++ ;
    ido=2;
}

if (ido==351) {                                   // quit after loadscenario
    writeDoStatus('LoadScenario quit');
    dsurveyFocus('_topBar_');  
    iat=doNodes.length  ;               // a hack to end this <DO>
    ido=2;
}


if (ido==36) {                                   // done with checkScenarioList (no scenario load)
    iat++ ;
    ido=2;
}

if (ido==361) {                                   // done with checkScenarioList (scenario load)
    writeDoStatus('Next scenario was retrieved');
    iat++ ;
    ido=2;
}


if (ido==41) {                                   // change user var status
    writeDoStatus(' Changing "allow to join status" to '+bvar  ,iat,'INTER') ;
    iat++ ;
    ido=2 ;
}

if (ido==203) {                         // getsubmitted (after retrieval of submittedData file <getSubmitted>
  var tmp=adelim.split('|');                                 // pre read, and sent as adelim argument -- the list of rounds to "retain" | all-records flag
  rlist=tmp[0];
  var doAllr=tmp[1];
  var csvList=tmp[2];
  parseSubmittedData(submittedDataNode,1) ;             // save to submittedData global

  if (strip(bvar)!=='.' && strip(bvar)!=='0' ) {
     writeDoStatus(' Submitted data has been retrieved. About to save rounds (' + rlist+') to suggested file name = ' +bvar,'','INTER');
     saveSubmittedDataAsXML(bvar,rlist,'','',doAllr);
     writeDoStatus(' .... success saving rounds '+rlist+'. You will need to use the file-save popup box to actually save the file. ','','INTER');
 } else {
     writeDoStatus(' Submitted data has been retrieved, and retained in submittedData ','','INTER');
 }

 if (csvList!=='0') {                  // write as CSV to popup window
    csvList=' '+csvList.toUpperCase()+' ';
    lug= E2_getNodes(submittedDataNode,'DATA');
    make_workData(lug,'*',' (pre CSV) saving Submitted  data to workData ',1,0);

    var headerType=1;                   // headerType: 0: no header, 1:first line varames (default is 1), 2: 1st line descrip, 2nd line varnames
    if (csvList.indexOf('NOHEADER')>-1) {
        headerType=0 ;
    } else {
      if (csvList.indexOf('DESC')>-1) headerType=2 ;
    }

    var missval='';
    if (csvList.indexOf('MISS=')>-1) {
        var ii=csvList.indexOf('MISS='); 
       missval=csvList.substr(ii+5,1);
    }

    var quoteNaN=1;
    if (csvList.indexOf('NOQUOTE')>-1) quoteNaN=0;

    stuff=workDataToCSV(headerType,missval,quoteNaN,rlist,1,'Converting workdata-submitted data to csv') 
    writeDoStatus('Writing '+stuff.length+ ' CSV bytes of Submitted data to a new window ');
    displayStringInWindow(stuff,0,2) ;

 }

 iat++ ;
 ido=2;

}


if (ido==2031) {                         // updatesubmitted (after appending current to all rounds server archives
  writeDoStatus(' The current-submitted-submittedData archive has been moved to the all-rounds-submittedData archive [ '+submittedDataAdd,'','INTER');
  iat++ ;
  ido=2;
}    


if (ido==204) {                         // getparticipants (after retrieval of participants) -- SAVE TO FILE
  writeDoStatus(' Participant list has been retrieved. About to save to suggested file name = ' +bvar,'','INTER');
  saveParticipantsAsXML(userListNode,bvar);
  writeDoStatus(' Participant list has been retrieved successfully. You will need to use the file-save popup box to actually save the file. ','','INTER');
  iat++ ;
  ido=2;
}


if (ido==25) {                              // getparticipants (after retrieval of participants) -- SAVE TO CURRENTPARTICIPANTS
    tt=parseUserListNode(userListNode,'RESET') ;             // save variables  to currentParticipants
/* DEPRECATED    controlVars['$ADDRESSES']=tt[0]          */
   tmp=getAttributes(doNodes[iat],'length varname var ','0');                     
   if (tmp!=='0')   {
        setControlVar(tmp,currentParticipants['$IDS'].length);
    }
    writeDoStatus('Current participant-list retrieved. # of current participants='+currentParticipants['$IDS'].length+' (<tt> '+tt[1]+' </tt>)',iat,'');
    iat++ ;
    ido=2;
}

if (ido==301) {                                   // post general data
   if (post3Response.substr(0,1)=='-') {
        alert('Error: unable to post '+bvar+' data to server archive= '+post3Response);
        doNodes=[];
        return 1;
    }
    writeDoStatus(' current-round <em>'+bvar+' data</em>  posted to server archive: '+ post3Response,iat,'INTER');
    iat++ ;
    ido=2;
}



// retrieving saved data (and then write to file): localFile,roundList+'|'+atype+'|'+awork+'|'+apopup+'|'+asCSV);

if (ido==333) {                                   // downloadDATA: user/genera/participants/results data has been retreived.. save it to local drive
  vv=adelim.split('|');
  rlist=vv[0]; atype=vv[1];awork=vv[2];apopup=vv[3];asCSV=vv[4];
  writeDoStatus(' retrieved <em>'+atype+' data </em>  from  server archive. ');

  if (bvar!=='0' ) {                                     // file="0" means do not save to file (say, just want to save to workdata)
      writeDoStatus(' ..... about to save rounds  (' + rlist+') to suggested file name = ' +bvar,'','INTER');
      if (asCSV=='0' || asCSV=='') {  
        saveDataAsXML(atype,bvar,rlist,'0','');
      } else {
        saveDataAsCSV(atype,bvar,rlist,asCSV,'');
      }
     writeDoStatus(' .... success saving rounds '+rlist+'. You will need to use the file-save popup box to actually save the file. ','','INTER');
  }

  if (apopup!=='') {                                     // file="0" means do not save to file (say, just want to save to workdata)
      writeDoStatus(' ..... about to display  (' + rlist+') in a popup ','','INTER');
      if (asCSV=='0' || asCSV=='') {  
        saveDataAsXML(atype,'0',rlist,'0',apopup);
      } else {
        saveDataAsCSV(atype,'0',rlist,asCSV,apopup);
      }
      writeDoStatus(' .... success displaying '+rlist+' in a popup. You can cut and paste the popup contents to a local file.','','INTER');
  }

  if (awork!=='') {
     if (awork=='1') awork='downloadData of the '+atype+' server archive';
     writeDoStatus(' saving downloaded data ('+awork+') to workData control-panel database ','','INTER');
     goo=saveDataNode.getElementsByTagName('DATA');
     lug=E2_getNodes(saveDataNode,'DATA');
     make_workData(lug,rlist,awork);
  }
  
  iat++ ;
  ido=2;               // onto the next <DO> action ...
}

/* -- END of followup code, Now do IDO=2 code */


if (ido==2) {                                   // process node IAT in doNodes
  useName=useName0;
  resetCounters(iat);
 
  if (iat>=doNodes.length) {                         // All done?  Note that donodes is dynamic in length; hence this active check
        doNodes=[];
// change style of clicked item?
      dastyles=strip(getAttributes(doStuffNode,'styleclick clickstyle ','-1')) ;
      if (dastyles!=='-1' && dastyles!=='') {
           if (dastyles=='1') {
                doStuffNode.style.color='purple';
           } else if (dastyles=='3') {
                doStuffNode.style.color='blue';
           } else {
              dastyles2=getWord(dastyles);
              if (dastyles2.length>1) {                 // if length=1, was done at start of task
                  doStuffNode.style.cssText=doStuffNode.style.cssText+';'+dastyles2[1]; 
              }        
           }
       }                        // dastyles exists
       doNodesRemaining=0 ;     // a global signal that the DO is done!
     
       return 1;               // all done
   }

   doNodesRemaining=doNodes.length-iat ;
   anode=doNodes[iat];                     // doNodes is global

   action=anode.nodeName; action=strip(action.toUpperCase()) ;                  // next action (is it legal? )
   if (doActions.indexOf(' '+action+' ')<0) {
        alert('Error: Unknown action in <DO> '+getAttributes(doNodes[0],'name','')+': '+action);   
        doNodes=[];
        return 1;
    }


// IF is special!
   if (action=='IF')  {            // get an XML file, save its parameters to globalVars or userData or refData (<IF>)
      qq=E2_getNodes(anode,'THEN ELSE ');
      if (qq.length>0) {
             alert('ERROR:  THEN and ELSE not supported in control panel <if> ');
             doNodes=[];
             return 0;
      }
      daif=getAttributes(anode,'if value var cond val name ',''); 
      if (daif!=='') {
           qq=E2_doMath(daif);
      } else {
         qq=0
      }  
      if (qq==0) {                       // false, skip step
        writeDoStatus(' Step  SKIPPED: IF not satisfied= '+daif,iat,'INTER');
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
      }
      qnode=E2_getNodes(anode,'*')  ;
      if (qnode.length==0) {                    // no actions to do, so skip
         iat++ ;
         doStuff(useNameRef,2,iat);                          // do next action
         return 1 ;
      }

// check qnode for text nodes (only task nodes allowed)
     goon=E2_getNodeText(anode);
     if (strip(goon)!=='') {
          alert('Warning: text string in a task <IF>: '+goon);
     }

     var tmp1=doNodes.slice(0,iat);
     var tmp2=doNodes.slice(iat+1);                     // skip this if, and then insert children of the if
     doNodes=Concat_Arrays(tmp1,qnode,tmp2);
      writeDoStatus('      IF is satisfied, '+qnode.length+' actions added. ('+daif+')',iat,'INTER');     //              If here, IF is true. So do it's internal node.
      doStuff(useNameRef,2,iat);                          // do next action (still is action iat!), which will be the first action in the IF
      return 1 ;
   }

   if (action=='GETPARAMS')  {            // get an XML file, save its parameters to globalVars or userData or refData

       dafile=E2_getNodeText(anode,1,'');
        if (dafile=='') {
                alert(' Error: no file specified in a GETPARAMS');
                doNodes=[];
                return 1;
        }
        toGet=getSessionDataURL+'name='+sessName+'&file='+strip(dafile);
        Get_xmlFile(toGet,'getFileNode','retrieving an XML data file ','doStuff',0,22,iat,dafile);
        writeDoStatus('Save Step # '+iat+': about to GETPARAMS ('+dafile+')','',' NOSAVE INTER ');
        return 1 ;
   }

   if (action=='LOADSCENARIO') {         // retrieve contents of a scenari file 
        doScenario2(0,iat,0);                     // retrieve list of scenarios
        return 1;
   }  

   if (action=='CHECKSCENARIOLIST') {          // check for first round of a new scenario
        ashow=strip(getAttributes(anode,'show' ,'0')) ;
        if (ashow=='1' || ashow.substr(0,1)=='Y') {              // no auto check, just open scenarios window    
              amess=anode.innerHTML ;
              tmp=doShowScenario(0,0,amess);
              doStuff(0,36,iat);
              return 0;
        }

        ashow=strip(getAttributes(anode,'check' ,'')) ;
        if (ashow=='1' || ashow.substr(0,1)=='Y') ashow=anode.innerHTML  ;
        ashow=ashow.replace(/\'/g,'`');
        ashow=ashow.replace(/\n/g,' '); 

        around=strip(getAttributes(anode,'var round' ,'nowround.g')) ;
        if (isNaN(around))  around=getVarInfo(around,'VALUE');              
        if (isNaN(around) || strip(around)==='') {
                around=strip(getAttributes(anode,'var round' ,'nowround.g')) ;
                alert('Error in checkScenarioList: bad round variable = '+around);
                return 0;
       }
        checkScenarioList(1,iat,around,0,ashow);
        return 1 ;
   }

   if (action=='RECOVERPARAMS' ) {         //  read cap files from server
        atype=strip(getAttributes(anode,'which','BOTH')).toUpperCase() ; 
        recoverParams(1,atype,useName,iat);
        return 1 ;                      // recoverParams will:   iat++ ;    doStuff(useName,2,iat);                          // do next action
  
   }

   if (action=='POSTPARAMS' || action=='POSTCAP' ) {         // post a file to the server  <POSTCAP> <POSTPARAMS>
        atype=getAttributes(anode,'type','OTHER'); atype=strip(atype.toUpperCase());
        aname='';
        if (postFileTypes.indexOf(' '+atype+' ')<0) {
                alert("Error. Bad type in '+action+': " + atype);
               doNodes=[];
                return 1;
        }
        if (atype=='OTHER') {
                aname=getAttributes(anode,'name','');
                if (aname=='') {
                        alert('Error. TYPE=OTHER, but no name specified ');
                       doNodes=[];
                        return 1;
                }
         }
         controlVars['$SUBMITFORMLIST']=submitFormList.join(' ');   // so that $submitFormList (in <GENERAL>) includes recently modified parameters
         tmp=[];
         for (var m0=0;m0<resultsData['*']['$VARS'].length;m0++) tmp=tmp.concat(resultsData['*']['$VARS'][m0]+".R");       // current GENERAL "results variables"
         controlVars['$RESULTS']=tmp.join(' ');   // so that $RESULTS (in <GENERAL>) includes results parametesr
         stuff=makeClientParametersXML(anode,1);

         submitFormList=[];                       // reinitialize list of "changed variables"

         stuff=escape(stuff);                        // escaped for safe passage (some stuff might end up being double esscaped)

         toPost=postClientFileBody+'&session='+sessName+'&name='+aname+'&type='+atype+'&contents='+stuff;
         Post_toServer(postFileURL,toPost,'postFileResponse','posting xml client accessible file ','doStuff',0,24,iat,atype) ;

         writeDoStatus('Save Step # '+iat+': about to '+action+' ('+atype+')','','NOSAVE INTER');
         return 1 ;
   }


// post user, general, or results data to a file <saveData> or to the the server <uploadData>
   if (action=='SAVEDATA' || action=='UPLOADDATA')  {            
       atype=strip(getAttributes(anode,'type','')); atype=atype.toUpperCase();
       gexpand=strip(getAttributes(anode,'expandG gexpand expand ','2'));               // default is to expand non-undefineds
       dahow=strip(getAttributes(anode,'how which' ,'').toUpperCase());  // mostly deprecated 
       if (dahow==='') {
          if (action=='SAVEDATA') dahow='FILE';
          if (action=='UPLOADDATA') dahow='SERVER';
       }
       if (atype!='GENERAL' && atype!='USERS' && atype!='RESULTS' && atype!='CUSTOM' && atype!='HISTORY' && atype!='PARTICIPANTS') {
                alert('Error in postData, bad type= '+atype);
                doNodes=[];
                return 1;
       }
       showtype=strip(getAttributes(anode,'showtype' ,'0').toUpperCase());

       around=strip(getAttributes(anode,'round','nowround.g')); around=around.toUpperCase();
       useround=getVarInfo(around,'VALUE');
       if (isNaN(useround) || useround==='' ) {
                alert('Error in '+action+': bad round '+around+' ('+useround);
                doNodes=[];
                return 1;
       }

// special attributes used for CUSTOM and HISTORY

       sround=strip(getAttributes(anode,'submitRound','nowround.g')); sround=sround.toUpperCase();
       if (sround.substr(0,1)=='L') {
           sround='-1';                // -1 signals "latest"
       } else {
           subround=getVarInfo(sround,'VALUE');
       }
       if (isNaN(subround) || subround==='' ) {
                alert('Error in '+action+': bad submit round '+sround+' ('+subround);
                doNodes=[];
                return 1;
       }
       sroundSkip=strip(getAttributes(anode,'submitSkipBlank submitSkip' ,'0').toUpperCase());
     

// create a string with the XML contents of the database ...
       varlist=E2_getNodeText(anode,1,'*');
       if (atype=='GENERAL') {
          stuff=makePostDataGeneral(useround,varlist);
       } else if (atype=='USERS') {
          stuff=makePostDataUsers(useround,varlist);
       } else if (atype=='PARTICIPANTS') {
          stuff=makePostDataParticipants(useround,varlist);
       } else if (atype=='RESULTS') {
          stuff=makePostDataResults(useround,varlist) ;
       } else if (atype=='HISTORY') {
          stuff=makePostDataHistory(useround,varlist,showtype,gexpand,sround,sroundSkip) ;
       } else if (atype=='CUSTOM') {
          stuff=makePostDataHistory(useround,varlist,showtype,gexpand,sround,sroundSkip) ;
       } else {
          alert(' Error: bad type in SAVEDATA ');
          doNodes=[];
          return 1;
       }

// post to server?
       if (dahow=='SERVER') {
          writeDoStatus('About to post '+atype+' data ('+stuff.length+' bytes)','','INTER');
          if (atype!='CUSTOM') {
              toPost=svyPost3Header+'session='+sessName+'&type='+atype+'&contents='+escape(stuff);
          } else {
               aname=strip(getAttributes(anode,'name','stuff'));   aname=aname.replace(/\s|\..*/g,'');  // remove spaces and .anything
               toPost=svyPost3Header+'session='+sessName+'&type='+atype+'&cname='+aname+'&contents='+escape(stuff);
          }
          Post_toServer(svyPost3URL,toPost,'post3Response','posting '+atype+' data ','doStuff',0,301,iat,atype) ;
          return 1 ;

       } else {                                                     // else, save to file

          dafile=strip(getAttributes(anode,'file','')) ;
          astuff='<?xml version="1.0" encoding="UTF-8" ?> \n  ';
          astuff=astuff+'<data_'+atype+'> \n';
          astuff=astuff+'   <!-- time: ' + dd + '   --> \n ';
          astuff=astuff+'   <!-- session: ' + sessName + '  --> \n ';
          astuff=astuff+'   <!-- round: ' + useround + '  --> \n \n ';
          astuff=astuff+stuff+' \n \n ';
          astuff=astuff+' \n </data_'+atype+'> \n';

// display?
          if (dafile=='') {                             // no filename -- display in window
             displayStringInWindow(astuff,1,0) ;
          } else {
             writeDoStatus('About to save '+atype+' data to local file ','','INTER'); // else, save file to the local hard drive
             saveStringToFile(dafile,astuff)
             writeDoStatus(' .... Note: to actually save '+atype+' data for this round, you will need to use the file-save popup box. ','','INTER');
          }                                 // dafile=''
       }                                     // dahow=server
       iat++ ;
       doStuff(useNameRef,2,iat);                          // do next action
       return 1 ;

   }                                // saveData and uploadData

   if (action=='POSTVAR')  {            // post a variable to the server
       avar=strip(getAttributes(anode,'name servervar',''));
       if (avar!='') {                          // literal mode
           aval=anode.value ;
           if (typeof(aval)=='undefined')   aval=getAttributes(anode,'value','');
            t0=avar+'='+aval ;
        } else {                                // equation mode
            aval0=E2_getNodeText(anode,1,'');
            t0=parseAt(aval0,'=');
            if (t0.length<2) {
                alert('Bad equation in POSTVAR:  '+aval0);
                doNodes=[];
                return 1;
            }
            avar=t0[0];
            aval=E2_doMath(t0[1]);
            t0='First equation: '+avar+'='+parseAt(t0[1],'\n');
         }

        if (avar.indexOf('$')>-1) {
                alert('Bad variable names in POSTVAR: $ can NOT be used in server variable names. '+avar);
                doNodes=[];
                return 1;
        }

        avar2=escape(strip(avar)); aval=escape(aval);
        toPost=setServerVarURL+'session='+sessName+'&varname='+avar2+'&value='+aval
        writeDoStatus('Save Step # '+iat+': about to POSTVAR ('+avar2+')','','NOSAVE INTER '); 
        Get_File(toPost,'setServerVarResults','posting server vars','doStuff',0,21,iat,t0) ;
        return 1 ;
   }


   if (action=='FOCUS')  {            // change the focus  <focus>

       t0=getAttributes(anode,'WHERE VALUE VAL ','');
       t0=strip(t0);
       if (t0=='') {
           writeDoStatus('Focus failure, no location','','NOSAVE INTER '); 
       } else {
           dsurveyFocus(t0,'0');                  // move focus?
           writeDoStatus('Focus is'+t0,'','NOSAVE INTER '); 
       }
       iat++ ;
       doStuff(useNameRef,2,iat);                          // do next action
       return 1 ;

  }


   if (action=='FROMRESULTS' || action=='RESULTS')  {            // retrieve results data
       dafile=E2_getNodeText(anode,1,'');
       vv=parseAt(dafile,'=');
       if (vv.length!=2) {
          alert('Error: bad FROMRESULTS = ' +dafile);
          doNodes=[];
          return 0;
        }

       rvar=vv[1];
       vv2=parseAt(vv[0],'.');
       newname=vv2[0] ;
       atype=strip(vv2[1].toUpperCase()).substr(0,1) ;           // first character is all that matters
       if (atype=='') atype='G' ;
       if (atype=='G') {
           newval=readResultsVar(rvar,'*','');
           foo=saveGeneralVar(newname,newval,0);        // never an array
           writeDoStatus('fromResults: general variable saved ' + newname);
        } else {
           for (var ic=0; ic<currentParticipants['$IDS'].length;ic++ ) {  
               newval=readResultsVar(rvar,currentParticipants['$IDS'][ic],'');
               saveUserVar(newname,currentParticipants['$IDS'][ic],nweval);
           }
           writeDoStatus('fromResults: user variable saved ' + newname);
      }

  }



   if (action=='ADDPARTICIPANTSDATA' || action=='ADDPARTICIPANTDATA' || action=='ADDP')  {            // get list of users currently enrolled in this session
        goon=E2_getNodeText(anode);
        t0=goon.split('\n');
        for (ic=0;ic<t0.length;ic++) {
           aline=strip(t0[ic]);
           if (aline=='') continue ;
           foo=parseAt(aline,'=');
           pvar=strip(foo[0]).toUpperCase();
           uvar=strip(foo[1]).toUpperCase();
           if (typeof(userData['$VARLOOKUP'][uvar])=='undefined') {   // no such user var
                alert('addParticipantsData error: no such user variable = '+aline);
                return 0;
           }
           if (pvar=='NAME' || pvar=='REFID') {
                alert('addParticipantsData error: can not set reserved currentParticipant variable='+aline);
                return 0;
           }
           for (var iic=0;iic<currentParticipants['$IDS'].length;iic++) { 
                aid=currentParticipants['$IDS'][iic] ;
                currentParticipants[aid][pvar]=userData[aid][uvar] ;
           }
           currentParticipants['$VARLOOKUP'][pvar]=1;
           currentParticipants['$VARS']=currentParticipants['$VARS'].concat(pvar);
        }
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
   }



   if (action=='GETPARTICIPANTS' || action=='GETP')  {            // get list of users currently enrolled in this session
       localFile=getAttributes(anode,'file','0');
        toGet=getUserListURL+'session='+sessName ;
        if (localFile==='0') {
            writeDoStatus('About to request list of participants ... ','','INTER');
           Get_xmlFile(toGet,'userListNode','getparticipants ... retrieving a list of current participants','doStuff',0,25,iat);
        } else {
             writeDoStatus('About to request list of participants (and then save to file) ... ','','INTER');
            Get_xmlFile(toGet,'userListNode','getpartipants ... retrieving   participants list prior to saving to file','doStuff',0,204,iat,localFile);
        }
        return 1 ;
   }

   if (action=='ASSIGNID'|| action=='ASSIGNREFID' )  {            // assign a reference ID to each participant, using REFERENCE database and HOW_ASSIGN session descriptor variable
        avar=strip(getAttributes(anode,'reset','1'));
        dastuff2=assignRefID(avar);
        if (dastuff2.substr(0,2)=='0,') {
                dastuff2=dastuff2.substr(2);        
                writeDoStatus('assignID:' +dastuff2,iat,'FINAL');
                controlVars['ASSIGNREFIDOK']=0 ;  
       } else {
            writeDoStatus('assignID:' +dastuff2,iat,'INTER'); 
                controlVars['ASSIGNREFIDOK']=1 ;
       }
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
   }


   if (action=='UPDATEDIV' )  {            // change contents of an element  <updatediv>
        atype=getAttributes(anode,'APPEND',0);
        anid=getAttributes(anode,'WHERE','');

        dtype=getAttributes(anode,'TYPE','block'); dtype=strip(dtype);  
        if (atype=='TOGGLE') atype='';
           if (dtype=='1' || atype.substr(0,1)=='Y' || dtype.substr(0,1)=='D') dtype='block';
           if (  dtype.substr(0,1)=='S') dtype='inline';
           if (  dtype.substr(0,1)=='N') dtype='none';

        if (anid==='') {
                alert('Error in updateDiv: No WHERE specified ');
                doNodes=[];
                return 0;
        }
        tmp=document.getElementById(anid);
        if (!tmp) {
                alert('Error in updateDiv: the id ('+anid+') can not be found ');
                doNodes=[];
                return 0;
        }

        amess='';
        for (var mm=0;mm<anode.childNodes.length;mm++) {
           bnode=anode.childNodes[mm];
           if (bnode.nodeType==3) {
                amess=amess+' '+bnode.nodeValue ;
           } else {
              if (bnode.nodeType==1 && (bnode.nodeName.toUpperCase()=='READ'|| bnode.nodeName.toUpperCase()=='SHOW') ) {      // ignore comments, etc
                avar=getAttributes(bnode,'var value name','');
                aval=getVarInfo(avar,'value','control');
                amess=amess+' '+aval;
              }
           }
        }
        if (atype==1) {
              tmp.innerHTML=tmp.innerHTML+' '+amess;
        } else {
              tmp.innerHTML=amess;
        }
        astyle=getAttributes(anode,'ADDSTYLE','');
        if (astyle!=='') update_styles(tmp,astyle)
        iat++ ;

        tmp.style.display=dtype ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
    }


   if (action=='DISPLAY' )  {            // toggle display of a div, or of one of the generic areas <DISPLAY>


        atype=getAttributes(anode,'TYPE',''); atype=strip(atype.toUpperCase());  
        if (atype=='TOGGLE') atype='';
           if (atype=='1' || atype.substr(0,1)=='Y' || atype.substr(0,1)=='D') atype='block';
           if (  atype.substr(0,1)=='S') atype='inline';
           if (  atype.substr(0,1)=='N') atype='none';
               
          aclear=getAttributes(anode,'CLEAR',0);

// special areas: _controlStatusHistory, _formArea, _controlStatus 

         divlist=E2_getNodeText(anode,1,'');

        aEcho=getAttributes(anode,'ECHO',''); 
        if (aEcho==1) writeDoStatus('Changing display of <tt>:'+divlist+'</tt>');

          divlist=strip(divlist);
          divlist=getWord(divlist);
          for (var m2=0;m2<divlist.length;m2++) {
                adiv=divlist[m2];
                displayDiv(adiv,atype);
                if (aclear==1) {
                       vvid=document.getElementById(adiv);
                       if (vvid) vvid.innerHTML=''
                }
         }
         
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
   }


   if (action=='TAKESNAPSHOT')  {            // save current status (client, reference, user, server variables to retrievable file on the server)

         toPost=doSaveSnapshotMake() ;          // this is also use by doSaveSnapshot()
         Post_toServer(postFileURL,toPost,'postStatusResponse','posting xml SNAPSHOT file ','doStuff',0,28,iat) ;
         writeDoStatus('Save Step # '+iat+': about to Post a status (all current parameters) file ','','NOSAVE INTER');
         return 1 ;

   }

   if (action=='STOP')  {                               // <stop>

// change style of clicked item?
        dastyles=strip(getAttributes(doStuffNode,'styleclick clickstyle ','-1')) ;
        if (dastyles!=='-1' && dastyles!=='') {
           if (dastyles=='1') {
                doStuffNode.style.color='purple';
           } else if (dastyles=='3') {
                doStuffNode.style.color='blue';
           } else {
              dastyles2=getWord(dastyles);
              if (dastyles2.length>1) {                 // if length=1, was done at start of task
                  doStuffNode.style.cssText=doStuffNode.style.cssText+';'+dastyles2[1]; 
              }        
           }
         }                        // dastyles exists
         doNodesRemaining=0 ;     // a global signal that the DO is done!
     
        resetCounters(doNodes.length);

        writeDoStatus('STOP: processing of this DO will stop',iat,'INTER');

        doNodes=[];
        return 1 ;
    }

   if (action=='ALERT')  {                      // <ALERT> 
        writeAnAlert(anode);
        iat++ ;
        doStuff(useNameRef,2,iat);
        return 1 ;
    }


   if (action=='NEXT_TASK')  {                      // <NEXT_TASK> 
        aval=getAttributes(anode,'value','');
        isFirst=0;
        doNow=getAttributes(anode,'open','0');
        afocus=strip(getAttributes(anode,'focus','1'));

        isStrong=getAttributes(anode,'strong highlight',0);
        if (aval=='') {
            avar=getAttributes(anode,'var','') ;        // use a variable
            avar=strip(avar).toUpperCase() ;
            if (avar==='') {
                 avar='FIRST_TASK_ID';        // if no VAR, and no VALUE, use default (FIRST_TASK_ID)
                 isFirst=1;
                 doNow=getVarInfo('first_task_goto','value','control');  
            }
            aval=strip(getVarInfo(avar,'value','control'));
            if (aval==='') {                                    // give up (first_task_id not specified?)
                 if (isFirst==1) {
                  alert('Error: <NEXT_TASK /> with no value specified for FIRST_TASK_ID ');
                 } else {
                    alert('Error: <NEXT_TASK> with no value or var attribute, or bad var== '+avar);
                 }
                iat++ ;
                doStuff(useNameRef,2,iat);
                return 0;
            }
        }                

        if (strip(aval)==='0') {                 // 0 means "ignore this next_task" (can be useful as default value of first_task_id
          iat++ ;
          doStuff(useNameRef,2,iat);
          return 1 ;
        }


        foo=document.getElementById(aval);               // does this id exists
        if (!foo) {
          alert('Error in <NEXT_TASK>: no such id = '+aval);
          doNodes=[];
          return 0;
        } else {                                // change the next_task_id (used by focusNextTask()
           controlVars['NEXT_TASK_ID']=aval;
        }

        controlVars['NEXT_TASK_STRONG']=isStrong ;

        if (isFirst==1) {
            dastuff=getVarInfo('FIRST_TASK_DESC','value','control');
        } else {
           dastuff=E2_getNodeText(anode,1,'') ;
        }

        if (dastuff!=='') {
              controlVars['NEXT_TASK_DESC']='Suggested next task: '+ dastuff
        } else {
              controlVars['NEXT_TASK_DESC']='The next suggested task: '+aval;
        }

        displayDiv('_nextTaskButton','inline');
        tmp=document.getElementById('_nextTaskButton1');
//alert('dodod '+doNow);
        if (tmp) {
            tmp.title=controlVars['NEXT_TASK_DESC'];
            if (doNow==1 || doNow==2 || doNow==3){ 
                if (doNow==3) { 
                    focusNextTask(0) ;
                } else {
                   focusNextTask(1) ;
                }  
                displayDiv('_nextTaskButton','none');
            }
            if (doNow==2){
                deFocusNextTask(1) ;
                tmp=document.getElementById(aval);
                fireEvent(tmp,'click');
            }                                           // auto-open variant of next_task
        }                                               // next task div is available 
        writeDoStatus('NEXT_TASK: next suggested task is '+ aval,iat,'INTER');

        dsurveyFocus(afocus,'_nextTaskButton');                  // move focus?

        iat++ ;
        doStuff(useNameRef,2,iat);
        return 1 ;
   
    }           // donow

   if (action=='GETSUBMITTED')  {            // download, from server, the submittedData. Then save selected rounds to local file <getSubmitted>
        var localFile=getAttributes(anode,'file','submittedData.xml');
        var toCurrent=getAttributes(anode,'getCurrent','0');
        var roundList=getAttributes(anode,'rounds','nowround.g');
        var csvList=getAttributes(anode,'csv','0');

        if (roundList=='' || roundList=='') roundList='nowround.g';
        doAllr=getAttributes(anode,'all doall','0');
        toGet=getSubmittedDataURL+'session='+sessName ;
        if (toCurrent==1) {
          roundList='current';
          doAllr='';           
          toGet=toGet+'&current=1';
           writeDoStatus('About to request current submitted data ... ','','INTER');
        } else {
           writeDoStatus('About to request submitted data ... ','','INTER');
        }

        Get_xmlFile(toGet,'submittedDataNode','getSubmitted.. retrieving submitted data','doStuff',0,203,iat,localFile,roundList+'|'+doAllr+'|'+csvList);
        return 1 ;
   }

   if (action=='UPDATESUBMITTED')  {            // download, from server, the submittedData. Then save selected rounds to local file
        toGet=updateSubmittedDataURL+'session='+sessName ;
         writeDoStatus('About to update current submitted data ... ','','INTER');
        Get_File(toGet,'submittedDataAdd','getSubmitted.. retrieving submitted data','doStuff',0,2031,iat);
        return 1 ;
    }

   if (action=='GETDATA' || action=="DOWNLOADDATA")  {            // download, from server, an uploaded dataset. Then save selected rounds to local file
        var roundList=strip(getAttributes(anode,'rounds round','nowRound.g'));

        if (roundList!=='*') {
           rlist=getWord(roundList);
          for (var jj=0;jj<rlist.length;jj++){
              rlist[jj]=getVarInfo(rlist[jj],'VALUE','GENERAL');
          }
           roundList=rlist.join(' ');
        }
        atype=getAttributes(anode,'type','');
        atype=strip(atype.toUpperCase()) ;
        if (atype!='GENERAL' && atype!='RESULTS' && atype!='USERS' && atype!='PARTICIPANTS' && atype!='HISTORY' && atype!='CUSTOM' ) {
           alert('Error in DOWNLOADDATA: bad type ='+atype);
           doNodes=[];
           return 1;
        }
        var localFile=getAttributes(anode,'file',atype+'data.xml');
        if (atype!='CUSTOM') {
            toGet=getSaveDataURL+'session='+sessName+'&type='+atype ;  
          } else {
               aname=strip(getAttributes(anode,'name',''));   aname=aname.replace(/\s|\..*/g,''); // remove spaces and .anything
               toGet=getSaveDataURL+'session='+sessName+'&type='+atype+'&cname='+aname ;

        }

        asCSV=strip(getAttributes(anode,'ASCSV CSV','0'));
        awork=getAttributes(anode,'workdata','');

        apopup=getAttributes(anode,'popup','');

        writeDoStatus('About to request '+atype+' data ... ','','INTER');
        Get_xmlFile(toGet,'saveDataNode','downloadData.. retrieving saved data (and then write to file)','doStuff',0,333,iat,localFile,roundList+'|'+atype+'|'+awork+'|'+apopup+'|'+asCSV);
        return 1 ;
   }

   if (action=='READLOCAL')  {            // retrieve file from local drive, save to workData  <readlocal>
     var usediv=getAttributes(anode,'useDiv use','5');             // the  "moveable box" to use. If it doesn't exist, it will be created
     var mr1=getAttributes(anode,'makeResults results ','0');
     var cr1=getAttributes(anode,'checkRound check ','');
     var ur1=getAttributes(anode,'useRound','');
     var rread1=getAttributes(anode,'read roundRead','');

      var localFile=getAttributes(anode,'file','submittedData.xml');
      writeDoStatus(' You should select the name of the file you wish to retrieve from the local hard drive, and then hit upload ');
      readLocalXmlFile(usediv,localFile,mr1,cr1,ur1,rread1,useNameRef,iat) ;

//      iat++ ;
//      doStuff(useNameRef,2,iat);                          // do next action
      return 1 ;
    }

   if (action=='LOADSNAPSHOT')  {            // read general, user, and reference info from SNAPSHOT file (and ERASE any prior general, user, and reference variables)
        dafile='SAVESNAPSHOT.xml' ;
        toGet=getSessionDataURL+'name='+sessName+'&file='+strip(dafile);
        Get_xmlFile(toGet,'getFileNode','retrieving an XML SAVESNAPSHOT file ','doStuff',0,29,iat);
        writeDoStatus('Save Step # '+iat+': about to loadSnapshot ','','NOSAVE  INTER');
        return 1 ;
   }

   if (action=='JOIN')  {            //  enable/disable participants joining session
        
        aval=getAttributes(anode,'allow','x') ;
        if (aval=='x') {
            alert('Error: a join command does not have an allow attribute ');
            doNodes=[];
            return 1;
        }  
        aval=strip(aval); aval=aval.toUpperCase();  
        if (aval.substr(0,1)=='Y' ) aval='1' ;
        if (aval.substr(0,1)=='R' ) aval='3' ;
        if (aval!=1 && aval!=3) aval='2' ;
        toGet=setServerStatusVarURL+'session='+sessName+'&varname=join&value='+strip(aval);
        amess='No new participants allowed';
        if (aval==1) amess='New participants are allowed ';
        if (aval==3) amess='Only rejoins are allowed ';
        Get_File(toGet,'getServerVarResults','posting server vars','doStuff',0,41,iat,amess) ;
        writeDoStatus('Save Step # '+iat+': about to change new-participants-joining status ('+aval+')','','NOSAVE  INTER');
        return 1;
    }

   if (action=='GETVAR')  {            // get  variables from the server  <getVar>
        adelim=getAttributes(anode,'delim',',') ;
        adefault=strip(getAttributes(anode,'default',''));
        dastuff=E2_getNodeText(anode,1,'') ;
        dastuff=dastuff.replace(/,/g,' ') ;
        dawords=getWord(dastuff);
        avars='' ;
        for (var m=0;m<dawords.length;m++) {
            t0=parseAt(dawords[m],'=');
            avar=t0[0];
            if (t0.length>1) avar=t0[1];
            avars=avars+avar+' ' ;   
        }
        t0=avars; t1=adelim;
        avars=escape(strip(avars)) ;
        adelim=escape(getAttributes(anode,'delim',','));
        toPost=getServerVarURL+'session='+sessName+'&varname='+avars+'&delim='+adelim ;
        if (adefault!=='') {
            adefault=escape(adefault) ;
            toPost=toPost+'&default='+adefault;
        }
        Get_File(toPost,'getServerVarResults','retrieving server vars','doStuff',0,23,iat,dastuff,t1) ;
        writeDoStatus('Save Step # '+iat+': about to GETVAR ('+t0+')','','NOSAVE  INTER');
        return 1;
    }

   if (action=='CHECKCLIENTS')  {            // check status of clients <checkClients>
       _checkClientNode=anode ;
       doCheckClients(1,useNameRef,iat);   // calls server, etc... then calls dostuff
       return 1;
   }

   if (action=='RESET')  {            // reset/delete variables  <reset>
       dastuff=E2_getNodeText(anode,1,'');
       if (dastuff=='') dastuff='GENERAL USERS REFERENCE SERVER CONTROL';
       dastuff=' '+dastuff+' ' ;
       dastuff2='';
       dastuff=dastuff.toUpperCase();
       if (dastuff.indexOf(' GENERAL ' )>-1) {
            dastuff2='GENERAL ';
           saveGeneralVar();
       }
       if (dastuff.indexOf(' USERS ' )>-1) {
            dastuff2=dastuff2+'USERS  ';
            saveUserVar();
       }
       if (dastuff.indexOf(' REFERENCE ' )>-1) {
            dastuff2=dastuff2+'REFERENCE  ';
            saveRefVar();
       }
       if (dastuff.indexOf(' SERVER ' )>-1) {
            dastuff2=dastuff2+'SERVER  ';
                serverVars=new Object();                   // variables retrieved from server
        }
       if (dastuff.indexOf(' CONTROL ' )>-1) {
            dastuff2=dastuff2+'CONTROL  ';
            controlVars={} ;                   
            var tmp1=E2_getNodes(sessionNode,'MESSAGE_OPTIONS','');
            if (tmp1.length>0)  {
                tmp2=strip(E2_getNodeText(tmp1[0]));
                setControlVar('MESSAGE_OPTIONS',tmp2);
             }
        }
        writeDoStatus('RESET: variables reset = ' +dastuff2,iat,'INTER');
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
  }        

   if (action=='MONITOR')  {            // <MONITOR> -- update <MONITOR>s
       processMonitor() ;                      // update <monitor>
       writeDoStatus('MONITORs processed','','INTER');
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
   }

   if (action=='SET')  {            // <SET> resolve user variables, using reference variables
       aval0=strip(E2_getNodeText(anode,1,''));
       doshow=getAttributes(anode,'SHOW','0');
       avals=aval0.split('\n');
       for (var ms=0;ms<avals.length;ms++) {
          aval=strip(avals[ms]);
          if (aval==='') continue ;
          t0=parseAt(aval,'=');
          if (t0[1]=='') {                      // no x= 
              avar=''; aval=t0[0];  vtype='no variable';
          } else {                             // x=something
              avar=t0[0]; aval=t0[1];  
              var vinfo=getVarInfo(avar);
              var vtype=vinfo.type ;
              if (vinfo.type=='SERVER') {
                  writeDoStatus('SET error: you can not use SET to change value of a .SERVER variable',iat);
                  iat++ ;
                  doStuff(useNameRef,2,iat);                          // do next action
                  return 1 ;
               }
        
          }
          var bval=E2_doMath(aval);
          if (avar!=='') {
            if (vinfo.type=='CONTROL') controlVars[vinfo.name]=bval ;
            if (vinfo.type=='GENERAL') saveGeneralVar(vinfo.name,bval) ;
          }
          if (doshow==2) {                               // show as a FINAL (with no technical notes)
               writeDoStatus(bval,'','FINAL');
          } else if (doshow==1) {                                // intermediate show, with technical notes (and do NOT save)
               writeDoStatus('SET: set '+vtype+'  '+avar+' = '+bval,iat,'NOSAVE INTER');
          } else {                                               // intermediate show, with technical notes (and do save)
               writeDoStatus('SET: set '+vtype+'  '+avar,iat,'INTER');
          }
        }                               // ms     
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
   }


   if (action=='RESOLVE' || action=='RESOLVEREFERENCE')  {            // resolve user variables, using reference variables
        if (currentParticipants['$IDS'].length==0)  {
           writeDoStatus('ResolveReference: no participants. Perhaps the participant list has not been retrieved from the server?',iat);
           iat++ ;
           doStuff(useNameRef,2,iat);                          // do next action
           return 1 ;
        }
        refid=getAttributes(anode,'REFID','REFID');                    // the match REFID (in currentParticipants data) to the  row ID in the REFERENCE database
        if (refid.indexOf('$')>-1) {
                alert('Bad ID name in ResolveReference: $ can NOT be used: '+refid);
                doNodes=[];
                return 1;
        }
        refid=strip(refid.toUpperCase());
        if (typeof(currentParticipants['$VARLOOKUP'][refid])=='undefined') { 
           writeDoStatus('<b>Problem with ResolveReference</b>: no such ID in list of currentUser variables:<tt> '+refid+'</tt> (current user-variables= <tt> '+currentParticipants['$VARS'].join(' | ')+'</tt>',iat);
           iat++ ;
           doStuff(useNameRef,2,iat);                          // do next action
           return 1;
        }

        if (currentParticipants['$VARLOOKUP'][refid]==-1) {             // no reference data (HOW_ASSIGN=NONE) 
           writeDoStatus('ResolveReference: no REFERENCE data processed (HOW_ASSIGN = NONE)',iat,'INTER');
           iat++ ;
           doStuff(useName,Ref2,iat);                          // do next action
           return 1 ;
        }

        dovars=getAttributes(anode,'VARS',' '); dovars=strip(dovars);
        if (dovars=='*' || dovars=='') {
                 dovars=refData['$VARS']  ;                          // add all available reference variables
        } else {
                dovars=getWord(dovars);
        }
        dovars2=[];
        for (var m2=0;m2<dovars.length;m2++) {
              rvar1=strip(dovars[m2].toUpperCase());
              if (typeof(refData['$VARLOOKUP'][rvar1])!='undefined') dovars2=dovars2.concat(rvar1);
        }
        uadds=0; uadds2=0;

// for all participants in the currentParticipants (on this contorl panel)
        for (var m=0;m<currentParticipants['$IDS'].length;m++) {
          uid=currentParticipants['$IDS'][m]; uid=strip(uid.toUpperCase()) ;  
          if (typeof(currentParticipants[uid][refid])=='undefined') continue ;     // no refID defined for this user, skip

          useref=currentParticipants[uid][refid]       ; 
          if (isNaN(useref)) useref=strip(useref.toUpperCase()) ;

          if (typeof(refData[useref])=='undefined') continue ;           // no reference row with that matches this users refid
          uadds++ ;

          if (typeof(userData[uid])=='undefined') {                     // create an element in the userData object (for this participant)
                  userData[uid]=new Object() ;
                  userData['$IDS']=userData['$IDS'].concat(uid)  ;                      // array of IDS defined in userData
           }

          for (var m2=0;m2<dovars2.length; m2++) {            // copy all of this row of refData to this row of userData 
                rvar1=dovars2[m2];
                aval1='';
                if (typeof(refData[useref][rvar1])!='undefined') aval1=refData[useref][rvar1] ;
                userData[uid][rvar1]=aval1 ;
                uadds2++ ;
          }
  
          if (uadds>0) {                                 // vars added, so add to userData['$VARS
            for (var m2=0;m2<dovars2.length;m2++) {
                 rvar1=dovars2[m2];
                 if (typeof(userData['$VARLOOKUP'][rvar1])=='undefined') {
                        userData['$VARLOOKUP'][rvar1]=1 ;
                        userData['$VARS']=userData['$VARS'].concat(rvar1);
                }
             }           // m2
          }                     // uadds
        }                       // m ... all the userData

        writeDoStatus('ResolveReference added/replace '+dovars2.length+ ' variables to '+ uadds+ ' userData ',iat,'INTER');
        iat++ ;
        doStuff(useNameRef,2,iat);                          // do next action
        return 1 ;
  }                             // reslove        

// SHOULD NEVER GET HERE
   alert(' no action? '+action);
   iat=iat+1 ;
   doStuff(0,2,iat)     ;                       // start with the zeroth node in doNodes

}                               // ido ==2


return 1;
}                               // END OF DOSTUFF 

// lastAction 

//=======
// help function: change focus (recognizes some special values
// used by <next_task> and <focus>
// afocus is where to go
// def1 is used if afocus=1
// afocus=0 means "do nothing"
// bump=Size of bump. 0 means none. Default is -15 (- means scroll down, so focus is below top of window)

function dsurveyFocus(afocus,def1,bump) {

var ii ;

if (arguments.length<1)afocus='_TOP';
if (arguments.length<2)def1='0';
if (arguments.length<3) bump='-15';
bump=strip(bump);

afocus=strip(afocus);
if (afocus=='1') afocus=strip(def1);
if (afocus=='0') return 0;              // do nothing
if (afocus.toUpperCase()=='_TOP') {
   window.scroll(0,0) ;
   return 1;
}
ii=document.getElementsByName(afocus);
if (ii.length==0) {
   document.location.hash=afocus ;   //  "set hash by id" might work
//   if (bump=='1') window.scrollBy(0,bump);
   return 0;
}
document.location.hash=afocus ;
//if (bump!=='1') window.scrollBy(0,bump);
return 1;

}


//=======
// calls server for client status, etc... then calls dostuff

function doCheckClients(ido,useName,iat,chkserver1) { 

var toGet,moo,m2,a1,moos,thisid,m,a2Name,lookupUser={},aid,arow,tt,nlookups=0,nowServerTime;
var tmpstat,qq,tmps,testEquation,dropEquation,repeatString,tmp,tmpa;
var sValue,sMessage,gnode,anode,sSummary,vv2;
var nDrop=0;nTest=0;
var atable,j,t1,t2,t1t2,m,daStates,daStates2,arow,at1;
var ww,aastate,aasummary,showClose,sUpdate,chkserver;
var itt,tmpvals,tmpvars,stmp;

if (ido==1) {
   toGet=getUserStatusURL+'name='+sessName;
   writeDoStatus('requesting user status ...','','NOSAVE RESET ',' requesting user status'); 
   Get_File([toGet,1],'getUserStatusResults','getting user status','doCheckClients',2,useName,iat);
   return 1 ;
}

// if here, got the resulst from the server. Also get some server vars?
if (ido==2) {
  anode=_checkClientNode ;                     // checkClientNode is global
  chkserver=getAttributes(anode,'server serverVars','') ; 

  if (chkserver!=='') {                         // got some server vars 
    toPost=getServerVarURL+'session='+sessName+'&varname='+escape(chkserver)+'&delim=,' ;
    Get_File(toPost,'getServerVarResults','reading server vars','doCheckClients',3,useName,iat,chkserver);
    writeDoStatus('Reading all server variables','','NOSAVE INTER');
    return 1 ;
  } else {
        getServerVarResults='';
  }
}

// if here, got server vars or no server vars to lookup
 
  anode=_checkClientNode ;                     // checkClientNode is global
  sValue=getAttributes(anode,'statusVar status','') ;   // results reported: 1 if queryvar and status are true for all
        sValue=strip(sValue);
        if (sValue!=='') {
           setControlVar(sValue,0) ;
        }


  sUpdate=getAttributes(anode,'statusUpdate update updateVar','') ;   // time of last server response
        sUpdate=strip(sUpdate);
        if (sUpdate!=='') {
           setControlVar(sUpdate,'') ;
        }

  sSummary=getAttributes(anode,'statusSummary summary summaryVar','') ;   // results reported: 1 if queryvar and status are true for all
        sSummary=strip(sSummary);
        if (sSummary!=='') {
           setControlVar(sSummary,'') ;
        }

  sMessage=getAttributes(anode,'statusMessage message ','') ; // results reported: a descriptive table to display
        sMessage=strip(sMessage);
        if (sMessage!=='')  setControlVar(sMessage,'n.a.') ;


  testEquation=''; dropEquation=''; repeatString=''; showClose='0'; 
       gnode=E2_getNodes(anode,'TEST');
       if (gnode.length>0) {
          testEquation=strip(E2_getNodeText(gnode,1,''));
          testEquation=testEquation.replace(/\n/g,' ');
       }
  gnode=E2_getNodes(anode,'DROP');
       if (gnode.length>0) {
          dropEquation=strip(E2_getNodeText(gnode,1,''));
          dropEquation=dropEquation.replace(/\n/g,' ');
       }

  gnode=E2_getNodes(anode,'REPEAT');
       if (gnode.length>0) {
         repeatString=strip(E2_getNodeText(gnode[0],1,''));
         repeatString=repeatString.replace(/\n/g,' ');
         showClose=strip(getAttributes(gnode[0],'close','0')).toUpperCase(); 
       }

//  gnode=E2_getNodes(anode,'CLOSE');
//  if (gnode.length>0) {
//         showClose=E2_getNodeText(gnode,1,'');
//         showClose=strip(showClose).toUpperCase() ;
//   }

moo=getUserStatusResults.split('\n');                   // 1 line for each particpant 
a2=moo[0].split('=') ;
nowServerTime=strip(a2[1]);

clist=[];
moos={};

for (var m=0 ; m<moo.length;m++) {
  a1=moo[m].split('&');                                 // & seperated values: id, time-of-last-request,absolute getTime(), message
  for (var m2=0;m2<a1.length;m2++) {
       a2=a1[m2].split('=') ;
       a2Name=strip(a2[0].toUpperCase());
       moos[a2Name]=strip(a2[1]);
  }
  if (typeof(moos['ID'])=='undefined') continue ;                  // bad id,   skip
  thisid=moos.ID ;
  clist=clist.concat(thisid);
  if (typeof(lookupUser[thisid])=='undefined') lookupUser[thisid]={} ;

  if (typeof(moos['MESSAGE'])=='undefined')    moos['MESSAGE']='';
  lookupUser[thisid].message=moos['MESSAGE'] ;

  if (typeof(moos['STATE'])=='undefined')    moos['STATE']=0;
  lookupUser[thisid].stateVar=moos['STATE'] ;

  nlookups++ ;
}

// now, compute the status?

if (sValue!=='') {
 tmpStat=1;
 dropEquation=strip(dropEquation);
 testEquation=strip(testEquation);
 testEquation=testEquation.replace(/\"/g,"'");
 for (var m=0;m<clist.length;m++) {
   aid=clist[m] ;
   setControlVar('_descVar',strip(lookupUser[aid].message));
   setControlVar('_stateVar',strip(lookupUser[aid].stateVar));
   if (dropEquation!=='') {                     // ignore this participant?
       qq=E2_doMath(dropEquation);
      if (qq>0) {
          nDrop++;
          continue ;
       }
   }
   if (testEquation!=='') {                     // ignore this participant?
       qq=E2_doMath(testEquation);
       if (qq==0) {
           tmpStat=0 ;
       } else { 
          nTest++ ;     
       }
   }

 }                              // m 
 setControlVar(sValue,tmpStat); 
}                               // svalue

// create a summary?
 if (sSummary!=='') {
   daStates={}; daStates2={};
   for (var m=0;m<clist.length;m++) {
      aid=clist[m] ;
      t1=strip(lookupUser[aid].stateVar).toUpperCase();
      t2=strip(lookupUser[aid].message).toUpperCase();
      t1t2=t1+' '+t2 ;
      if (typeof(daStates[t1t2])=='undefined') {
          daStates[t1t2]=0; daStates2[t1t2]='';
       }
       daStates[t1t2]++; 
      
      if (typeof(currentParticipants)!=='undefined' && typeof(currentParticipants[aid])!=='undefined') {
         vv2=currentParticipants[aid]['NAME'];
         daStates2[t1t2]=daStates2[t1t2]+vv2+'  ';
      }
   }
   atable='<div style="margin:5px 5% 5px 5%;background-color:#effeef"> \n';
   atable=atable+'<table border="1" cellpadding="1"><tr><td><span title="The status variable (_stateVar) most recently  provided by participants '+
          ' (i.e.; nowround)"> '+
          '  <tt>Status</tt></span></td><th><span title="The descriptiveWord (_descVar) most recently provided by participants ">Summary </span>  '+
          '  </th><th>Count</th><td>who</td></tr>';  
   for (var j in daStates) {
        aastate=''; aasummary='';
        ww=getWord(j);
        if (ww.length>0) aastate=ww[0];
        if (ww.length>1) aasummary=ww[1];
        arow='<tr><td><tt>'+aastate+'</tt></td><td>  <b>'+aasummary+'</b></td><td><tt>'+daStates[j]+'</td><td><span style="font-size:80%;font-family:monospace">'+daStates2[j]+'</td></tr>\n ';
        atable=atable+arow
   }
   atable=atable+'</table></div>\n';

  if (getServerVarResults!=='') {
       tmpvals=getWord(getServerVarResults,0,',');
       tmpvars=getWord(chkserver1);
       tt='<table cellpadding="1" border="1" style="background-color:#effeef"><tr><th>Server variables:</th>';
       itt=1;
       for (var j=0;j<tmpvals.length;j++) {
          itt++ ;
          if (itt>5) {
             itt=0;
             tt=tt+'</tr>\n<tr>';
           }
           tt=tt+'<td>'+tmpvars[j]+'=<tt>'+tmpvals[j]+'</tt> </td>';
       }
       tt=tt+'</tr></table>';
       stmp='<div id="showSelectedServerVars" style="display:none;background-color:#effeef">'+tt+'</div>';
  }

  if (repeatString!=='' && sMessage=='') {
     tt='<input value="'+repeatString+'" type="button"  onClick="doStuff(\''+useName+'\',1,0) " /> ';
     atable=atable+tt ;
     if (showClose=='DATAMESSAGE') {
         tmp='<input value="Close.." type="button"  onClick="writeDataMessage(0,-1)" /> ' ;
         atable=atable+tmp ;
     }
     if (showClose=='MOVEBOX') {
         tmp='<input value="Close..." type="button"  onClick="close_moveBox(1)" /> ' ;
         atable=atable+tmp ;
    }
    if (getServerVarResults!=='') {
         tmp='<input title="Display values of server variables (that are set by the control panel)" value="ServerVars .." type="button"  onClick="displayDiv(\'showSelectedServerVars\')" /> ' ;
         atable=atable+tmp ;
         atable=atable+stmp ;
    }
  }
 
   setControlVar(sSummary,atable) ;
 }


// save time of update
if (sUpdate!=='') {
  tt='<div style="font-weight:400;font-size:80%">Updated: '+Date(nowServerTime)+'</div>';
   setControlVar(sUpdate,tt) ;
}

// status message?
if (sMessage!=='') {
  tt='';
  tt=tt+'<div style="background-color:#bbccbb;font-weight:400">';
  tt=tt+'<table><tr><td># of participants: <tt> '+nlookups+'</tt></td><td><menu>\n ';

  at1='<span title="'+dropEquation+'" style="text-decoration:underline">dropped</span>'; 
  if (dropEquation!=='') tt=tt+'<li># of participants '+at1+' (not used in test computation): <tt>'+nDrop+'</tt></li>\n ';
  at1='<span title="'+testEquation+'" style="text-decoration:underline">passed test</span>';
  if (testEquation!=='') tt=tt+'<li># of participants who '+at1+':<tt> '+nTest+'</tt></li>\n ';
  tt=tt+'</menu></td></tr></table></div>';


  if (repeatString!=='') {

     tmp='<input value="'+repeatString+'" type="button"  onClick="doStuff(\''+useName+'\',1,0) " /> ';
     tt=tt+tmp;

     if (showClose=='DATAMESSAGE') {
         tmp='<input value="Close.." type="button"  onClick="writeDataMessage(0,-1)" /> ' ;
         tt=tt+tmp ;
     }
     if (showClose=='MOVEBOX') {
         tmp='<input value="Close..." type="button"  onClick="close_moveBox(1)" /> ' ;
         tt=tt+tmp ;
    }


  }
  setControlVar(sMessage,tt) ;

}

if (iat!==0) {
  iat++ ;    doStuff(useName,2,iat);   
}

return 1 ;

}

//=========== desc
// used to simulate click on dom objects (where .click() doesn't work)
function fireEvent(element,event){
if (document.createEventObject){    // dispatch for IE
  var evt = document.createEventObject();
  return element.fireEvent('on'+event,evt)
}
else{    // dispatch for firefox + others
  var evt = document.createEvent("HTMLEvents");
  evt.initEvent(event, true, true ); // event type,bubbling,cancelable
  return !element.dispatchEvent(evt);
}
}


// ==============
// process parameters in a XML params file
function  processXMLParamsFile(getFileNode,doreset,noGet,unesc,iat) {
var gnode,rvars,saveVars,saveRmax ;

 if (doreset==1) {                                // reset (i.e.; loading from snapshot
      generalVars=new Object();               // variables read from <GENERAL> section of session specific files
        generalVars['$VARS']=[] ;                  // array  variables defined in generalvars
        generalVars['$lastSaved']={};               // values of general variables that were written to a postcap
      userData=new Object() ;                  // client specific variables read from <USER> section of session specific files
        userData['$IDS']=[] ;                      // array of IDS defined in userData
        userData['$VARS']=[] ;                  // array of user variable names
        userData['$VARLOOKUP']=new Object();                  // object of user variable names
      refData=new Object() ;                  // reference variables
        refData['$IDS']=[] ;                      // array of IDS defined in refData
        refData['$VARS']=[] ;                  // array of reference variable names
        refData['$VARLOOKUP']=new Object();                  // object of reference variable names
      currentParticipants=new Object() ;             //  each object is an ID of a participant currently enrolled in this session -- currentParticipants[anid] can contain clientId, referenceId, contact, etc
        currentParticipants['$IDS']=[] ;                      // array of IDS
        currentParticipants['$VARS']=[] ;                  // array of user variable names
        currentParticipants['$VARLOOKUP']=new Object();                  // object of user variable names
        currentParticipants['$TIME']=new Date().getTime();
       bvar='SNAPSHOT file ';
  }                                                     // 29 (loadeparameters)

// get GENERAL variables
  if (noGet.indexOf(' GENERAL ')<0) {                           // not suppressed
     gnode=E2_getNodes(getFileNode,'GENERAL');
     if (gnode.length>0)  {
         rvars=parseVarNodeGeneral(gnode,unesc);             // create generalVars, using data in gnode
         saveVars=getAttributes(doNodes[iat],'generalVars','');
         if (saveVars!=='') {
             saveVars=strip(saveVars.toUpperCase());
             saveGeneralVar(saveVars,rvars);
         }
     }
  }

 // get USERS variables from param or saveparam file
  if (noGet.indexOf(' USERS ')<0) {                           // not suppressed
    gnode=E2_getNodes(getFileNode,'USERS');
    if (gnode.length>0)  {
          rvars= parseVarNodeUR(gnode,'USERS',unesc) ;
          saveVars=getAttributes(doNodes[iat],'userVars','');
          if (saveVars!=='') {
              saveVars=strip(saveVars.toUpperCase());
              saveGeneralVar(saveVars,rvars);
           }
     }
  }

// get REFERENCE variables
  if (noGet.indexOf(' REFERENCE ')<0) {                           // not suppressed
     gnode=E2_getNodes(getFileNode,'REFERENCE');
     if (gnode.length>0)  {
         rvars= parseVarNodeUR(gnode,'REFERENCE',unesc) ;
         saveVars=getAttributes(doNodes[iat],'refVars','');
         if (saveVars!=='') {
            saveVars=strip(saveVars.toUpperCase());
            saveGeneralVar(saveVars,rvars);
         }
     }
     saveRMax=getAttributes(doNodes[iat],'saveRefMax','0');
     if (saveRMax==1) {            // save server variable: maxRecordId
        var tmp=changeServerVars(1,'maxRecordId',refData['$MAXREFID']) ; // assume it works (don't bother checking response from server
   }


  }                     // reference

   if (noGet.indexOf(' currentParticipants ')<0) {                           // not suppressed
       gnode=E2_getNodes(getFileNode,'currentParticipants');
       if (gnode.length>0)  rvars= parseVarNodeUR(gnode,'currentParticipants') ;             // save currentParticipants. BUt don't bother tracking
   }            // current participants


 return 1;


}




//========
// recover the cap files, and read its general variables
// when done, do:  iat++ ;    doStuff(useName,2,iat);   


function  recoverParams(ido,atype,useName,iat) {
var aerr,afile,toGet,dafile;


if (ido==1) {
  if (atype=='BOTH') {
       if (typeof(sessionParamsURL)=='undefined') { alert('ERROR: no sessionParamsURL variable '); return '' }; 
        afile=sessionParamsURL.replace(/!SESS!/ig,sessName);
        Get_xmlFile(afile,['_capSession','_capSessionError'],'Retrieve SESSION CAP file ','recoverParams',2,atype,useName,iat);
        return 1;
  }
  if (atype=='SESSION') {
       if (typeof(sessionParamsURL)=='undefined') { alert('ERROR: no sessionParamsURL variable '); return '' }; 
        afile=sessionParamsURL.replace(/!SESS!/ig,sessName);
        Get_xmlFile(afile,['_capSession','_capSessionError'],'Retrieve SESSION CAP file ','recoverParams',3,atype,useName,iat);
        return 1;
  }
  if (atype=='ROUND') {
        if (typeof(roundParamsURL)=='undefined') { alert('ERROR: no roundParamsURL variable '); return '' };
        afile=roundParamsURL.replace(/!SESS!/ig,sessName);
        Get_xmlFile(afile,['_capRound','_capRoundError'],'Retrieve Round CAP file ','recoverParams',3,atype,useName,iat);
        return 1;
  }
}                       // ido=1

if (ido==2) {                   // return from BOTH -session read, so read round file
   if (typeof(roundParamsURL)=='undefined') { alert('ERROR: no roundParamsURL variable '); return '' };
    afile=roundParamsURL.replace(/!SESS!/ig,sessName);
    Get_xmlFile(afile,['_capRound','_capRoundError'],'Retrieve Round CAP file ','recoverParams',3,atype,useName,iat);
  return 1;
}

if (ido==3) {           // cap files read, now process snapshot file
     dafile='SAVESNAPSHOT.xml' ;
     toGet=getSessionDataURL+'name='+sessName+'&file='+strip(dafile);
     Get_xmlFile(toGet,['_snapShot','_snapShotError'],'retrieving an XML SAVESNAPSHOT file ','recoverParams',4,atype,useName,iat);
     return 1;
}

// now process...
  aerr=''+_snapShotError;
  if (aerr.substr(0,1)>2) {
        alert('Error in recoverParams: no snapshot available');
        return 0;
  }
  
  processXMLParamsFile(_snapShot,1,'',1,iat);            // reset all globals, and unescape

  if (atype=='BOTH' || atype=='SESSION') {
     aerr=''+_capSessionError;
     if (aerr.substr(0,1)>2) {
        alert('Error in recoverParams: no CAP-session file available');
        return 0;
    }
     processXMLParamsFile(_capSession,0,'',0,iat);
  }

  if (atype=='BOTH' || atype=='ROUND') {
     aerr=''+_capRoundError;
     if (aerr.substr(0,1)>2) {
        alert('Error in recoverParams: no CAP-round file available');
        return 0;
    }

    processXMLParamsFile(_capRound,0,'',0,iat);
  }


  iat++ ;    doStuff(useName,2,iat);   
  return 1;
}

//==========
// check if first round in a scenario (in scenario list)
// if so, ask if this scenario should be retrieved
function checkScenarioList(ido,iat,around,igot0,ashow) {
var stuff,m,toGet,toget1,igot=-1,gnode,rvars,glob ;
if (arguments.length<5) ashow='' ;

if (ido==2 || ido==22) {                   // ask if this scenario should be loaded


   if (ashow!=='' ) {              // custom "new scenario should start" window
        stuff='<input type="button" value="x" title="Click to NOT load scenario " onClick="doStuff(0,36,'+iat+')" />'+
                '  At round '+around+': <input type="button" value="load scenario: '+scenarioListUse[igot0][1]+'" '+
                        '  onClick="close_moveBox(10);checkScenarioList(22,'+iat+','+around+','+igot0+')" ' +
                           '      title="Click here when you are ready to load scenario ...." />';

              write_moveBox(10,stuff,ashow)

              open_moveBox(10);
//              doStuff(0,36,iat);
              return 0;
   }

   if (ido==2) {
     attnode=E2_getNodes(scenarioFile,'desc'); 
     stuff='(none available) ';
     if (attnode.length>0)   stuff=E2_getNodeText(attnode[0],1,'');
     stuff='A new scenario is scheduled to start: '+scenarioListUse[igot0][1]+'\n'+
            'Description: '+stuff+'\n'+
            'Do you want to load this scenario? ';
     aa=confirm(stuff);
   } else {
        aa=1;
   }
   if (aa==0)   {
      doStuff(0,36,iat);
  } else {
      gnode=E2_getNodes(scenarioFile,'GENERAL');
      if (gnode.length>0)  {
         rvars=parseVarNodeGeneral(gnode,0);             // create generalVars, using data in gnode
         saveGeneralVar('GENERALVARS',rvars);
      }
      glob=getWord(rvars);
      writeDoStatus('Saving '+glob.length+' variables in scenario: '+scenarioListUse[igot0][1],'','RESET');
      writeDataMessage('Saving '+glob.length+' variables in scenario: '+scenarioListUse[igot0][1],4);
      scenarioListUse[0][1]=scenarioListUse[igot0][1]      ;                // special element = last loaded scenario
      doStuff(0,361,iat);
  }
  return 1 ;
}

// no scenario list?
 if (typeof(scenarioListUse)=='undefined' || scenarioListUse[0][0]==0) {
    doStuff(0,36,iat);
    return 0;
 }

// else, is around first in one of the scenarios
for (var m=1;m<=scenarioListUse[0][0];m++) {
   if (scenarioListUse[m][3]==around) igot=m;
}
if (igot<0) {                   // NOT a first round 
    doStuff(0,36,iat);
    return 0;
}

toGet=getSessionDataURL+'name='+sessName+'&file=';          // root of scenario files 
toget1=toGet+scenarioListUse[igot][0]; 
Get_xmlFile(toget1,'scenarioFile','retrieving a scenario','checkScenarioList',2,iat,around,igot,ashow);
return 0;

}


//=======
// load or create a scenario 
// ido=0 : load
// ido=1 : create

function doScenario2(ido,iat,useName,ido2,ido3) {

var toGet,amm,i,arf,m,zoob,zarf,vfile,attnode,defScenario,defScenarioInstrument,defScenarioSession;
var instName,stuff,aname,aval,avar,m2 ;
var valList,arrayList,delimList,aval0 ;
var ishow1,ishow2,ishow0;
var rvars,saveVars,gnode;
var tname ;

dsurveyFocus('_formDisplay_')  ;

/* :: scenario loading :: */
if (ido==0) {                                   // load a scenario
  toGet=getScenarioListURL+'&session='+sessName;
  Get_File(toGet,'scenarioList','retrieving list of scenarios ','doScenario2',11,iat,useName) ;
  return 0;
}

if (ido==11) {                                   // display list of sessions
  amm=document.getElementById('_controlStatus');
   if (!amm) {
        alert(' doScenario2 error: no such id= _controlStatus ');
        return 0 ;
   }
  amm.innerHTML='<h3>List of scenarios </h3>';

   instName='';
  defScenario='';

  zoob=E2_getNodes(sessionNode,'SCENARIO_FILE');
  if (zoob.length>0)   defScenario=E2_getNodeText(zoob[0],1,'');

  zoob=E2_getNodes(sessionNode,'instrumentName');
  if (zoob.length>0)   instName=E2_getNodeText(zoob[0],1,'');

  a1=[];
   if (strip(scenarioList)!=='') {
      a1=getWord(scenarioList);                                 // '' seperates these simple values (not &)
   }
   arf='<table cellpadding="2"><tr><th class="scenarioCol">Scenario (click to select) </th>'+
        '<td><input type="button" value="quit" onClick="doStuff(0,351,'+iat+')" /> </tr>';

// are there "scenario" defaults defined? If so, make them available (instrument defaults, and session defaults)

   if (defScenario!='') {
    
     vfile=getInstrumentDataURL+'name='+instName+'&file='+defScenario;
     zoob=make_procString('Get_xmlFile',vfile,'scenarioFile','retrieving a scenario','doScenario2',13,iat,useName,'instrument default');
     arf=arf+'<tr><td class="scenarioCol"><span onClick="'+zoob+' " style="text-decoration:underline;color:blue">Instrument defaults</span></td>';

     vfile=getSessionDataURL+'name='+sessName+'&file='+defScenario;
     zoob=make_procString('Get_xmlFile',vfile,'scenarioFile','retrieving a scenario','doScenario2',13,iat,useName,'session default');
     arf=arf+'<td class="scenarioCol"><span onClick="'+zoob+' " style="text-decoration:underline;color:blue">Session defaults</span></td></tr>'; 
 
     } else {                                   // no SCENARIO_FILE specified  in the instrument desciptor file, so give up
         arf=arf+'<tr><td class="scenarioCol"><span>instrument defaults</span></td>';
         arf=arf+'<td class="scenarioDescCol">instrument defaults were not specified </td></tr> \n ';
         arf=arf+'<tr><td class="scenarioCol"><span>session defaults</span></td>';
         arf=arf+'<td class="scenarioDescCol">session defaults were not specified </td></tr> \n ';
    }

    toGet=getSessionDataURL+'name='+sessName+'&file=';          // root of scenario files 
    for (var m=0;m<a1.length; m++) {
       vfile=toGet+a1[m] ;
       scid='SC_'+m;
        arf=arf+'<tr>' ;

        tname=parseUsing(a1[m],'_','.');
// load xml link
        zoob=make_procString('Get_xmlFile',vfile,'scenarioFile','retrieving a scenario','doScenario2',13,iat,useName,a1[m]);
        arf=arf+'<td class="scenarioCol"><span onClick="'+zoob+' " style="text-decoration:underline;color:blue">'+tname+'</span></td> \n ';

        arf=arf+'</tr> \n ';
    }

// add display areas
   arf=arf+'</table> \n ';
   arf=arf+'<div id="scenarioShow" class="scenarioParams"> \n ';
  arf=arf+'</div> \n ';  
  amm.innerHTML=amm.innerHTML+arf;
 
  return 0;
}

if (ido==13) {                                   // scenario file retrieved, display use it
   attnode=E2_getNodes(scenarioFile,'desc'); 
   stuff='';
   if (attnode.length>0)   stuff=E2_getNodeText(attnode[0],1,'');
   attnode=E2_getNodes(scenarioFile,'general')
   if (attnode.length==0) {
       ishow1=document.getElementById('scenarioShow1');
      doStuff(0,35,iat,'No parameters were specified in this scenario file' );
      return 1 ;
  }
     gnode=E2_getNodes(scenarioFile,'GENERAL');
     if (gnode.length>0)  {
         rvars=parseVarNodeGeneral(gnode,0);             // create generalVars, using data in gnode
         saveGeneralVar('GENERALVARS',rvars);
     }
    var glob=getWord(rvars);
    scenarioListUse[0][1]=ido2      ;                // special element = last loaded scenario

   doStuff(0,35,iat,'Saving (to control panel variables) '+glob.length+' variables in scenario: '+ido2+' (<em>'+stuff+'</em>)' );
   return 1 ;
}


}

// =========================
// <ALERT> write an alert to alert box, _controlStatus div, or dataMessage div
// used by ALERT action in <DO> and in <CHANGE> action of form-elements
function writeAnAlert(anode,iat,aset,avalue) {
  var bnode,mm,amess,avar,aval,aval2,fade,tsec,autoClose,newNode;
  var toWhere,iv,tavar,iid,vstyle,i,aval2,tdiv,foo;
  var vvs,vuse,aheader,islit;

  amess='';

// this is used for non-datamessage & non-moveable (simple messages with no html within html)
  for (var mm=0;mm<anode.childNodes.length;mm++) {
           bnode=anode.childNodes[mm];
           if (bnode.nodeType==3) {
                amess=amess+' '+bnode.nodeValue ;
           } else {
              if (bnode.nodeType==1 && (bnode.nodeName.toUpperCase()=='READ'|| bnode.nodeName.toUpperCase()=='SHOW') ) {      // ignore comments, etc
                avar=getAttributes(bnode,'var value name','');
                aval=getVarInfo(avar,'value','control');
                amess=amess+' '+aval;
              }
          }
   }
   aval=amess ;

   if (arguments.length>2) {
       aval=aval.replace(/this.set/g,aset);               // replace a "this.set" with the  "set" value of form element
       aval=aval.replace(/this.value/g,avalue);               // replace a "this.value" with the  "value" value of form element
   }
   avar=getAttributes(anode,'where','');
   if (avar=='') avar='ALERT';

   islit=strip(getAttributes(anode,'literal','0'));
   if (islit=='1') aval=anode.innerHTML ;


   aheader=strip(getAttributes(anode,'header',''));
  
   ahvar=strip(getAttributes(anode,'headerVar',''));
   if (ahvar!==''){
        aheader=getVarInfo(ahvar,'value') 
   }


   toWhere=getWord(avar);
   for (var iv=0;iv<toWhere.length;iv++) {
      avar=strip(toWhere[iv]);
      tavar=avar.toUpperCase();
      if (tavar=='ALERT')  {alert(aval); continue  }
      if (tavar=='STATUS_FINAL'|| tavar=='STATUS' )  {
                writeDoStatus(' '+aval,'','FINAL'); continue  ;
      }
      if (tavar=='STATUS_INTER')  {                          // intermediate
           if (priorFinalOnly==1) {                                     // final only (so display=none
                writeDoStatus('Comment: '+aval,'','INTER'); continue ;
          } else {                                                      // not final only, so display=div
                writeDoStatus('Comment: '+aval,''); continue  ;
          }
      }

      if (tavar=='TASKBAR' || tavar=="STATUSNOW" || tavar=="TASKHISTORY"  )  {
           writeLastAction(aval,1);          // 1 means "alert highlight"
           continue ;
      }

      if (tavar=='SUGGEST' || tavar=='SUGGESTION')  {
           writeSuggest(anode);
           continue ;
      }

      if (tavar=='DATAMESSAGE') {

         newNode=E2_copyNode(anode);
         foo=newNode.getElementsByTagName('*');
         vvs=[]; vuse=[];
         for (var i=0;i<foo.length;i++) {vuse[i]=0; vvs[i]=''; }
         for (var i=0;i<foo.length;i++)  {
           if (foo[i].nodeName=='SHOW' || foo[i].nodeName=='READ') {
               avar=getAttributes(foo[i],'var value name','');
               aval=getVarInfo(avar,'value','control');
               vvs[i]=aval; vuse[i]=foo[i];
           }
         }

         for (var i=0;i<vuse.length;i++)  {
            if (vuse[i]==0) continue ;
            tdiv=document.createElement('span'); 
            tdiv.innerHTML=vvs[i];
            newNode.insertBefore(tdiv,vuse[i]);
            vuse[i].parentNode.removeChild(vuse[i]);
         }

         aval2=newNode.innerHTML ;
         tsec=getAttributes(anode,'time lifespan duraction seconds sec','');
         fade=getAttributes(anode,'fade','0');
         autoClose=getAttributes(anode,'autoClose close','1');
         if (tsec!=='') {
              writeDataMessage(aval2,tsec,fade,autoClose); continue  ;
         } else {
             writeDataMessage(aval2,0,0,autoClose); continue  ;
         }
         continue ;
      }

      if (tavar=='MOVEBOX') { 
         newNode=E2_copyNode(anode);
         foo=newNode.getElementsByTagName('*');
         vvs=[]; vuse=[];
         for (var i=0;i<foo.length;i++) {vuse[i]=0; vvs[i]=''; }
         for (var i=0;i<foo.length;i++)  {
           if (foo[i].nodeName=='SHOW' || foo[i].nodeName=='READ') {
               avar=getAttributes(foo[i],'var value name','');
               aval=getVarInfo(avar,'value','control');
               vvs[i]=aval; vuse[i]=foo[i];
           }
         }

         for (var i=0;i<vuse.length;i++)  {
            if (vuse[i]==0) continue ;
            tdiv=document.createElement('span'); 
            tdiv.innerHTML=vvs[i];
            newNode.insertBefore(tdiv,vuse[i]);
            vuse[i].parentNode.removeChild(vuse[i]);
         }
         aval2=newNode.innerHTML ;
         write_moveBox(1,aheader,'<div style="height:100%;background-color:#abbaab">'+aval2+'</div>');
         open_moveBox(1);
         continue ;
      }


// if here, write to the avar element 
      iid=document.getElementById(avar);
      if (iid) iid.innerHTML=aval
   }
   return 1 ;
} // anode

// iat
//==================== 
// write to suggestion box
// e is either a string, or a dom object whose innerHTML should be used
function writeSuggest(e) {


var id1,id2,id3,asummary,ee;
 if (typeof(e)=='string') {
        asummary=e;
 } else {
   ee=E2_copyNode(e);
   expandReads(ee);

// are there two divs? Then show the first one, and have a more... button to show the 2nd
   txt1=E2_getNodeText(ee,1,'');
   tnodes=E2_getNodes(ee);
   if (tnodes.length==2) {              
        if (tnodes[1].nodeName.toUpperCase()=='DIV' && tnodes[0].nodeName.toUpperCase()=='DIV' && strip(txt1)==='' ) {
            tnodes[1].style.display="none" ;
            aa=document.createElement('a');
            aa.href='#';
            aa.setAttribute('onClick',"displayNextElement(this)");
            znode2=document.createTextNode('more...');
            aa.appendChild(znode2);  
            ee.insertBefore(aa,tnodes[1]);
        }
   }
   asummary=E2_xmlToString(ee);
 }
id1=document.getElementById('_suggestionsBoxShow');
if (!id1) {
   alert('No suggestBox id (message ='+asummary)
   return 0;
}

oldmess=id1.innerHTML;     // retain prior message for history (below)
id1.innerHTML=asummary;

var def2=0;
var def1=E2_getNodes(sessionNode,'SHOWSUGGEST','');
if (def1.length>0)  def2=strip(E2_getNodeText(def1[0]));
var isshow=getAttributes(ee,'show',def2);

if ( (isshow==1 || isshow==2)  && suppressAutoSuggest!==1 ) {
   displayDiv('_suggestionsBox','block');
}

if (isshow==2 ) {
   id1=document.getElementById('_suggestionsHide');
   if (id1)  id1.focus();
}


id1=document.getElementById('_suggestButton');
if (id1) {
  id1.style.border='3px groove lime ';
}

id1=document.getElementById('_suggestButtonN');
if (id1) {
  var vv=id1.innerHTML;
  if (isNaN(vv) || vv=='') {
     id1.innerHTML=1;
   } else {
     id1.innerHTML=parseInt(id1.innerHTML)+1;
   }
}


id1=document.getElementById('_suggestionsBoxHistoryAddHere');
if (!id1) {
 alert('no suggestions box history ');
 return 0 ;                                            // not an error
}
var adate=svy_currentTime() ;
id1.innerHTML='<span style="font-weight:300;font-size:80%">'+adate+': </span>'+oldmess+'<hr/>\n '+id1.innerHTML ;
return 0;
}


//      ====================
// view current controlVars
function viewControlVars(towrite,doappend) {
var amm,tmp ;
var atable,atbody,atr,atd,i,ivar,atext,atext2,ainput,avar,aval,vval ;
var vlist=[];
var ccolors=['#ababab','#bababa'];
var icolor=0 ;

amm=document.getElementById(towrite);
if (!amm) {
        alert(' viewControlVars error: no such id= '+towrite);
        return 1 ;
}
if (doappend==0) amm.innerHTML='';

for (var ivar in controlVars)  vlist=vlist.concat(ivar);

atable=document.createElement('hr');
amm.appendChild(atable);
atable=document.createElement('h3');
    atext=document.createTextNode(vlist.length+' control variables: ');
    atable.appendChild(atext);
amm.appendChild(atable);

atable=document.createElement('table');
atbody=document.createElement('tbody');
atr=document.createElement('tr');
  atd=document.createElement('th');
    atext=document.createTextNode('Control Variable ');
    atd.appendChild(atext);
  atr.appendChild(atd);
  atd=document.createElement('th');
    atext=document.createTextNode('Value');
    atd.appendChild(atext);
  atr.appendChild(atd);
  atr.style.backgroundColor="cyan";
  atbody.appendChild(atr);  

for (var m=0;m<vlist.length;m++) {
  ivar=vlist[m];
  vval=controlVars[ivar]; 
  atr=document.createElement('tr');

  atd=document.createElement('th');
      ainput=document.createElement('span');
      atext=document.createTextNode(ivar);
      ainput.appendChild(atext);
      ainput.setAttribute('onClick','gotoVarView("submitControlChanges")');
  atd.appendChild(ainput);
  atr.appendChild(atd);

  atd=document.createElement('td');
   atext=document.createTextNode(vval);
    ainput=document.createElement('input');
    ainput.setAttribute('value',controlVars[ivar]);
    ainput.setAttribute('name',ivar);
    ainput.setAttribute('changed',0);
    ainput.setAttribute('size',80);
    ainput.setAttribute('changeValue',controlVars[ivar]);
    ainput.setAttribute('svyType','CONTROL');
    ainput.setAttribute('onChange','changeVarView(this)');
    ainput.setAttribute('onClick','showVarView(this,"Control")');
    atd.appendChild(ainput);

  atr.appendChild(atd);

  icolor=1-icolor ;
  atr.style.backgroundColor=ccolors[icolor];
  atbody.appendChild(atr);  
}
atable.appendChild(atbody);
amm.appendChild(atable);    

ainput=document.createElement('input');
ainput.setAttribute('type','submit');
ainput.setAttribute('id','submitControlChanges');
ainput.setAttribute('value','Save changes to  CONTROL variables');
ainput.setAttribute('onClick','saveVarView("CONTROL")');
ainput.style.marginBottom='80px';
amm.appendChild(ainput);    


return 1 ;

}


//      ====================
// view working data
function viewWorkingData(awhere,showbutton,redo) {        // old function name, keep it cause I am lazy
  if (arguments.length<1 || awhere=='') awhere=actionDiv ;
  if (arguments.length<2) showbutton=0;
  if (arguments.length<3) redo=0;
  a=viewWorkData(awhere,showbutton,redo) ;
  return a;
} 

function viewWorkData(awhere,showbutton,redo) {                   
if (arguments.length<1 || awhere=='') awhere=actionDiv ;
if (arguments.length<2) showbutton=0;
if (arguments.length<3) redo=0;

if (redo==0) workData_viewList={} ;                     // reset view list (to view all) ;

ii=document.getElementById(awhere);
ii.innerHTML='';
var ii,ttable,tbody,ttr,ttd,aval,avar,m,m2,m3,tth,ttval,ttrh,arnd,aid,icol,vval,avar0;

var noDispList={} ;

if (showbutton==1) {
  var m2=viewAllVarsButtons() ;
  if (m2==0) return 0;
}

var gpp='<div style="font-size:90%; background-color:#bdbdbd ; padding:3px ;color:blue; text-decoration: underline" ';
gpp=gpp+' onClick="showSelectVars(\''+awhere+'\',\''+showbutton+'\')">Select variables to display ';  
gpp=gpp+'    <span class="jumpTop" style="float:right;margin-right:20px;margin-top:-10px" onClick="dosurveyFocus(\'_topBar_\');" title="jump to top of screen">Top...</span> \n';
gpp=gpp+'</div> \n ';

ii.innerHTML=gpp;

vv='<div id="selectVars_workdata" style="display:none" border="1px gray dashed" background-color:#acabae">Table for selecting variables</div> '
ii.innerHTML=ii.innerHTML+vv+ '\n';


ii.innerHTML=ii.innerHTML+'<h3>Most recently read values (stored in workData) </h3>'; 
ii.innerHTML=ii.innerHTML+'<em>souce= </em><tt>'+workData['source']+'</tt><br/>';
if (typeof(workData['vars'])=='undefined') {
  ii.innerHTML=ii.innerHTML+'<b>No working data available </b><em>Use ReadFile (or download/save, or a task) to create workData </em> <p/> ';
  return 1 ;
}

varlist=workData['vars'];
var noDispTot=0;
for (jj in workData_viewList) {
 if (workData_viewList[jj]==0) noDispTot++ ;
}
if (noDispTot>0) {
   ii.innerHTML=ii.innerHTML+'<em>of '+varlist.length+' variables, '+noDispTot+' will not be displayed</em><br/>';
}

ttable=document.createElement('table');
ttable.setAttribute('cellpadding',"5");
ttable.setAttribute('cellspacing',"5");  
if (typeof(tableFontSize)=='undefined') tableFontSize=100 ;
ttable.style.fontSize=tableFontSize+'%';
ttable.setAttribute('border',1);
tbody=document.createElement('tbody');

ttrh=document.createElement('tr');
ttrh.style.backgroundColor='cyan';

tth=document.createElement('th');
ttval=document.createTextNode('Round');
tth.appendChild(ttval);
ttrh.appendChild(tth); 

tth=document.createElement('th');
ttval=document.createTextNode('ID');
tth.appendChild(ttval);
ttrh.appendChild(tth); 




icol=0;
if (!viewRefNoTH) {                           // do not supress column names row
for (var m=0;m<varlist.length;m++) {                    // create column headers for variables (perhaps spread over several table rows)

  avar0=varlist[m];
  if (typeof(workData_viewList[avar0])!='undefined' && workData_viewList[avar0]=='0') {  
        noDispList[avar0]=1 ;
        continue ;
   }
  if (icol==viewRefColsPerRow) {                         // enough in this table row -- create new new row (with empty cells for Round and ID leading columns)
    tbody.appendChild(ttrh);

    ttrh=document.createElement('tr');
    ttrh.style.backgroundColor='cyan';

    tth=document.createElement('th');
    ttval=document.createTextNode(' ');
    tth.appendChild(ttval);
    ttrh.appendChild(tth); 

    tth=document.createElement('th');
    ttval=document.createTextNode(' ');
    tth.appendChild(ttval);
    ttrh.appendChild(tth); 

    icol=0 ;
 }
  icol++ ;
  avar=splitString(varlist[m],10,' ');
  tth=document.createElement('th');
  ttval=document.createTextNode(avar);
  tth.appendChild(ttval);
  ttrh.appendChild(tth); 
}     
tbody.appendChild(ttrh);                // append headers (perhaos in several rows) to table
}                               // viewrefnoth

var roundlist=Concat_Arrays(workData['rounds']);            // ALWAYS show values for  "all rounds, all ids", even if no such <DATA> record was specified
var idlist=Concat_Arrays(workData['ids']);
for (var m=0;m<roundlist.length;m++) {                                  // write data rows (round, id, var1 .... 
   arnd=strip(roundlist[m]);
   if (typeof(workData[arnd])=='undefined') continue ;
   for (var m2=0;m2<idlist.length;m2++) {
       aid=strip(idlist[m2]);
       if (typeof(workData[arnd][aid] )=='undefined') continue ;
       ttr=document.createElement('tr'); 

       ttd=document.createElement('td');                // col 1: round 
       ttval=document.createTextNode(arnd);
       ttd.appendChild(ttval);
       ttd.style.backgroundColor='cyan';
       if (arnd=='*') {ttd.style.color='green'; ttd.style.weight='500' ;ttd.style.fontSize='120%' };
       ttr.appendChild(ttd);

       ttd=document.createElement('td');                // col 2: id    
       ttval=document.createTextNode(aid);
       ttd.appendChild(ttval); 
       if (aid=='*')  {ttd.style.color='red';  ttd.style.weight='500' ;ttd.style.fontSize='120%' }; 
       ttd.style.backgroundColor='cyan';
       ttr.appendChild(ttd);

       icol=0;
       for (var m3=0;m3<varlist.length;m3++) {                            // the variables

        avar0=varlist[m3];
        if (typeof(noDispList[avar0])!='undefined' && noDispList[avar0]==1) continue ;

         if (icol==viewRefColsPerRow) {
            tbody.appendChild(ttr);
            ttr=document.createElement('tr'); 

            ttd=document.createElement('td');                // col 1: round 
            ttval=document.createTextNode(' ');
            ttd.appendChild(ttval);
            ttd.style.backgroundColor='cyan';
            ttr.appendChild(ttd);
            ttd=document.createElement('td');                // col 2: id 
            ttval=document.createTextNode(' ');
            ttd.appendChild(ttval);
            ttd.style.backgroundColor='cyan';
            ttr.appendChild(ttd);
            icol=0;
          }
          icol++ ;
          ttd=document.createElement('td');   
          avar=varlist[m3];
          if (typeof(workData[arnd][aid][avar] )=='undefined') {             // no such var for this record
              ttd.style.backgroundColor='#fafaea';     
              ttval=document.createTextNode(' ');
              ttd.appendChild(ttval);
             vval='undefined';
           } else {
              vval=workData[arnd][aid][avar]
              ttval=document.createTextNode(vval);
              ttd.appendChild(ttval);  
              ttd.style.backgroundColor='#dadada';  
              ttd.style.margin='4px';
              ttd.style.padding='4px';
           }
          atitle='Round='+arnd+' user='+aid.substr(0,5)+'.. '+avar+' = '+vval ;
          ttd.setAttribute('title',atitle);
          ttd.setAttribute('onClick','writeDataMessage(this.title)');
          ttr.appendChild(ttd);                 // add cell to this row


        }                                       // varlist 
        tbody.appendChild(ttr);                 // add row to table body
   }                                    // this id 
}                                       // this round 

ttable.appendChild(tbody);

ii.appendChild(ttable);

}

//      ====================/
// show a table to use for selecting which varaibles to display
function showSelectVars(gg1,gg2) {
var i1,i2,i3;
var ttable,ttr,ttd,tth ;
var mm,vlist,avar;

i1=document.getElementById("selectVars_workdata");  

if (i1.style.display=='block') {                        // just hide
  i1.style.display='none';
  return 1 ;
} else {
  i1.style.display='block';
}
varlist=workData['vars'];
ttable='<div style="border:2px black solid;margin-left:10px;margin-right:10px"> ';
ttable=ttable+'<em>Click on a variable to enable/disable display.  </em> ';
ttable=ttable+'<span style="font-size:90%;  padding:3px ;color:blue; text-decoration: underline" onClick="showSelectVarsEnable(1)">';
ttable=ttable+'Enable all ' + ' </span> ';
ttable=ttable+'<span style="color:yellow"> | </span> ' ;
ttable=ttable+'<span style="font-size:90%;  padding:3px ;color:blue; text-decoration: underline" onClick="showSelectVarsEnable(0)">';
ttable=ttable+'Disable all </span> <br /> ';


ttable=ttable+'\n  <table border="1"><tr> ';
itr=0;
for (var i2=0;i2<varlist.length;i2++) {
    itr++ ;
    if (itr>8) {
        ttable=ttable+"</tr><tr> \n ";
        itr=1 ;
   }
   ttable=ttable+'<td> <span name="workDataVars" useName="'+varlist[i2]+'" svyEnabled="1" onClick="showSelectVars2(this)">'+splitString(varlist[i2],10,' ')+'</span></td>';  
}
ttable=ttable+"</tr></table>" ;
ttable=ttable+'<div style="font-size:90%;  padding:3px ;color:blue; text-decoration: underline" ';
ttable=ttable+' onClick="viewWorkData(\''+gg1+'\',\''+gg2+'\',1)">redisplay...</div>';

i1.innerHTML=ttable ;

// now, deselect all variables in workData_viewList
vlist=document.getElementsByName('workDataVars');
for (var mm=0;mm<vlist.length;mm++) {
  avar=getAttributes(vlist[mm],'useName','');
  if (typeof(workData_viewList[avar])!='undefined' && workData_viewList[avar]=='0' )  showSelectVars2(vlist[mm],0) ;  
}

}

//      ====================
// a variable has been enabled/disabled. Toggle its display if ido not specified.
// else, enable (ido=1) or disable (ido=0)
function showSelectVars2(aa,ido) {
var isel,icolor,avar;
if (arguments.length>1) {
   aa.setAttribute('svyEnabled',ido);
   avar=getAttributes(aa,'useName','');
   workData_viewList[avar]=ido ;
   icolor='black';
   if (ido==0) icolor='gray';
   aa.style.color=icolor ;
   return 1 ;
}

isel=getAttributes(aa,'svyEnabled',0) ;
var avar=getAttributes(aa,'useName','');

if (isel==0) {           // was disabled, so enable 
   aa.setAttribute('svyEnabled',1);
   aa.style.color='black';
   workData_viewList[avar]=1 ;
} else {
   aa.setAttribute('svyEnabled',0);
   aa.style.color='gray';
   workData_viewList[avar]=0 ;

}
return 1;
}

//      ====================
// enable/disable all vars
function showSelectVarsEnable(ido) {
var vlist ;
vlist=document.getElementsByName('workDataVars');
for (var m=0;m<vlist.length;m++) {
   showSelectVars2(vlist[m],ido) ;
}
return 1; 

}

//      ==================== 
// view results data
function viewResultsData(awhere,showbutton) {  
if (arguments.length<1 || awhere=='') awhere=actionDiv ;
if (arguments.length<2) showbutton=0;


ii=document.getElementById(awhere);
ii.innerHTML='';
var ii,ttable,tbody,ttr,ttd,aval,avar,m,m2,m3,tth,ttval,ttrh,arnd,aid,icol,vval;
if (showbutton==1) {
  var m2=viewAllVarsButtons() ;
  if (m2==0) return 0;
}

ii.innerHTML=ii.innerHTML+'<h3>Results (stored in resultsData): for round ' +resultsData['$ROUND'] +' </h3>'; 
if ( typeof(resultsData)=='undefined' ) {
  ii.innerHTML=ii.innerHTML+'<b>No results data available </b><em>Use ReadFile, or a task, to read workData and then create resultsData </em> <p/> ';
  return 1 ;
}
ii.innerHTML=ii.innerHTML+'<h4>Client-specific results </h4>';  

//var tdiv=document.createElement('table');
ttable=document.createElement('table');
ttable.setAttribute('cellpadding',"5");
ttable.setAttribute('cellspacing',"5");  
if (typeof(tableFontSize)=='undefined') tableFontSize=100 ;
ttable.style.fontSize=tableFontSize+'%';
ttable.setAttribute('border',1);
tbody=document.createElement('tbody');

ttrh=document.createElement('tr');
ttrh.style.backgroundColor='cyan';

tth=document.createElement('th');
ttval=document.createTextNode('ID');
tth.appendChild(ttval);
ttrh.appendChild(tth); 

varlist=resultsData['$VARS'];
icol=0;

if (!viewRefNoTH) {                           // do not supress column names row
for (var m=0;m<varlist.length;m++) {                    // create column headers for variables (perhaps spread over several table rows)
 if (icol==viewRefColsPerRow) {                         // enough in this table row -- create new new row (with empty cells for Round and ID leading columns)
    tbody.appendChild(ttrh);

    ttrh=document.createElement('tr');
    ttrh.style.backgroundColor='cyan';

    tth=document.createElement('th');
    ttval=document.createTextNode(' ');
    tth.appendChild(ttval);
    ttrh.appendChild(tth); 

    icol=0 ;
 }
  icol++ ;
  avar=varlist[m];
  tth=document.createElement('th');
  ttval=document.createTextNode(avar);
  tth.appendChild(ttval);
  ttrh.appendChild(tth); 
}     
tbody.appendChild(ttrh);                // append headers (perhaos in several rows) to table
}

for (var m2=0;m2<resultsData['$IDS'].length;m2++) {
       aid=resultsData['$IDS'][m2];
       if (aid=='*') continue ;                          // * id non-client specific; hence they are general variables
       if (typeof(resultsData[aid] )=='undefined') continue ;
       ttr=document.createElement('tr'); 


       ttd=document.createElement('td');                // col 1: id    
       ttval=document.createTextNode(aid);
       ttd.appendChild(ttval); 
       if (aid=='*')  {ttd.style.color='red';  ttd.style.weight='500' ;ttd.style.fontSize='120%' }; 
       ttd.style.backgroundColor='cyan';
       ttr.appendChild(ttd);

       icol=0;
       for (var m3=0;m3<varlist.length;m3++) {                            // the variables

         if (icol==viewRefColsPerRow) {
            tbody.appendChild(ttr);
            ttr=document.createElement('tr'); 

            ttd=document.createElement('td');                // col 1: id 
            ttval=document.createTextNode(' ');
            ttd.appendChild(ttval);
            ttd.style.backgroundColor='cyan';
            ttr.appendChild(ttd);
            icol=0;
          }
          icol++ ;
          ttd=document.createElement('td');   
          avar=varlist[m3];
          if (typeof(resultsData[aid][avar] )=='undefined') {             // no such var for this record
              ttd.style.backgroundColor='#fafaea';     
              ttval=document.createTextNode(' ');
              ttd.appendChild(ttval);
             vval='undefined';
           } else {
              vval=resultsData[aid][avar]
              ttval=document.createTextNode(vval);
              ttd.appendChild(ttval);  
              ttd.style.backgroundColor='#dadada';  
              ttd.style.margin='4px';
              ttd.style.padding='4px';
           }
          atitle='ID='+aid+', Var='+avar+', value='+vval ;
          ttd.setAttribute('title',atitle);
          ttd.setAttribute('onClick','writeDataMessage(this.title)');
          ttr.appendChild(ttd);                 // add cell to this row


        }                                       // varlist 
        tbody.appendChild(ttr);                 // add row to table body
   }                                    // this id 

ttable.appendChild(tbody);

ii.appendChild(ttable);

// ========= GENERAL  ( * id )

ii.innerHTML=ii.innerHTML+'<h4>General results </h4>'; 

ttable=document.createElement('table');
ttable.setAttribute('cellpadding',"5");
ttable.setAttribute('cellspacing',"5");  
if (typeof(tableFontSize)=='undefined') tableFontSize=100 ;
ttable.style.fontSize=tableFontSize+'%';
ttable.setAttribute('border',1);
tbody=document.createElement('tbody');

ttrh=document.createElement('tr');
ttrh.style.backgroundColor='cyan';

tth=document.createElement('th');
ttval=document.createTextNode(' ');
tth.appendChild(ttval);
ttrh.appendChild(tth); 

varlist=resultsData['$VARS'];

icol=0;
varlist=resultsData['*']['$VARS'];
 
if (!viewRefNoTH) {                   // column name row not suppressed?
for (var m=0;m<varlist.length;m++) {                    // create column headers for variables (perhaps spread over several table rows)
 if (icol==viewRefColsPerRow) {                         // enough in this table row -- create new new row (with empty cells for Round and ID leading columns)
    tbody.appendChild(ttrh);

    ttrh=document.createElement('tr');
    ttrh.style.backgroundColor='cyan';

    tth=document.createElement('th');
    ttval=document.createTextNode(' ');
    tth.appendChild(ttval);
    ttrh.appendChild(tth); 

    icol=0 ;
 }
  icol++ ;
  avar=varlist[m];
  tth=document.createElement('th');
  ttval=document.createTextNode(avar);
  tth.appendChild(ttval);
  ttrh.appendChild(tth); 
}     
tbody.appendChild(ttrh);                // append headers (perhaos in several rows) to table
}

ttr=document.createElement('tr');
   ttd=document.createElement('td');                // col 1: id 
   ttval=document.createTextNode(' ');
   ttd.appendChild(ttval);
   ttd.style.backgroundColor='cyan';
   ttr.appendChild(ttd);
 icol=0;

aid='*';
for (var m3=0;m3<varlist.length;m3++) {                            // the values  for the * row
   if (icol==viewRefColsPerRow) {
            tbody.appendChild(ttr);
            ttr=document.createElement('tr'); 

            ttd=document.createElement('td');                // col 1: id 
            ttval=document.createTextNode(' ');
            ttd.appendChild(ttval);
            ttd.style.backgroundColor='cyan';
            ttr.appendChild(ttd);
            icol=0;
   }
   icol++ ;
   ttd=document.createElement('td');   
   avar=varlist[m3];
   if (typeof(resultsData[aid][avar] )=='undefined') {             // no such var for this record
              ttd.style.backgroundColor='#fafaea';     
              ttval=document.createTextNode(' ');
              ttd.appendChild(ttval);
               vval='undefined';
   } else {
              vval=resultsData[aid][avar]
              ttval=document.createTextNode(vval);
              ttd.appendChild(ttval);  
              ttd.style.backgroundColor='#dadada';  
              ttd.style.margin='4px';
              ttd.style.padding='4px';
    }
     atitle='var='+avar+', value='+vval ;
     ttd.setAttribute('title',atitle);
     ttd.setAttribute('onClick','writeDataMessage(this.title)');
     ttr.appendChild(ttd);                 // add cell to this row
  }                                       // varlist 
  if (ttr) tbody.appendChild(ttr);                 // * id

ttable.appendChild(tbody);

ii.appendChild(ttable);


}  

// workData
//      ====================
// view current generalVars
// note: table with changeable values is in a DIV with an ID of "viewGeneralVarsDiv"
function viewGeneralVars(towrite,doappend,descObj,ifull) {
var amm,tmp,vval,mm,mm0,m,m0,tmp  ;
var atable,atbody,atr,atd,i,ivar,atext,atext2,ath,ainput,aval,avar ;
var aspan,a2;
var dvar,ivarc,dvarfull;

var vlist=[];
var ccolors=['#ababab','#bababa'];
var icolor=0 ;
if (arguments.length<4) ifull=0;

var amm0;

amm0=document.getElementById(towrite);
if (!amm0) {
        alert(' viewGeneralVars error: no such id= '+towrite);
        return 1 ;
}
if (doappend==0) amm0.innerHTML='';

vlist=readGeneralVar() ;

amm=document.createElement('div');
amm.setAttribute('id','viewGeneralVarsDiv');

atable=document.createElement('hr');
amm.appendChild(atable);
atable=document.createElement('h3');
    atext=document.createTextNode(vlist.length+' general variables: ');
    atable.appendChild(atext);
amm.appendChild(atable);

atable=document.createElement('table');
atbody=document.createElement('tbody');
atr=document.createElement('tr');
  atd=document.createElement('th');
    atext=document.createTextNode('General Variable ');
    atd.appendChild(atext);
    if (typeof(descObj)!='undefined')  {  
        a2=document.createElement('div');
        a2.setAttribute('onClick','doViewDescDiv()');
        a2.style.textDecoration='underline' ; a2.style.color='blue';
           atext=document.createTextNode('(...click variable names ')
         a2.appendChild(atext)
           atext=document.createElement('br');
         a2.appendChild(atext)
           atext=document.createTextNode('to view descriptions)');
         a2.appendChild(atext) ;
        atd.appendChild(a2);
    }

  atr.appendChild(atd);

  atd=document.createElement('th');
    atext=document.createTextNode('Value');
    atd.appendChild(atext);

    aspan=document.createElement('span');
    aspan.style.fontSize='90%';
    aspan.style.marginLeft='10px';

    ainput=document.createElement('input');
    ainput.setAttribute('id','noShowValues');
    ainput.setAttribute('type','checkbox');
    ainput.setAttribute('checked','0');
    ainput.setAttribute('defaultChecked',false);       
    ainput.defaultChecked=false ;
    ainput.setAttribute('achecked','0');
    ainput.setAttribute('onClick','switchCheck(this)');
    aspan.appendChild(ainput);

    ainput=document.createElement('span');
    atext=document.createTextNode('Show current variable values in popup box');
    ainput.appendChild(atext);
    aspan.appendChild(ainput);
   atd.appendChild(aspan);
    

  atr.appendChild(atd);
  atr.style.backgroundColor="cyan";

  atbody.appendChild(atr);  

 doViewDescDivStat=0;                   // global var

for (var m=0;m<vlist.length;m++) {
  ivar=vlist[m];
  vval=readGeneralVar(ivar);

  atr=document.createElement('tr');

  ath=document.createElement('th');                     // first cell is varname
  ainput=document.createElement('span');
     atext=document.createTextNode(ivar);
     ainput.appendChild(atext);
     if (arguments.length<3) {
         ainput.setAttribute('onClick','gotoVarView("submitGeneralChanges")');
    } else {
          dvarfull=''; dvar=''; ivarc=strip(ivar).toUpperCase();
          if (typeof(descObj)!='undefined' && typeof(descObj[ivarc])!='undefined')  {
                 if (ifull==1)      dvarFull=descObj['$full_'+ivarc] ;
                 dvar=descObj[ivarc] ;
                 dvar=dvar.replace(/\"|\'/g,'`');
                 dvar=dvar.replace(/\n/g,'<br>');
           }
          tmp=ivar+': '+dvar ;
          ainput.setAttribute('onClick','gotoVarView(\'submitGeneralChanges\',\''+tmp+'\')');
     }
     ath.appendChild(ainput);
  atr.appendChild(ath);                

  atd=document.createElement('td');
    if (typeof(vval)=='object' && typeof(vval.length)!='undefined') {           // this is an array
        for (var mm=0;mm<vval.length;mm++) {
               ainput=document.createElement('input');
               tmpvar=ivar+'['+mm+']';
               ainput.setAttribute('name',tmpvar);
               ainput.setAttribute('value',vval[mm]);
               ainput.setAttribute('changed',0);
               ainput.setAttribute('size',8);
               ainput.setAttribute('changeValue',vval[mm]);
               ainput.setAttribute('svyType','GENERAL');
               ainput.setAttribute('onChange','changeVarView(this)');
               ainput.setAttribute('onClick','showVarView(this,"General")');
               atd.appendChild(ainput);
        }
    } else {                                    // not an array
       ainput=document.createElement('input');
       ainput.setAttribute('name',ivar);
       ainput.setAttribute('value',vval);
       ainput.setAttribute('changed',0);
       ainput.setAttribute('size',10);
       ainput.setAttribute('changeValue',vval);
       ainput.setAttribute('svyType','GENERAL');
       ainput.setAttribute('onChange','changeVarView(this)');
       ainput.setAttribute('onClick','showVarView(this,"General")');
       atd.appendChild(ainput);
   }
  ainput=document.createElement('div');
  ainput.style.display='none';
  ainput.name='DescDiv';
  ainput.setAttribute('name','DescDiv');
  ainput.style.backgroundColor='#ddee33'; 
  atext=':Description:  ';  
  ivarc=strip(ivar).toUpperCase();  
  if (typeof(descObj)!='undefined' && typeof(descObj[ivarc])!='undefined') {
     if (ifull==1 && typeof(descObj['$full_'+ivarc])!='undefined') {
            atext=descObj['$full_'+ivarc] ;
      } else {
           dvar=descObj[ivarc] ;
           atext='Description:  '+dvar ;
      }
//      dvar=dvar.replace(/\"/g,'`');
//      atext='Description:  '+dvar ;
   }
//  aspan=document.createTextNode(atext);
//  ainput.appendChild(aspan);
     ainput.innerHTML=atext;
  atd.appendChild(ainput);

  atr.appendChild(atd);                 // next is value 

  icolor=1-icolor ;
  atr.style.backgroundColor=ccolors[icolor];
  atbody.appendChild(atr);  
}
atable.appendChild(atbody);
amm.appendChild(atable);    

// if descobj, then do NOT allow these changes to be saved locally (for use in scenario file only
if (typeof(descObj)=='undefined')  {
  ainput=document.createElement('input');
  ainput.setAttribute('type','submit');
  ainput.setAttribute('id','submitGeneralChanges');
  ainput.setAttribute('value','Save changes to GENERAL variables');
  ainput.setAttribute('onClick','saveVarView("GENERAL")');
  ainput.style.marginBottom='80px';
  amm.appendChild(ainput);    
}

amm0.appendChild(amm);
return 1 ;

}
// gotovarview

//============ ====
// enable/disable view of DescDiv "dsecription divs" (for scenario)
function doViewDescDiv() {
var vvs,mm,aview ;
if (doViewDescDivStat==0) {
   aview='block';  doViewDescDivStat=1;
} else {
   aview='none';  doViewDescDivStat=0;
}
vvs=document.getElementsByName('DescDiv') ;

for (var mm=0; mm<vvs.length;mm++) {
   vvs[mm].style.display=aview;
}
return 1;

}

//======= 
// turn the show-current-values-in-popup checkbox on and off
function switchCheck(athis) {
  var vv=athis.getAttribute('checked');
  var vv2=athis.getAttribute('achecked');
  athis.setAttribute('achecked',1-vv2);
  athis.setAttribute('checked',!vv);
  return 1;
}
//====== jump to submit button
function gotoVarView(awhere,avar) {
 var ii,jj;
 if (arguments.length>1) {
      writeDataMessage(avar);
  } else {
    ii=document.getElementById(awhere);
    ii.focus() ;
 }
return 1 ;
}

//========== 
// show name and value of a general or control variable
function showVarView(athis,atype) {
var aline,avar,aval;
var iid ;

iid=document.getElementById('noShowValues');

if (iid.getAttribute('achecked')==0) return 0;

vval=athis.getAttribute('changeValue');
avar=athis.getAttribute('name');
aline=atype+' variable: ' +avar+'= '+ vval;
writeDataMessage(aline);
return 1;

}

//========== 
// on change event handler for input fields for control and general variables
function changeVarView(athis) {
var z=athis.getAttribute('changed');
var vv=strip(athis.value) ;
athis.setAttribute('changeValue',vv);
athis.setAttribute('changed',(1*z)+1);
writeDataMessage('',0.1);
return 1 ;
}

//==========
// save all changed input fields (that have a svyType = atype
function saveVarView(atype) {
var alist,m,anode,ageneral,achanged,cval,cname,ndo='';
var goo,darray;

alist=document.getElementsByTagName('input');
for (var m=0;m<alist.length;m++) {
  anode=alist[m];
  ageneral=getAttributes(anode,'svyType','');
  if (ageneral!=atype) continue ;
  achanged=getAttributes(anode,'changed',0);
  if (achanged==0) continue ;
  cval=getAttributes(anode,'changeValue','');
  cname=getAttributes(anode,'name','');

  if (atype=='CONTROL') {
        controlVars[cname]=cval ;
  } 
  if (atype=='GENERAL') {
        if (cname.indexOf('[')>0)  {                    // an element of an array
           goo=parseUsing(cname,'[',']',1);    
           darray=readGeneralVar(goo[0]);
           darray[goo[1]]=cval;
           saveGeneralVar(goo[0],darray,1);
           submitFormList=submitFormList.concat(goo[0]);
        } else {                                        // not an array
            saveGeneralVar(cname,cval,0)
            submitFormList=submitFormList.concat(cname);
//alert('ss2s '+submitFormList);
        }


  } 

  ndo=ndo+cname+' ';

}
if (atype=='GENERAL') {
   writeDataMessage(getWord(ndo).length+' '+atype+' variables changed: <br/> ' + ndo+'<p/><b>Reminder</b>: the internal databases have been changed. To inform clients of these changes, you can use the save session_file or save round_file actions' );
} else {
   writeDataMessage(getWord(ndo).length+' '+atype+' variables changed: <br/> ' + ndo);
}
return 1 ;

}

//      ==================== 
// view current servarVars
function viewServerVars(towrite,doappend) {
var amm,tmp ;
var atable,atbody,atr,atd,i,ivar,atext,atext2 ;
var vlist=[];
var ccolors=['#ababab','#bababa'];
var icolor=0 ;

amm=document.getElementById(towrite);
if (!amm) {
        alert(' viewServerVars error: no such id= '+towrite);
        return 1 ;
}
if (doappend==0) amm.innerHTML='';

for (var ivar in serverVars)  vlist=vlist.concat(ivar);

atable=document.createElement('hr');
amm.appendChild(atable);

atable=document.createElement('div');
atable.style.border='1px blue groove';
atable.style.backgroundColor='#dfdfdf';
atable.style.display='none';
atable.setAttribute('id','viewAllServerVars');
amm.appendChild(atable);

atable=document.createElement('h3');
    atext=document.createTextNode(vlist.length+'  server variables (currently read): ');
    atable.appendChild(atext);
amm.appendChild(atable);

atable=document.createElement('table');
atbody=document.createElement('tbody');
atr=document.createElement('tr');
  atd=document.createElement('th');
    atext=document.createTextNode('Server Variable ');
    atd.appendChild(atext);
  atr.appendChild(atd);
  atd=document.createElement('th');
    atext=document.createTextNode('Value');
    atd.appendChild(atext);
  atr.appendChild(atd);
  atr.style.backgroundColor="cyan";
  atbody.appendChild(atr);  

for (var m=0;m<vlist.length;m++) {
  ivar=vlist[m];
  atr=document.createElement('tr');
  atd=document.createElement('th');
    atext=document.createTextNode(ivar);
    atd.appendChild(atext);
  atr.appendChild(atd);
  atd=document.createElement('td');
    atext=document.createTextNode(serverVars[ivar]);
    atd.appendChild(atext);
  atr.appendChild(atd);
  icolor=1-icolor ;
  atr.style.backgroundColor=ccolors[icolor];
  atbody.appendChild(atr);  
}
atable.appendChild(atbody);
amm.appendChild(atable);    
return 1 ;

} 

// =========================
// view the history data archive on the server  

function viewHistory(retainedVars,ido) {
if (arguments.length<1) retainedVars='*';
if (arguments.length<2) ido=1;

var ccolors=['#a3a3a3','#cacaca'];  

var aname,toGet,m,m2,dlist,goo,foo,nname,avar,aval,nv=0,iv,ii0,icolor=0;
var ir,atable,atr,atd,ii,iid,iround,atitle;
var adat={};
var roundList=[], idList=[],varList=[];

adat['rounds']={};
adat['ids']={};
adat['vars']={};

if (ido==1) {
   toGet=getSaveDataURL+'session='+sessName+'&type=HISTORY' ;;
   writeDoStatus('About to request history data ... ');
   Get_xmlFile(toGet,'saveDataNode','viewHistory.. retrieving history archive','viewHistory','*',2);
  return 1 ;
}

// saveDataNode contains xml file
dlist=E2_getNodes(saveDataNode,'data');

for (var m=0;m<dlist.length;m++) {                      // all <DATA records
   goo=E2_getNodes(dlist[m],'ID');
   if (goo.length==0) {
        alert('Error in history archive: no ID in row '+m+' (see popup source) ');
        foo=E2_xmlToString(saveDataNode);
        displayStringInWindow(foo,1,0);
        return 1;
   }        
   aid=E2_getNodeText(goo[0]);

   goo=E2_getNodes(dlist[m],'ROUND');
   if (goo.length==0) {
        alert('Error in history archive: no ROUND in row '+m+' (see popup source) ');
        foo=E2_xmlToString(saveDataNode);
        displayStringInWindow(foo,1,0);
        return 1;
   }   
   around=E2_getNodeText(goo[0]);  

   if (typeof(adat[around])=='undefined')  adat[around]={}
   if (typeof(adat[around][aid])=='undefined') adat[around][aid]={};
   adat['rounds'][around]=1 ;
   adat['ids'][aid]=1 ;

   goo=E2_getNodes(dlist[m],'*');
   for (var m2=0;m2<goo.length;m2++) {
     nname=strip(goo[m2].nodeName.toUpperCase()) ;
     if (nname=='ID' || nname=='ROUND') continue ;
     aval=E2_getNodeText(goo[m2]);
     adat[around][aid][nname]=aval;
     adat['vars'][nname]=1;
   }                                    // <varname> in a datreord
}

for (var m in adat['rounds'] )  roundList=roundList.concat(m) ; roundList=sortNum(roundList);
for (var m in adat['ids'] )  idList=idList.concat(m) ; idList=idList.sort();
for (var m in adat['vars'] )  varList=varList.concat(m) ; 

foo='';
//foo="var ause='history.xml'; if(strip(viewRefColsPerRow)=='') ause=''; saveHistoryDataAsXML(ause)"  ; foo=' onClick="'+foo+'" ' ;
//foo='<span style="border:1px solid gray; background-color:#efefef;text-decoration:underline;color:blue" title="Save history data as history.xml, on this machines hard drive " '  +foo+'>Save as XML </span><p/>';
   
stuff=foo+'<h3>The history archive for this session ('+sessName+')</h3>';
stuff=stuff+'<ul><li><b># records:</b> <tt>'+dlist.length+'</tt></li> \n ';
stuff=stuff+'<li><b># participants:</b> <tt> '+idList.length+'</tt></li> \n ';
stuff=stuff+'<li><b># rounds:</b> <tt>'+roundList.length+'</tt></li> \n ';
stuff=stuff+'</ul>';


for (var ir=0;ir<roundList.length;ir++) {
  iround=roundList[ir];
  atable='<div style="margin-top:15px;border:1px blue solid;background-color:#ddeedd">For round: '+iround+'</div> \n ';
  atable=atable+'<table border="1" ><tbody><tr><th  bgcolor="lime" >ID</th> ' ;   
  ii0=0; 
  if (!viewRefNoTH) {                           // do not supress column names row

  for (var iv=0;iv<varList.length;iv++) {                      // headers
       aname=varList[iv];
       atable=atable+'<th  bgcolor="lime">'+cvtToArrayName(aname)+'</th> ';
       if (ii0==viewRefColsPerRow) {
           atable=atable+'</tr> \n <tr  bgcolor="lime"><th bgcolor="lime">..</th> \n ' ;
           ii0=0;
       }
    ii0++ ;
  }
 }                      // (!viewRefNoTH) {                  

  atable=atable+'</tr>  \n ';

  for (var ii=0;ii<idList.length;ii++) {
     iid=idList[ii];
     if (typeof(adat[iround][iid])=='undefined') continue ;
     icolor=1-icolor ;
     atable=atable+'<tr bgcolor="'+ccolors[icolor]+'"> \n ';  ii0=0;
     atable=atable+'<td bgcolor="cyan">'+iid+'</td> \n ';
     for (var iv=0;iv<varList.length;iv++) {
           aname=varList[iv];
           if (typeof(adat[iround][iid][aname])=='undefined') {
               atitle='Round='+iround+' user='+iid.substr(0,5)+'.. '+cvtToArrayName(aname)+' = Not defined' ;
               atable=atable+'<td > <div title="'+atitle+'" onClick="writeDataMessage(this.title)" style="background-color:#aeafa0;margin:4px;font-style:oblique"> n.a. </div> </td> \n ';
           } else {
               atitle='Round='+iround+' user='+iid.substr(0,5)+'.. '+cvtToArrayName(aname)+' = '+adat[iround][iid][aname] ;
               atable=atable+'<td  title="'+atitle+'" onClick="writeDataMessage(this.title)">'+adat[iround][iid][aname]+'</td> \n ';
           }
          if (ii0==viewRefColsPerRow) {
              atable=atable+'</tr> \n <tr  bgcolor="'+ccolors[icolor]+'"><td bgcolor="#eeeeff" >..</td> \n ' ;
              ii0=0;
          }
         ii0++ ;
     }                        // this variable

     atable=atable+'</tr> \n ';
  }                                     // this id
  atable=atable+'</tbody > </table >\n';                        // end of round
  stuff=stuff+atable+' \n ' ;
}                                                       // next round

writeDoStatus(stuff,'',' NOSAVE RESET',' view history');
return 1;
}

//===========
// toggle a column of a table
function  toggleTableCol(aName) {
var i,idi ;
idi=document.getElementsByName(aName);

if (idi.length>0) {
  for (var i=0;i<idi.length;i++) {
     if (idi[i].style.display=='none') {
        idi[i].style.display='inline';
     } else {
        idi[i].style.display='none';
     }
  }
}
return 0 ;
}


// =========================
// retreive the submitted data database on the serer, and display it. sort by round, and user in round.
// Only display variables listed in retainedVars space delimited list. Or all variables if retainedVars = '*'

function viewSubmittedData(retainedVars,ido,showbutton) {

var avars,dlines,stuff,userIdLookups,userList,userLookupsVars,anid,m;
var iround,nowr,idsInRound,toGet,myName,myContact,myNickName;
var varList,wasSubmitted ;
var atitle,atitle2 ;
var arecord,atable,wasTime,ivar,aval,arow,avar,icolor;
var tarray,avar,rvars,notSubmittedVars ;
var acolName,colName,colClick,colTitle,auserId,UIDindex;
var ccolors=['#a3a3a3','#cacaca'];  

if (arguments.length<1) retainedVars='*';
if (arguments.length<2) ido=1;
if (arguments.length<3) showbutton=0;

if (ido==1) {                   // first call, reterieve xml file containig submitted data
  if (retainedVars=='*') {
    avars=E2_getNodes(sessionNode,'VARS');
    retainedVars=E2_getNodeText(avars[0],1,'');
  }
  toGet=getSubmittedDataURL+'session='+sessName ;
   writeDoStatus('viewSubmittedData: getting submitted data','','INTER');
  Get_xmlFile(toGet,'submittedDataNode','retrieving submitted data','viewSubmittedData',retainedVars,2,showbutton);
  return 1 ;
}

if (ido==2) {                   // second, get list of current participants
    if (retainedVars=='*') {
      avars=E2_getNodes(sessionNode,'VARS');
      retainedVars=E2_getNodeText(avars[0],1,'');
    }
   toGet=getUserListURL+'session='+sessName;
   writeDoStatus('viewSubmittedData: getting participant list','','INTER');
   Get_xmlFile(toGet,'userListNode','retrieving   participants list','viewSubmittedData',retainedVars,3,showbutton);
   return 1 ;
}

// got submittedData and userList
userList=toArray_userListNode(userListNode);

// CREATE a lookup table (returns array index given a user id)
userIdLookups={} ;
for (var m=1;m<userList.length;m++){
 anid=userList[m][0];
 userIdLookups[anid]=m;
}

// CREATE a lookup table (user ids vars)
userLookupsVars={} ;
for (var m=1;m<userList[0].length;m++){
 avar=userList[0][m];
 userLookupsVars[avar]=m;
}

// create submittedData database from submitted data xml file returned by server
parseSubmittedData(submittedDataNode,1) ; 


varList=submittedData['$varList'];                              // all variables submitted (in ANY data row)

rvars=new Object() ;
retainedVars=getWord(retainedVars);

for (var m=0;m<retainedVars.length;m++) {
   avar=strip(retainedVars[m].toUpperCase()); rvars[avar]=1 ;
}

// recreate varList, only retaining those in the retainedVars list 
tarray=[];notSubmittedVars=[];wasSubmitted=new Object();
varListSubmitted=varList ;
for (var m=0;m<varList.length;m++) {
   avar=strip(varList[m].toUpperCase());
   if (typeof(rvars[avar])!='undefined') {
         tarray=tarray.concat(avar);
         wasSubmitted[avar]=1;
  }
}

//varList=tarray;               // deprected: retained is just for fun

for (var m=0;m<retainedVars.length;m++){
  avar=strip(retainedVars[m].toUpperCase());
  if (typeof(wasSubmitted[avar])=='undefined' ) notSubmittedVars=notSubmittedVars.concat(avar);
}

stuff='<table border="1" > ' ;
stuff=stuff+'<tbody ><tr> \n';

// viewRefColsPerRow
foo="var ause='submittedData.xml'; if((viewRefColsPerRow)=='') ause=''; saveSubmittedDataAsXML('')"  ; foo=' onClick="'+foo+'" ' ;
foo='<span style="text-decoration:underline;color:blue" title="View submitted data as an XML file in a new window" ' + foo+' >View as XML </span>';


stuff=stuff+'<td>'+foo+'</td> \n ';

foo="viewSubmittedDataVar(this,4)"  ; foo=' onClick="'+foo+'" ' ; 
stuff=stuff+'<td> Display variable: <select> \n ';
for (var m=0;m<varList.length;m++){
  stuff=stuff+'<option '+foo+' value="'+varList[m]+'" />'+varList[m]+'</option> \n ';
}
stuff=stuff+'</select></td>';


var goo=retainedVars.join(' ');
foo="viewSubmittedDataId('"+goo+"',4)"  ; foo=' onClick="'+foo+'" ' ; 
foo='<span style="text-decoration:underline;color:blue" '+foo+'>View by id </span>';
stuff=stuff+'<td>'+foo+'</td> \n ';

stuff=stuff+'<td bgcolor="yellow"><table><tbody><tr><td><a name="aTop"><em>Jump to round .. </em> </a></td><td bgcolor="#fbeafb"><table border="1"><tbody><tr>';
var ifoo=0;
for (var iround=0;iround<submittedData['$roundList'].length;iround++) {
  if (ifoo==15) {
        stuff=stuff+'</tr> \n <tr> ' ;
        ifoo=0;
  }
  ifoo++ ;
  nowr=submittedData['$roundList'][iround];
  stuff=stuff+'<td><a href="#ROUND_'+nowr+'">'+nowr+'</a></td>  ';
}
stuff=stuff+'</tr></tbody></table> </tr></tbody></table> </td></tr></tbody</table>\n ';

if (showbutton==1) {
  var m2=viewAllVarsButtons() ;
  if (m2==0) return 0;
  writeDoStatus(stuff,'','NOSAVE FINAL ');
} else {
  writeDoStatus(stuff,'','NOSAVE RESET FINAL',' view submitted data');
}

stuff='<h3>Summary of submitted data</h3>';
stuff=stuff+'<ul><li><b># records (excluding multiple submissions):</b> <tt>'+submittedData['$records']+'</tt></li> \n ';
stuff=stuff+'<li><b># participants:</b> <tt> '+submittedData['$idList'].length+'</tt></li> \n ';
stuff=stuff+'<li><b># rounds:</b> <tt>'+submittedData['$roundList'].length+'</tt></li> \n ';

foo=' onClick="displayDiv(\'_listOfSubmittedVars\') " ';

stuff=stuff+'<li><span style="text-decoration:underline;color:blue" '+foo+' >' ;
   stuff=stuff+varListSubmitted.length +' variables in submitted data:</span>  ';

stuff=stuff+'<span id="_listOfSubmittedVars" style="display:none;margin:5px 5% 5px 5%;background-color:lime">'+varListSubmitted.join(' ')+'</span></li> ';

stuff=stuff+'</li> \n ';
stuff=stuff+'</ul> \n ';
writeDoStatus(stuff,'',' NOSAVE FINAL ');

var notInThisRound='';
// now create a set of divs for each round. Each div contains a table, 1 row-set for each id
for (var iround=0;iround<submittedData['$roundList'].length;iround++) {

  nowr=submittedData['$roundList'][iround];
  idsInRound=submittedData['$rounds'][nowr];

  stuff='<div  id="ROUND_'+nowr+'" style="background-color:#dcdcde; border: 1px dashed gray"><a href="#aTop">Round # ' +nowr+'</a> &nbsp; # participants='+idsInRound.length+' \n ';

  if (typeof(tableFontSize)=='undefined') tableFontSize=100;
   atable='\n <table style="font-size:'+strip(tableFontSize)+'%"  border="1"><tr  bgcolor="lime"><th bgcolor="lime">Id</th>';

  var ii=0; 

  if (viewRefNoTH==0) {
    for (var ivar=0;ivar<varList.length;ivar++) {
       if (ii==viewRefColsPerRow) {
           atable=atable+'</tr> \n <tr  bgcolor="lime"><th bgcolor="lime">..</th> \n ' ;
           ii=0;
      }
      ii++ ;
      acolName='SUBMIT_TABLE_'+ivar ;
      colName=' name="'+acolName+'" ' ;  
      colTitle='title="Click to toggle display of '+varList[ivar]+'" ';
      colClick= ' onClick="toggleTableCol(\''+acolName+'\')" ';  
      atable=atable+' \n <th '+ colClick+ colTitle +' >&nbsp;&nbsp;<span ' + colName +  '>'+splitString(varList[ivar],10,' ')+'</span></th> ' ;
    }                           // ivar...
  }                             // viewrefnoth

  atable=atable+' </tr> \n';
  icolor=1;
  for (var iuser=0;iuser<idsInRound.length;iuser++) {           //  idsInRound is an array of client IDs of participants in this round 
     auserId=idsInRound[iuser];
     arecord=submittedData[nowr][auserId];
     icolor=1-icolor ;
     atable=atable+'<tr bgcolor="'+ccolors[icolor]+'"> \n';

     wasTime=new Date(1*arecord['$time']);
     wasTime=wasTime.toLocaleDateString()+' '+wasTime.toLocaleTimeString();
     myContact='';myNickName='';myName='';

     if (typeof(userIdLookups[auserId])=='undefined') {
        UIDindex=userIdLookups[auserId]; 
        if (typeof(userList[UIDindex])!='undefined') {
           if (typeof(userLookupVars['CONTACT'])!=='undefined')  myContact=userList[UIDindex][userLookupsVars['CONTACT']] ;
           if (typeof(userLookupVars['NICKNAME'])!=='undefined') myNickName=userList[UIDindex][userLookupsVars['NICKNAME']] ;
           if (typeof(userLookupVars['NAME'])!=='undefined')  myName=userList[UIDindex][userLookupsVars['NAME']] ;
        }                       //      UIDindex in userlist exists
     }                           // userIdLookups exists
     atitle=myName+' submitted at ' +wasTime+', notFinal_ct = ' + arecord['$notFinal']+'_'+arecord['$ct'] ;
        atitle=atitle+', contact: ' +myContact+', nickname='+myNickName ;
     atitle2="'"+atitle+"'";
     arow='\n <td bgcolor="cyan" title="'+atitle+'" onClick="writeDataMessage(this.title)"> <b> '+ myName+ '</b> <div style="font-size:80%">'+auserId+'</div> </td> ' ;

     var ii=0; 

     for (var ivar=0;ivar<varList.length;ivar++) {
       if (ii==viewRefColsPerRow) {
              atable=atable+arow+'</tr> \n';
              arow='<tr bgcolor="'+ccolors[icolor]+'"> <td bgcolor="#eeeeff">..</td> \n ';
              ii=0;
        }
        ii++ ;
        acolName='SUBMIT_TABLE_'+ivar ;
        colName=' name="'+acolName+'" ' ;

        aval=''; avar=varList[ivar];
        if (typeof(arecord[avar])!='undefined') aval=arecord[avar];
        var showval=aval ;   if (showval=='') showval='&nbsp;';
        atitle='Round='+nowr+', user='+myName+': '+avar+' = '+aval ;
        arow=arow+'\n    <td '+' title="'+atitle+'" onClick="writeDataMessage(this.title)"><span '+colName+'>'+showval+'</span></td> ';
     }                                          // ivar (variables)
     atable=atable+arow+'</tr> \n';
  }                                                     // iuser: participants in this round (
  stuff=stuff+atable+'\n </div> \n ';
  writeDoStatus(stuff,'',' NOSAVE FINAL');
}                                                       // iround 
return 1;

}
 // my
//================
// convert workData to a CSV file (returned as a string
// headerType: 0: no header, 1:first line varames (default is 1), 2: 1st line descrip, 2nd line varnames
// missval : a string (i.e.' '', or '.') to use for missing values  (default is '')
// quoteNaN : if 1 or 2, then "" non-numeric values, 1 remove crls and "s , 2 don't remove  (default is 1)
// roundlist: space delimited list of rounds. * (the default) means all rounds.
// noStar : if 1, suppess "*" id and round (that is, only write participant/round specific data
// amessage: optional message for header (if headType=2)

function workDataToCSV(headerType,missval,quoteNaN,roundlist,noStar,amessage) {

if (arguments.length<1) headerType=1;
if (arguments.length<2) missval='';
if (arguments.length<3) quoteNaN=1;
if (arguments.length<4) roundlist='*';
if (arguments.length<5) noStar='*';
if (arguments.length<6) amessage='';

var stuff,tmp,a1,a2,a3,i,n,m;
var nv,aline,thisid,mround,mmid,thisround,nv,aval,nowrite;
var nids,nrounds,nvars,allr;
nids=workData['ids'].length;
nrounds=workData['rounds'].length;
nvars=workData['vars'].length ;

roundlist=strip(roundlist);
if (roundlist=='' || roundlist=='*') allr=1;
roundlist=' '+strip(roundlist)+' ' ;

stuff='';
var dd=new Date() ;
if (headerType==2) stuff='Session '+sessName+' at '+dd+': rounds= '+roundlist+'. '+amessage+'\n';;


aheader='ROUND,ID,';
if (nvars>0) aheader=aheader+workData['vars'][0];
for (i=1; i<nvars;i++) {
  aheader=aheader+','+workData['vars'][i];
}
if (headerType==1 || headerType==2) stuff=stuff+aheader+'\n';

for (var mround=0;mround<nrounds;mround++) {
  nowrite=0;
  thisround=workData['rounds'][mround];
  if (typeof(workData[thisround])=='undefined') continue ;

  if (noStar==1 && strip(thisround)=='*') continue ;
  if (allr==0 && roundlist.indexOf(thisround)<0) continue ;           // not a match round

  for (var mmid=0;mmid<nids;mmid++) {
    aline=thisround+',';
    thisid=workData['ids'][mmid];
    if (typeof(workData[thisround][thisid])=='undefined') continue ;
    if (noStar==1 && strip(thisid)=='*') continue ;
    aline=aline+thisid;
    for (var nv=0;nv<nvars;nv++) {
        vname=workData['vars'][nv];
       if (typeof(workData[thisround][thisid][vname])=='undefined') {
            aval=missval ;
        } else {
           aval=workData[thisround][thisid][vname];  // the value of this variable for this round & client. Note that the [ct] is NOT supported -- so multiple >DATA> nodew s (for a single round/client) will overwrite each other
           if (quoteNaN!==0) {
             if (isNaN(aval)) {
                if (quoteNaN!==2) aval=aval.replace(/\"|\n/g,' ');
                aval='"'+aval+'"';
             }
           }
        }
        aline=aline+','+aval
    }
    if (nowrite==1) continue ;
    aline=aline+'\n';
    stuff=stuff+aline;
  }
}
 
return stuff;  


}


//===================
// save data in saveDataNode as a CSV file
// atype: type of data (GENERAL, etc). Used in message (so not important)
// filename: name to use when saving file
// rlist: list of rounds to save. * mean "all"
// asCSV : space delimited list of options 
// asCSV can contain:  NOHEADER DESC NOQUOTE NODEFAULTS MISS=c 

function saveDataAsCSV(atype,fileName,rlist,asCSV,apopup) {

var amessage,lug,i,headerType,missval;
var quoteNaN,noStar,ig;
asCSV=' '+strip(asCSV.toUpperCase())+' ';

lug= E2_getNodes(saveDataNode,'DATA');

ig=0;
if (asCSV.indexOf('EXPAND=1')>-1) ig=1 ;
if (asCSV.indexOf('EXPAND=2')>-1) ig=2 ;
make_workData(lug,'*',' (saveDataAsCSV) saving to workData: from server archive '+atype,0,ig);

  
// headerType: 0: no header, 1:first line varames (default is 1), 2: 1st line descrip, 2nd line varnames
headerType=1;
if (asCSV.indexOf('NOHEADER')>-1) {
        headerType=0 ;
} else {
  if (asCSV.indexOf('DESC')>-1) headerType=2 ;
}

missval='';
if (asCSV.indexOf('MISS=')>-1) {
    i=asCSV.indexOf('MISS='); 
    missval=asCSV.substr(i+5,1);
}


quoteNaN=1;
if (asCSV.indexOf('NOQUOTE')>-1) quoteNaN=0;

noStar=0;
if (asCSV.indexOf('NODEFAULTS')>-1) noStar=1;
  
amessage='Type of data saved is '+atype;

stuff=workDataToCSV(headerType,missval,quoteNaN,rlist,noStar,amessage) 

if (fileName!=='' && fileName!=='0') {
  writeDoStatus('About to save ['+amessage+'  ] to local CSV file (via the server).  You will be asked to specify a file name ','','');
  saveStringToFile(fileName,stuff)
}
//alert('stuff='+stuff);
if (apopup==1)  displayStringInWindow(stuff,0,2)      ;



return 1;
}



// =========================
// given a saveDataNode that contains an xml file (as retrieved with xmlhttp), 
// create an xml string containing the saveData; and save to local drive (using server hack)
// filename='' means "display in new browser window"
// atype=submited is special: do NOT search for match in roundlist

function saveDataAsXML(atype,fileName,roundlist,amessage,apopup) {
var allr=0,m,sdata,stuff,rr,around,foo;
var aform,hiddenField,ww;  
var around,aid,atime,vvars,vvals,lfoo,mv,vvar1,vval1 ;

if (arguments.length<2) fileName='';
if (arguments.length<3) roundlist='*';
if (arguments.length<4 || amessage=='0') amessage=atype+' data ';

roundlist=strip(roundlist);
if (roundlist=='' || roundlist=='*') allr=1;
roundlist=' '+strip(roundlist)+' ' ;
sdata='data_'+atype ;
var dd=new Date() ;
stuff='<?xml version="1.0" encoding="UTF-8" ?> \n  ';
stuff=stuff+'<data_'+atype+'> \n';
stuff=stuff+'   <!-- time: ' + dd + '   --> \n ';
stuff=stuff+'   <!-- session: ' + sessName + '  --> \n ';
stuff=stuff+'   <!-- rounds: ' + roundlist+ '  --> \n \n ';

dnodes=E2_getNodes(saveDataNode,'data');
for (var m=0;m<dnodes.length;m++) {
  if (atype!=='SUBMITTED') {                    // if submitted, don't check for round
     rr=E2_getNodes(dnodes[m],'round');
     if (rr.length==0) continue ;                  // should never happen
     around=E2_getNodeText(rr[0],1,'');
     around=' '+around+' ';
     if (allr==0 && roundlist.indexOf(around)<0) continue ;           // not a match round
     foo=E2_xmlToString(dnodes[m]);
  } else { 
     around=getAttributes(dnodes[m],'round','');
     aid=getAttributes(dnodes[m],'id','');
     atime=getAttributes(dnodes[m],'time','');
     foo='<data> <round>'+around+'</round> ';
        foo=foo+'  <id>'+aid+'</id> ';
        foo=foo+'  <time>'+atime+'</time> ';

     rr=E2_getNodes(dnodes[m],'vars');
     vvars=getWord(E2_getNodeText(rr[0],1,''));

     rr=E2_getNodes(dnodes[m],'vals');
     vvals=E2_getNodeText(rr[0],1,'').split(',');

     lfoo='';
     for (var mv=0;mv<vvars.length;mv++) {
        vvar1=cvtFromArrayName(vvars[mv]); vval1=unescape(vvals[mv]); vval1=vval1.replace(/\&/gi,'&amp;');
        lfoo=lfoo+'  <'+vvar1+'>'+vval1+'</'+vvar1+'> '
     }
     foo=foo+lfoo+'</data> \n'   ;
  }                     // submitted type
  stuff=stuff+foo + '\n ' ;
}
stuff=stuff+' \n </data_'+atype+'> \n';

// display?
if (fileName=='') {                             // no filename -- display in window
  writeDoStatus('Writing '+stuff.length+ ' bytes of ' +atype+' data to a new window ');
  displayStringInWindow(stuff,0,1) ;
  return 1;
}

// else, use call to server as a hack to save file to the local hard drive
if (fileName!=='0' && fileName!=='') { 
 writeDoStatus('About to save ['+amessage+'  ] to local file (via the server).  You will be asked to specify a file name ','','');
 saveStringToFile(fileName,stuff)
}

if (apopup==1)  displayStringInWindow(stuff,0,1)      ;

 return 1;
}


// ===========================
// function to use POST form hack to save a string to a file on the local harc drive
function saveStringToFile(fileName,stuff) {
var aform,hiddenField;
if (stuff.length>50000) {
  aform=confirm('Warning:  a convoluted method is used to save to a local file.\n  For the '+stuff.length+' bytes about to be saved, this method may fail, resulting in a crash of the control panel. \n Are you sure? \n (if yes, you might want to use takeSnapshot first)');
  if (!aform) {
     writeDoStatus(' You cancelled creation of '+fileName);
     return 0 
  }
}

var aform= document.createElement("form");
    aform.setAttribute("method",'POST');
    aform.setAttribute("action",postFileURL );

      var hiddenField = document.createElement("input");
      hiddenField.setAttribute("type", "hidden");
      hiddenField.setAttribute("name", echoFileName);
      hiddenField.setAttribute("value", echoFileValue);
      aform.appendChild(hiddenField);

    var hiddenField = document.createElement("input");
    hiddenField.setAttribute("type", "hidden");
    hiddenField.setAttribute("name",'filename');
    hiddenField.setAttribute("value", fileName);
    aform.appendChild(hiddenField);

    var hiddenField = document.createElement("input");
    hiddenField.setAttribute("type", "hidden");
    hiddenField.setAttribute("name", "contents");
    hiddenField.setAttribute("value",stuff);
    aform.appendChild(hiddenField);
    document.body.appendChild(aform);
    aform.submit() ;
    writeDoStatus(' (contacting server with a '+stuff.length+' bytes upload)');
   return 1  ;                  // note that on mozilla, this leave main window content un changed.

}




// =========================
// create an xml string containing the submittedData
// fileName = filename to save as. If '', then display in new window (no call to server)
//              otherwise, a hacky method of saving file is used -- post request to server with contents, that is sent back in a way that browser ask to save file to disk
// roundlist: if not specified, or *, or 'current', save all rounds. Else, space delmited list of rounds to save
//             items in roundlist can be numbers, or general or control variables (x.g or x.c)
// idist: if not specified, or *, save all ids (in all rounds). Else, space delmited list of numeric ids (typically, as generated when client joined a session) to save
// varlist: if not specified, or *, save all variables (for all ids in all rounds). Else, space delmited list of variables to save

function saveSubmittedDataAsXML(fileName,roundlist,idlist,varlist,doAllr)  {
var ir,iround,id,iid,iv,stuff,arow;
if (arguments.length<1) fileName='';
if (arguments.length<2) roundlist='*';
if (arguments.length<3) idlist='*';
if (arguments.length<4) varlist='*';
if (arguments.length<5) doAllr=0;

if (strip(idlist)=='') idlist='*';
if (strip(varlist)=='') varlist='*';
varlist=' '+strip(varlist)+' ';
idlist=' '+strip(idlist)+' ';

if (roundlist=='current') roundlist='*'

if (strip(roundlist)!='*') {
   var tt=getWord(roundlist);
   for (var itt=0;itt<tt.length;itt++) tt[itt]=getVarInfo(tt[itt],'value');
   roundlist=tt.join(' ');
   roundlist=' '+strip(roundlist)+' ' ;
} else {
   roundlist='*' ;
}

var dd=new Date() ;
stuff='<?xml version="1.0" encoding="UTF-8" ?> \n  <submittedData session="'+sessName+'" time="'+dd.getTime()+'" > \n ';
stuff=stuff+'<!-- time: ' + dd + '   --> \n ';
stuff=stuff+'<!-- rounds: ' + roundlist+ '  --> \n ';
stuff=stuff+'<!-- allRecords: ' + doAllr+ '  --> \n ';
stuff=stuff+'<!-- ids: ' + submittedData['$idList'].join(' ')+ '  --> \n ';
stuff=stuff+'<!-- variables: ' + submittedData['$varList'].join(' ')+ '  --> \n \n ';

for (var ir=0;ir<submittedData['$roundList'].length;ir++) {
   iround=submittedData['$roundList'][ir]; ;
   if (roundlist!='*' && roundlist.indexOf(' '+iround+' ')==-1)  continue ;               // not a selected round
   stuff=stuff+' \n <!-- Round ' +iround+' --> \n \n ';
   for (var iid=0;iid<submittedData['$idList'].length;iid++) {
     id=submittedData['$idList'][iid]; ;

     if (strip(idlist)!='*' && idlist.indexOf(' '+id+' ')==-1)  continue ;               // not a selected id
     if (typeof(submittedData[iround][id])=='undefined') continue ;                     // no vars  
     stuff=stuff+'\n <!-- ID ' +id+' --> \n  ';

// all rounds, or just final?
     if (doAllr==1) {
        arow='';
        for (ict=0;ict<submittedData[iround][id]['$cts'].length;ict++){  
            var jct=submittedData[iround][id]['$cts'][ict];
            arow=arow+'<data><round>'+iround+'</round><id>'+id+'</id> ';
             arow=arow+'  <ct>'+jct+'</ct> \n  ';
             arow=arow+'  <time>'+submittedData[iround][id][jct]['$time']+'</time> ' ;
            arow=arow+'  <notFinal>'+submittedData[iround][id][jct]['$notFinal']+'</notFinal> \n' ;
            for (iv=0;iv<submittedData['$varList'].length;iv++) {
               avar=strip(submittedData['$varList'][iv].toUpperCase()) ;
               if (strip(varlist)!='*' &&  varlist.indexOf(' '+avar+' ')==-1) continue ;           // not a selected var
               var aval='' ; if (typeof(submittedData[iround][id][jct][avar])!='undefined') aval= submittedData[iround][id][jct][avar] ;
               avar=cvtFromArrayName(avar);
               arow=arow+ '     <'+avar+'>'+ aval+'</'+avar+'> \n ' ;
            }                                   // iv (variables )
           arow=arow+'\n ';
        }                                       // ict ([ct]
     } else {                           // do finals only
        arow='<data><round>'+iround+'</round><id>'+id+'</id> \n ';
        arow=arow+'  <time>'+submittedData[iround][id]['$time']+'</time> ' ;
        arow=arow+'  <ct>'+submittedData[iround][id]['$ct']+'</ct>';
        arow=arow+'  <notFinal>'+submittedData[iround][id]['$notFinal']+'</notFinal> \n' ;
        for (iv=0;iv<submittedData['$varList'].length;iv++) {
           avar=strip(submittedData['$varList'][iv].toUpperCase()) ;
           if (strip(varlist)!='*' &&  varlist.indexOf(' '+avar+' ')==-1) continue ;           // not a selected var
           var aval='' ; if (typeof(submittedData[iround][id][avar])!='undefined') aval= submittedData[iround][id][avar] ;
           avar=cvtFromArrayName(avar);
           arow=arow+ '     <'+avar+'>'+ aval+'</'+avar+'> \n ' ;
        }
     }

     arow=arow+'</data> \n \n';
     stuff=stuff+arow;
   }
}

stuff=stuff+'\n \n </submittedData> \n ';

// display?
if (fileName=='') {                             // no filename -- display in window
  displayStringInWindow(stuff,0,1) ;  
  return 1;
}


// else, use call to server as a hack to save file to the local hard drive

 writeDoStatus('About to save submitted data to local file ','','INTER');
 saveStringToFile(fileName,stuff)


}

//=========================================================
// display string a new new HTML window. if cvt=1, convert <  > and & to entity codes
function  displayStringInWindow(stuff,cvt,astext){
  var stuff2;
  if (arguments.length<3) astext=0;
  stuff2=stuff;
  if (cvt==1) {
      stuff2=stuff2.replace(/\&/g,'&amp;');
      stuff2=stuff2.replace(/\>/g,'&gt;');
      stuff2=stuff2.replace(/\</g,'&lt;');
  }
  var ww=window.open('','sdata',"toolbar=0,menubar=0,directories=0,scrollbars=1,resizable=1" );
  if (astext==2) {
       ww.document.open('text/plain');
  } else if (astext==1)  { 
        ww.document.open('text/xml');
  } else {
    ww.document.open('text/html');
  }

/*
  if (astext!=1) {
    ww.document.open('text/html');
  } else {
    if (astext!=2) {
        ww.document.open('text/xml');
    } else {
       ww.document.open('text/plain');
    }
  }
*/

  ww.document.writeln(stuff);
  ww.document.close();
  ww.focus();
  return 1;
}

// =========================
// create an xml string containing th participant list (in userListNode)
// fileName = filename to save as. If '', then display in new window (no call to server)
//              otherwise, a hacky method of saving file is used -- post request to server with contents, that is sent back in a way that browser ask to save file to disk

// parse an XML file containing a user list (as returend by server). Return an array, 1 row per "user". Each row is an array
// The 0'th row is an array of variable names
// row 1 ... are the "user" rows, with variables matching the 0th row
// column 0 is always the id. 
// If a string, it's an error message. 

function saveParticipantsAsXML(uNode,fileName)  {

var ir,iround,id,iid,iv,stuff,arow;
if (arguments.length<2) fileName='';

var ulist=toArray_userListNode(uNode)   ;       
var dd=new Date() ;
stuff='<?xml version="1.0" encoding="UTF-8" ?> \n ';
stuff=stuff+'<participantList session="'+sessName+'" time="'+dd.getTime()+'" > \n \n';
stuff=stuff+'<!-- time: ' + dd + '   --> \n \n ';

for (var ir=1;ir<ulist.length;ir++) {
  arow='<user><id>'+ulist[ir][0]+'</id> \n ';
  for (var iv=1;iv<ulist[ir].length;iv++) {
    arow=arow+'    <'+ulist[0][iv].toUpperCase()+'>'+ulist[ir][iv]+'</'+ulist[0][iv].toUpperCase()+'> \n'
  }
 arow=arow+'</user> \n ';
 stuff=stuff+arow ;
}
stuff=stuff+'\n \n </participantList> \n ';

// display?
if (fileName=='') {                             // no filename -- display in window
  displayStringInWindow(stuff,1,1) ; 
  return 1;
}

// else, use call to server as a hack to save file to the local hard drive
saveStringToFile(fileName,stuff)
return 1  ;                  // note that on mozilla, this leave main window content un changed.

}


// =========================
/* view just one variable in the submitted data (a table with all rounds and all users */
function viewSubmittedDataVar(athis,ido,savebutton) {
if (arguments.length<2) ido=1 ;
if (arguments.length<3) savebutton=1 ;

var avars,avar;
var anid,m,userList,varlist;
var foo,nowr,aval2,m2,foo0;
var showbutton=1 ;

if (ido==1) {                   // first call, reterieve xml file containig submitted data
  toGet=getSubmittedDataURL+'session='+sessName ;
  avar=athis.value ;
  Get_xmlFile(toGet,'submittedDataNode','retrieving submitted data','viewSubmittedDataVar',avar,2,savebutton);
  return 1 ;
}

if (ido==2) {                   // second, get list of current participants
        toGet=getUserListURL+'session='+sessName;
        Get_xmlFile(toGet,'userListNode','retrieving   participants list','viewSubmittedDataVar',athis,3,savebutton);
        return 1 ;
}

// got submittedData and userList
userList=toArray_userListNode(userListNode);
varList=submittedData['$varList'];                              // all variables submitted (in ANY data row)

// create submittedData from dlines
if (ido==4)  {                                  // new var selected 
  avar=athis.value ;
} else {
  parseSubmittedData(submittedDataNode,1) ; 
  avar=strip(athis.toUpperCase());
  if (typeof(submittedData['$vars'][avar])=='undefined') {
    alert('Error in viewSubmittedDataVar: bad variable name = ' +avar);
    return 1 ;
  }
}


foo="viewSubmittedDataVar(this,4)"  ; foo=' onClick="'+foo+'" ' ; 
stuff='<table border="1"><tbody ><tr><td> Display variable: <select> \n ';
for (var m=0;m<varList.length;m++){
  stuff=stuff+'<option '+foo+' value="'+varList[m]+'" />'+varList[m]+'</option> \n ';
}
stuff=stuff+'</select></td> </tr></tbody></table> \n';

stuff=stuff+'<h3>Displaying variable ' + avar +'</h3> \n ';

if (showbutton==1) {
  var m2=viewAllVarsButtons() ;
  if (m2==0) return 0;
  writeDoStatus(stuff,'','NOSAVE FINAL ');
} else {
  writeDoStatus(stuff,'','NOSAVE RESET FINAL ',' view submitted data (var) ');
}


if (typeof(tableFontSize)=='undefined') tableFontSize=100; 
stuff='<table style="font-size:'+tableFontSize+'%" border="1" cellpadding="5" cellspacing="5"><tbody><tr bgcolor="lime"><th bgcolor="yellow">Round</th>';
for (var m=1;m<userList.length;m++) {
  var aid=userList[m][0];
  var aname=userList[m][1] ;
  var acontact=userList[m][2] ;
  var anickname=userList[m][3] ;

  foo='For participant [ '+aname+' ]: the id='+aid+', contact='+acontact ; foo="writeDataMessage('"+foo+"') " ;
  foo='<span onClick="'+foo+'"> '+aname+'</span> ' ;
  stuff=stuff+'\n  <th>'+foo+'</th>';
}
stuff=stuff+'</tr> \n ';
for (var m=0;m<submittedData['$roundList'].length;m++) {
  nowr=submittedData['$roundList'][m];
  foo='<tr><th bgcolor="yellow">'+nowr+'</th>  ';
  for (var m2=1;m2<userList.length;m2++ ){
        aid=userList[m2][0]; aval='';
        if (typeof(submittedData[nowr][aid])!='undefined' )  {
           aval=submittedData[nowr][aid][avar] ;
           foo0='';
       } else {
          foo0='bgcolor="#ededed" ';
        }
       foo=foo+'\n   <td '+foo0+'>'+aval+'</td> ';
  }
  foo=foo+'</tr> \n ' ;
  stuff=stuff+foo;
}

stuff=stuff+'</tr></tbody></table> \n ';
writeDoStatus(stuff,'','NOSAVE FINAL');
return 1 ;
}

// =========================
/* view all server data. sort by user, and round in user. Use variables defined in VARS node of session descriptor file, or in the retainedVars space delimited list */
function viewSubmittedDataId(retainedVars,ido,savebutton) {

var avars,dlines,stuff,userIdLookups,userList,anid,m;
var iround,nowr,idsInRound,toGet,myName,myContact,myNickName;
var varList,wasSubmitted ;
var atitle,atitle2 ;
var arecord,atable,wasTime,ivar,aval,arow,avar,icolor;
var tarray,avar,rvars,notSubmittedVars ;
var id1,nowid,ifoo,ip,roundsInId,irow,anid;
var nameLookup={} ;
var userLookupsVars,showval ;

var ccolors=['#a3a3a3','#cacaca'];

if (arguments.length<1) retainedVars='*';
if (arguments.length<2) ido=1;
if (arguments.length<3) savebutton=1 ;

if (ido==1) {                   // first call, reterieve xml file containig submitted data
  if (retainedVars=='*') {
    avars=E2_getNodes(sessionNode,'VARS');
    retainedVars=E2_getNodeText(avars[0],1,'');
  }
  toGet=getSubmittedDataURL+'session='+sessName ;
  Get_xmlFile(toGet,'submittedDataNode','retrieving submitted data','viewSubmittedDataId',retainedVars,2,savebutton);
  return 1 ;
}

if (ido==2) {                   // second, get list of current participants
        toGet=getUserListURL+'session='+sessName;
        Get_xmlFile(toGet,'userListNode','retrieving   participants list','viewSubmittedDataId',retainedVars,3,savebutton);
        return 1 ;
}

// got submittedData and userList

  userList=toArray_userListNode(userListNode);
// CREATE a lookup table  (given clientId, what record in the userList array is this?
  userIdLookups={} ;
  for (var m=1;m<userList.length;m++){
    anid=userList[m][0];
    userIdLookups[anid]=m;
 }

// CREATE a lookup table (user ids vars)
 userLookupsVars={} ;
 for (var m=1;m<userList[0].length;m++){
   avar=userList[0][m];
   userLookupsVars[avar]=m;
 }

// create submittedData from dlines
parseSubmittedData(submittedDataNode,1) ;
varList=submittedData['$varList'];                              // all variables submitted (in ANY data row)

rvars=new Object() ;
retainedVars=getWord(retainedVars);

for (var m=0;m<retainedVars.length;m++) {
   avar=strip(retainedVars[m].toUpperCase()); rvars[avar]=1 ;
}

// recreate varList, only retaining those in the retainedVars list
tarray=[];notSubmittedVars=[];wasSubmitted=new Object();
varListSubmitted=varList ;
for (var m=0;m<varList.length;m++) {
   avar=strip(varList[m].toUpperCase());
   if (typeof(rvars[avar])!='undefined') {
         tarray=tarray.concat(avar);
         wasSubmitted[avar]=1;
  }
}
//varList=tarray;               // deprecated, retained is just for fun

for (var m=0;m<retainedVars.length;m++){
  avar=strip(retainedVars[m].toUpperCase());
  if (typeof(wasSubmitted[avar])=='undefined' ) notSubmittedVars=notSubmittedVars.concat(avar);
}

foo="viewSubmittedDataVar(this,4)"  ; foo=' onClick="'+foo+'" ' ; 
stuff='<table border="1"><tbody ><tr><td> Display variable: <select> \n ';
for (var m=0;m<varList.length;m++){
  stuff=stuff+'<option '+foo+' value="'+varList[m]+'" />'+varList[m]+'</option> \n ';
}
stuff=stuff+'</select></td>';

var goo=retainedVars.join(' ');
foo="viewSubmittedData('"+goo+"',4,"+savebutton+")"  ; foo=' onClick="'+foo+'" ' ; 
foo='<span style="text-decoration:underline;color:blue" '+foo+'>View by round </span>';
stuff=stuff+'<td>'+foo+'</td> \n ';

stuff=stuff+'<td><table><tbody><tr><td bgcolor="yellow"><a name="aTop"><em>Jump to id .. </em> </a></td><td bgcolor="#fbeafb"><table border="1"><tbody><tr>'; 
var ifoo=0;
for (var id1=0;id1<submittedData['$idList'].length;id1++) {
  if (ifoo==15) {
        stuff=stuff+'</tr> \n <tr> ' ;
        ifoo=0;
  }
  ifoo++ ;
  nowid=submittedData['$idList'][id1]; 
  myName=nowid;                                  // default (if NAME not a particpant variable. This should NEVER be needed
  if (typeof(userIdLookups[nowid])!='undefined') {    //  get info from currentParticipants dataset
       ip=userIdLookups[nowid];
       if (typeof(userList[ip])!='undefined') {
          if (typeof(userLookupVars['NAME'])!=='undefined') myName=userList[ip][userLookupVars['NAME']] ;
       }
  }
  nameLookup[nowid]=myName;

  stuff=stuff+'<td><a href="#ID_'+nowid+'">'+myName+'</a></td>  ';
}
stuff=stuff+'</tr></tbody></table> </tr></tbody></table> \n ';
stuff=stuff+'</td></tr></tbody></table> \n ';

if (savebutton==1) {
  var m2=viewAllVarsButtons() ;
  if (m2==0) return 0;
  writeDoStatus(stuff,'','NOSAVE FINAL');
} else {
  writeDoStatus(stuff,'','NOSAVE RESET FINAL',' view submitted data (id) ');
}

stuff='<h3>Summary of submitted data</h3>';
stuff=stuff+'<ul><li><b># records (excluding multiple submissions):</b> <tt>'+submittedData['$records']+'</tt></li> \n ';
stuff=stuff+'<li><b># participants:</b> <tt> '+submittedData['$idList'].length+'</tt></li> \n ';
stuff=stuff+'<li><b># rounds:</b> <tt>'+submittedData['$roundList'].length+'</tt></li> \n ';

foo='Submitted variables to date: '+varListSubmitted.join(' ') ; foo="writeDataMessage('"+foo+"')"; foo='onClick="'+foo+'"';
stuff=stuff+'<li><span style="text-decoration:underline;color:blue" '+foo+'> '+varListSubmitted.length +' variables in submitted data</span>  ';

//stuff=stuff+'  '+varList.length+ ' submittedVars (see column headers)  are ';
//foo=retainedVars.length+' variables are listed in the session-descriptor file: '+retainedVars.join(' ') ; foo="writeDataMessage('"+foo+"')"; foo='onClick="'+foo+'"';
//stuff=stuff+'<span style="text-decoration:underline;color:blue" '+foo+'> listed in the session-descriptor file;  </span>  ';
//stuff=stuff+' and  '+notSubmittedVars.length+ ' of these ';
//foo=notSubmittedVars.length+' variables listed in the session-descriptor file have not yet been submitted: '+notSubmittedVars.join(' ') ; foo="writeDataMessage('"+foo+"')"; foo='onClick="'+foo+'"';
//stuff=stuff+'<span style="text-decoration:underline;color:blue" '+foo+'> have not been submitted to the server</span>  ';


stuff=stuff+'</li> \n ';
stuff=stuff+'</ul> \n ';
writeDoStatus(stuff,'',' NOSAVE FINAL ');

// now create a set of divs for each round. Each div contains a table, 1 row-set for each id
for (var id1=0;id1<submittedData['$idList'].length;id1++) {

  nowid=submittedData['$idList'][id1];
  myName=nameLookup[nowid];
  roundsInId=submittedData['$ids'][nowid];                      // elements of submittedData['$ids'] are arrays
  stuff='<div  id="ID_'+nowid+'" style="background-color:#dcdcde; border: 1px dashed gray"><a title="'+nowid+'" href="#aTop">Name ' +nameLookup[nowid]+'</a> &nbsp; # rounds='+roundsInId.length+' \n ';
  if (typeof(tableFontSize)=='undefined') tableFontSize=100 ; tableFontSize=strip(tableFontSize);
  atable=' \n <table  border="1" style="font-size:'+tableFontSize+'%" ><tr  bgcolor="lime"><th bgcolor="lime">Round</th>';

//viewRefColsPerRow=12 ;
  var ii=0; 
  if (viewRefNoTH==0) {
  for (var ivar=0;ivar<varList.length;ivar++) {
     if (ii==viewRefColsPerRow) {
         atable=atable+'</tr><tr  bgcolor="lime"><th bgcolor="lime">..</th> \n ' ;
         ii=0;
    }
    ii++ ;
    atable=atable+'\n <th>'+splitString(varList[ivar],10,' ')+'</th> ' ;
  }
  }                             // viewrefnoth
  atable=atable+' </tr> \n';
  icolor=1;
  for (var irow=0;irow<roundsInId.length;irow++) {
    nowr=roundsInId[irow];
    arecord=submittedData[nowr][nowid];

     icolor=1-icolor ;
     atable=atable+'<tr bgcolor="'+ccolors[icolor]+'"> \n';

     wasTime=new Date(1*arecord['$time']); 
    wasTime=wasTime.toLocaleDateString()+' '+wasTime.toLocaleTimeString();
    atitle='Round ' + nowr+' submitted at ' +wasTime+', notFinal_ct = ' + arecord['$notFinal']+'_'+arecord['$ct'];
     arow='\n <td bgcolor="cyan" title="'+atitle+'" onClick="writeDataMessage(this.title)"> <b> '+ nowr+ '</b>  </td> ' ;

  var ii=0; 
  for (var ivar=0;ivar<varList.length;ivar++) {
       if (ii==viewRefColsPerRow) {
              atable=atable+arow+'</tr> \n';
              arow='<tr bgcolor="'+ccolors[icolor]+'"> <td bgcolor="#eeeeff">..</td> \n ';
              ii=0; 
        }
        ii++ ;
        aval=''; avar=varList[ivar];  
        if (typeof(arecord[avar])!='undefined') aval=arecord[avar];
           showval=aval ;   if (showval=='') showval='&nbsp;';
           atitle='Round='+nowr+', user='+myName+': '+avar+' = '+aval ;     
           arow=arow+'\n    <td title="'+atitle+'" onClick="writeDataMessage(this.title)"> '+showval+'</td> ';
        }
        atable=atable+arow+'</tr> \n';
  }                                                     // participants in this round
  stuff=stuff+atable+'\n </div> \n ';
  writeDoStatus(stuff,'',' NOSAVE FINAL');
}  
return 1;


}


// =========================
/* parse information in a "submitted data" XML file, and save to submittedData object.
   Syntax:
     parseSubmittedData(anode,reset)
   where
         anode : a dom node containing an XML file -- say, as returned by the Get_xmlFile procedure
        reset : optional. If 0 (the default), do NOT reset submittedData. If NOT reset, contents of anode are ADDED to submittedData

Syntax of submittedData
  submittedData['$records']    # of records stored
  submittedData['$idlist']        array listing all the participants with one, or more, data records
  submittedData['$rounddlist]=[]   array of rounds that have one or more data rows 
  submittedData['$varlist']=[]    array of variable names mentioned in any data record

  submittedData[roundNum][clientId][varname]   : value of the "varname" variable for clientId participant in round roundNum. Maybe overwritten by later data records
                                                  (with different values of the ct attribute)
     Special varnames:
           $notFinal :   value of the "not final" attribute for the data. If notFinal=0 for a record, than this record will NOT be overwritten by
                         later records. Thus, a $notFinal of 0 "protects" this element of submittedData from being overwritten
           $ct       :  value of the "ct" attribute for the data (for first notFinal=0, or last appearing record, for this participant in this round).  
           $time     : time of submission (in getTime() format) 
           $cts       : an array of all the values of the [ct] submissions for this id & round 

  submittedData[roundNum][clientId][ct][varname]   : : value of the "varname" variable for ct submission of clientId participant in round roundNum. 
                                                      There may be several submissions per round
                                                      If no CT attribute in a data record, ct="1" is assigned.
                                                      If ct is NOT a nunber, an error occurs
                                                      Note that the $notFinal and $time varnames are also available for each of these [ct] "records"
                                                      ($ct is redundant, and $cts is NOT available)
                                        
*/

function    parseSubmittedData(anode,reset) {

var myTime,myNotFinal,myCt,myTime,myRound,myID,dlines,m,myRecord,tmpData ;
var varNode,valNode,statusNode,avar,aval,myvals,myvars,m2,ntot=0;
var tmpData=[] ;

if (arguments.length<2) reset=0 ;
if (reset==1 || typeof(submittedData)=='undefined' || typeof(submittedData['$ids'])=='undefined' ) {
  submittedData=new Object() ;                  // data submitted by clients
  submittedData['$records']=0 ;
  submittedData['$idList']=[] ;                      // array of ids with data.
  submittedData['$ids']={} ;                  // object of ids. Each object is an array of objects in submittedData that belong to this id
  submittedData['$roundList']=[] ;                      // array of rounds that have one or more data rows 
  submittedData['$rounds']={} ;;                  // object of rounds. Each object is an array of objects in submittedData that belong to this round
  submittedData['$varList']=[] ;                  // array of variable names mentioned in any data record
  submittedData['$vars']={}  ;                  // object of variable names mentioned in any data record. Object value=1 (flag that variable exists)
}

dlines=E2_getNodes(submittedDataNode,'data');


for (var m=0;m<dlines.length;m++) {
   aline=dlines[m];
   myID=getAttributes(aline,'id','');
   if (myID=='') {
        alert('Error in parseSubmittedData: no ID in a record ');
        return 0;
   }
   myRound=getAttributes(aline,'round','');
   if (myRound=='') {
        alert('Error in parseSubmittedData: no ROUND in a record ');
        return 0;
   }
   myCt=getAttributes(aline,'ct',1);
   if (isNaN(myCt)) {
        alert('Error in parseSubmittedData: CT is non-numeric : '+myCt);
        return 0;
  }

   myNotFinal=getAttributes(aline,'notFinal',0);
   myTime=getAttributes(aline,'time','');

   varNode=E2_getNodes(aline,'vars');
   valNode=E2_getNodes(aline,'vals');
   statusNode=E2_getNodes(aline,'status');

   if (varNode.length==0) continue ;                    // empty record, ignore
   if (valNode.length==0) continue ;                    // empty record, ignore

   myvars=E2_getNodeText(varNode[0],1,' ');
   if (myvars=='') continue ;                           // empty record. Ignore.

   myvals=E2_getNodeText(valNode[0],1,' ');
   if (myvals=='') continue ;                           // empty record. Ignore.


// first mention of this ROUND?
  if (typeof(submittedData['$rounds'][myRound])=='undefined') {
         submittedData['$rounds'][myRound]=[] ; 
         submittedData['$roundList']=submittedData['$roundList'].concat(myRound) ; 
         submittedData[myRound]={} ;
  }

// first mention of this ID?
  if (typeof(submittedData['$ids'][myID])=='undefined') {
         submittedData['$ids'][myID]=[] ; 
         submittedData['$idList']=submittedData['$idList'].concat(myID) ; 
  }


// variables specified in this record
    myvars==strip(myvars.toUpperCase()) ;
    myvars=getWord(myvars);                     // array of variable names specifed in this record
    for (m2=0; m2<myvars.length;m2++) {
        avar=myvars[m2];
        if (typeof(submittedData['$vars'][avar])=='undefined') {           // first mention of this variable --so record it 
                submittedData['$vars'][avar]=1 ;
                submittedData['$varList']=submittedData['$varList'].concat(avar) ;
        }
    }

// values specified in this record
    myvals=getWord(myvals,0,',');                     // array of values
    for (m2=0; m2<myvals.length;m2++) {
        myvals[m2]=unescape(myvals[m2]);
    }
    

// setup records (for this round/id/ct)
   myRecord=new Object() ;  
   myRecord['$notFinal']=myNotFinal ;      myRecord['$time']=myTime ;   myRecord['$ct']=myCt ;
   myRecord['$id']=myID ; myRecord['$round']=myRound ;

// now save values for this record's specified variables (to a temporary variables)
   for (var m2=0 ; m2<myvars.length; m2++) {
        avar=myvars[m2];
        if (!isNaN(avar)) {
                alert('Error in parseSubmitted: variable name is a number: ' +avar);
                return 0;
        }
        myRecord[avar]=myvals[m2];
   }

// save .. but make sure no existing record with notFinal=0
    if (typeof(submittedData[myRound][myID])=='undefined' ) {
          submittedData[myRound][myID]=myRecord ;
          submittedData['$rounds'][myRound]=submittedData['$rounds'][myRound].concat(myID);             // add to both reference tables
          submittedData['$ids'][myID]=submittedData['$ids'][myID].concat(myRound);
          ntot++ ;
   } else {
       if (submittedData[myRound][myID]['$notFinal']!=0)  {
          submittedData[myRound][myID]=myRecord ;
      }
   }
    tmpData=tmpData.concat(myRecord);                         // store for creation of [ct] elements

}                                       // this data row

// now create [ct] elements

for (var m=0;m<tmpData.length;m++) {
  myRecord=tmpData[m];
  myID=myRecord['$id'];
  myRound=myRecord['$round'];
  myCt=myRecord['$ct'];
  submittedData[myRound][myID][myCt]=myRecord ;
  if (typeof(submittedData[myRound][myID]['$cts'])=='undefined') {
        submittedData[myRound][myID]['$cts']=[myCt] ;
  } else {
     submittedData[myRound][myID]['$cts']=submittedData[myRound][myID]['$cts'].concat(myCt) ;
  }
}
  
submittedData['$records']=ntot + submittedData['$records'] ; 

return 1;
}

// =========================
/* display reference/user data
 refData['$IDS']=[] ;                      // array of IDS defined in refData
 refData['$VARS']=[] ;                  // array of reference variable names 
 refData['$VARLOOKUP']=new Object();                  // object of reference variable names 

userData is the same

Note that actionDiv is used directly  (doWriteStatus is NOT used, so nothing is stored in the history
*/


function viewReferenceVars(reftype,awhere,showbutton) {

if (arguments.length<1) reftype='REFERENCE';
if (arguments.length<2) awhere=actionDiv ;
if (arguments.length<3) showbutton=1 ;

if (awhere=='')  awhere=actionDiv ;
var ttr,tth,ict,tstuff,ict,icurrent ;
var ttable,ttbody ;
var numRows,iswitch;
var idName,rowdata,ids,varnames,headerMessage ;
var cvar,rval,rowID,tti,m2; 

if (showbutton==1) {
    var m2=viewAllVarsButtons() ;
    if (m2==0) return 0;
}

ttiBcolor=['#6fdfd1','#8fdf91'];

if (reftype=='REFERENCE') {
        idName='Ref Id';
        rowdata=refData;
        ids=refData['$IDS'];
        varnames=refData['$VARS'];
        headerMessage=' Reference variables for this session';
} else {
        idName='User Id';
        rowdata=userData;
        ids=userData['$IDS'];
        varnames=userData['$VARS'];
        headerMessage=' Current USER data';
}

if (typeof(ids)=='undefined' || typeof(ids.length)=='undefined' || ids.length==0) {
  icurrent=document.getElementById(awhere);
  icurrent.innerHTML=  icurrent.innerHTML+'<h3>'+headerMessage+'</h3> \n <b>None available!</b> <br /> Have you initialized the session?';
  return 1 ;        
}

icurrent=document.getElementById(awhere);
icurrent.innerHTML=icurrent.innerHTML+'<h3>'+headerMessage+'</h3> \n ';
    
ttable=document.createElement('table');
ttable.setAttribute('border',1);
if (typeof(tableFontSize)!='undefined') ttable.style.fontSize=strip(tableFontSize)+'%' ;

ttbody=document.createElement('tbody');

if (!viewRefNoTH) {                   // column name row not suppressed?
  ttr=document.createElement('tr');             // first column (refereence id)
  tth=document.createElement('th');
  tstuff=document.createTextNode(idName);
  tth.appendChild(tstuff);
  tth.style.width='65px';
  ttr.style.backgroundColor='#aaff99'; 
  ttr.appendChild(tth);

  tth=document.createElement('td');                     // divider cell
  tstuff=document.createTextNode(' ');
  tth.appendChild(tstuff);
  tth.style.backgroundColor='cyan';
  tth.style.width='5px';
  ttr.appendChild(tth);

ict=0;
for (var m=0;m<varnames.length;m++) {                  // write the column headers, possibly several table rows per data row
    ict++ ;
    if (ict>viewRefColsPerRow) {                             // create 2nd, 3rd line within header row
        ttbody.appendChild(ttr);
        ttr=document.createElement('tr');
        ttr.style.backgroundColor='#aaff99'; 

        tth=document.createElement('td');
           tstuff=document.createTextNode(' ');
           tth.appendChild(tstuff);
           tth.style.width='65px'; 
        ttr.appendChild(tth);

        tth=document.createElement('td');                     // divider cell
           tstuff=document.createTextNode('   ');
           tth.appendChild(tstuff); 
           tth.style.backgroundColor='cyan';
           tth.style.width='5px';
        ttr.appendChild(tth);
        ict=1;
    }                                   // new line in header row
    tth=document.createElement('th');
    tavar=strip(varnames[m].toUpperCase());
    tstuff=document.createTextNode(tavar);
    tth.appendChild(tstuff);
    adesc='' ; 
    ttr.appendChild(tth);
 }                                              // varnames header row

}
//ttbody.appendChild(ttr);
//ttable.appendChild(ttbody);



// display data rows (possibly over several table lines
  numRows=ids.length;
  iswitch=1;      
for  (var m=0;m<numRows;m++) {                                // 1 TR (table row) per data row

     rowID=ids[m]                 // this row's id
     iswitch=1-iswitch ;
     ttr=document.createElement('tr');  
     ttr.style.backgroundColor=inputRowColor[iswitch] ;

     tth=document.createElement('th');                                  // id column
     tstuff=document.createTextNode(rowID);
     tth.appendChild(tstuff);
     tth.style.width='65px'; 
     tth.style.color='red';
     ttr.appendChild(tth);

     tth=document.createElement('td');                          // spacer
     tstuff=document.createTextNode(' ');
     tth.appendChild(tstuff);
     tth.style.backgroundColor='cyan';
     tth.style.width='5px';
     ttr.appendChild(tth);

     ict=0;
     for (var m2=0;m2<varnames.length;m2++) {                            // the variables (perhaps over several table lines
        ict++ ;
        if (ict>viewRefColsPerRow) {                                         // make 2nd, ... table line for this data row
            ttbody.appendChild(ttr);
            ttr=document.createElement('tr');
            ttr.style.backgroundColor=inputRowColor[iswitch] ;
            tth=document.createElement('td');
               tstuff=document.createTextNode(' ');
               tth.appendChild(tstuff);
               tth.style.width='65px'; 
               ttr.appendChild(tth);
            tth=document.createElement('td');
               tstuff=document.createTextNode(' ');
               tth.appendChild(tstuff);
               tth.style.width='5px'; 
               tth.style.backgroundColor='cyan'; 
               ttr.appendChild(tth);
            ict=1;
        }                                                       // 2nd,.. lines in data row
        cvar=varnames[m2];
        rval=rowdata[rowID][cvar];
        tth=document.createElement('td');                               // a cell for this row/variable
//        tti=document.createElement('span');
//        tti.innerHTML=rval;
        tti=document.createElement('input');
        tti.value=rval ;
        tti.setAttribute('value',rval);
        if (typeof(tableFontSize)!='undefined') tti.style.fontSize=strip(tableFontSize)+'%' ;


        tti.setAttribute('size',rval.length+2);
        tti.setAttribute('readonly','yes');
        tti.style.backgroundColor=ttiBcolor[iswitch];
        tti.setAttribute('title',cvar);
        tth.appendChild(tti);
        ttr.appendChild(tth);
     }                                                  // m2  (varnames)
     ttbody.appendChild(ttr);
        
 }                                     //  m (rows)

 ttable.appendChild(ttbody);
 icurrent.appendChild(ttable);

return 1 ;

}                       

//      ====================
// retrieve all the variables from the server, and display them
function getAllServerVars(ido,showbutton) {
var id1,stuff,toPost,m,stuff2,a1,a12;
if (arguments.length<1) ido=0;
if (arguments.length<2) showbutton=0;

if (ido==2) {
    gws=getWord(getServerVarResults,0,',');
    stuff='<h3>Current server variables for this session </h3> <em>may include variables not yet read by control panel or participants</em><br /> \n';
    stuff2='<table>';
    for (var m=0;m<gws.length;m++) {
        a1=gws[m] ;
        a12=a1.split('=') ; 
        stuff2=stuff2+'<tr><th>'+a12[0]+'</th><td><input  class="SBUTTONS"  name="'+a12[0]+'" orig="'+a12[1]+'" type="text" value="'+a12[1]+'"></td></tr> \n ' ;
    }
    stuff2=stuff2+'</table> \n ';
    stuff2=stuff2+'<br><input type="button" value="Submit changes" onClick="changeServerVars()"> \n ';
    stuff=stuff+stuff2;

  if (showbutton==1) {
      var m2=viewAllVarsButtons() ;
      if (m2==0) return 0;
  }

    if (showbutton!=1) {
       writeDoStatus(stuff,'',' RESET FINAL ',' retrieve data from server ');
     } else {
       writeDoStatus(stuff,'','NOSAVE  FINAL ');
     }
    return 1;
}

toPost=getServerVarURL+'session='+sessName+'&varname=*&delim=,' ;
Get_File(toPost,'getServerVarResults','reading all server vars','getAllServerVars',2,1) ;
writeDoStatus('Reading all server variables','','NOSAVE INTER');
return 1;

}

//      ====================
// change server variables
function changeServerVars(ido,avar,aval) {

if (arguments.length<1) ido=1 ;
if (arguments.length<2) avar='';
var goo,z,aclass,m,setval,curval,aname ;
var toGet,varlist='',vallist='' ;
goo=[];

if (ido==1) {
  if (avar=='') {
     z=document.getElementsByTagName('input');
     for (var m=0;m<z.length;m++) {
       aclass=getAttributes(z[m],'class','');
       if (aclass!='SBUTTONS') continue ;
       setval=getAttributes(z[m],'orig','');
       aname=getAttributes(z[m],'name','');
       curval=z[m].value ;
       if (curval==setval) continue ;
       varlist=varlist+aname+' ';
       if (vallist!='') vallist=vallist+',' ;
       vallist=vallist+curval ;
     }
   } else {
         varlist=avar ;
         vallist=aval;
   }
   if (varlist=='') {
     writeDoStatus(' No changes made','','');
     return 0 ;
  }
  toGet=setServerVarURL+'session='+sessName+'&varname='+escape(varlist)+'&value='+escape(vallist)+'&delim='+escape(',') ;
  writeDoStatus(' Setting server variables: '+varlist,'','NOSAVE RESET INTER',' setting server variables');
  Get_File(toGet,'setServers','setting server variables','changeServerVars',2);
  return 1 ;
}
writeDoStatus(' Response from server: ' +setServers,'','');
return 1 ;
}


//      ====================
// save parameters to a round or session specific client accessible parameters file
// this is called by a generic action. It is similar to the postCAP DO action
// atype should either be SESSION or ROUND
// ido should be 1 

function doSaveCAP(atype,ido) {
var tmp,stuff,toPost,aname='';

if (arguments.length<2) ido=1 ;  

if (ido==1) {
  controlVars['$SUBMITFORMLIST']=submitFormList.join(' ');   // so that $submitFormList (in <GENERAL>) includes recently modified parameters
  tmp=[];
  stuff=makeClientParametersXML('GENERAL MESSAGE',0);             // arg can be list containing GENERAL USERS and REFERENCE 
  submitFormList=[];                       // reinitialize list of "changed variables"
  stuff=escape(stuff);                        // escaped for safe passage (some stuff might end up being double esscaped)
  toPost=postClientFileBody+'&session='+sessName+'&name='+aname+'&type='+atype+'&contents='+stuff;
  writeDoStatus('About to save '+atype+' client accessible paramteters file ','','RESET NOSAVE INTER',' saving cap file');
  Post_toServer(postFileURL,toPost,'postFileResponse','posting '+atype+' xml client accessible file ','doSaveCAP',atype,2);
  return 1 ;
}

  writeDoStatus('Save '+atype+' client accessible parameter files:'+postFileResponse,'');
  writeDataMessage('Save '+atype+' client accessible parameter files:'+postFileResponse,5);

  return 1 ;


return 1 ;
}

//      ====================
// save the current session status. This is a standalone version (slightly different code when SAVESNAPSHOT done as <DO> action
function doSaveSnapshot(todo,dodisp) {
if (arguments.length<1) todo=0
if (arguments.length<2) dodisp=0

var astuff,t0,t1,t2,toPost,towrite,doappend=0;
if (todo==2) {                  // after resposne from server
    t0=strip(doSaveSnapshotResponse) ;
    if (t0.substr(0,1)=='-') {
        tmp='SAVESNAPSHOT ERROR: unable to save current status. Error message= '+t0 ;
     } else {
         tmp= 'SNAPSHOT file (for this session) has been  saved (status: '+doSaveSnapshotResponse;
    }
    if (dodisp==1) {

       writeDoStatus(tmp,'','RESET ',' saving snapshot');
   } else {
       writeDoStatus(tmp,'','RESET INTER',' saving snapshot');
    }
    return 1;
}

// post info to server
    toPost=doSaveSnapshotMake()  ;
    Post_toServer(postFileURL,toPost,'doSaveSnapshotResponse','posting xml SNAPSHOT file ','doSaveSnapshot',2,dodisp) ;
    writeDoStatus('About to Post a status (all current parameters) file ','','NOSAVE INTER');
    return 1 ;

}

// ==================
// make the guts of a SAVESNAPSHOT request. Uses postFileHeader and sessName globals
// note: don't bother saving messageData
function doSaveSnapshotMake()  {
  var astuff,toPost ;
        astuff='<?xml version="1.0" encoding="UTF-8" ?> \n' ;
        astuff=astuff+'<parameters> \n ';
        astuff=astuff+'<created> '+sessName+' at ' + svy_currentTime(1,2)+' </created>   \n ';
        t0=makeGUparametersXMLGeneral(0,1);
        t1=makeGUparametersXMLReference(0,1);
        t2=makeGUparametersXMLUsers(0,1);
        t3=makeGUparametersXMLcurrentParticipants(0,1);
        astuff=astuff+'\n\n <general> \n'+t0+' </general> \n\n <reference> \n'+t1+' </reference> \n\n <users>\n'+t2+'\n</users> \n <currentParticipants> \n '+t3+'</currentParticipants> \n </parameters> \n';
        toPost=postFileHeader+'&name='+sessName+'&file=SAVESNAPSHOT.xml&content='+astuff ;
        return toPost ;
}

//      ====================
// Subfunction of doStuff : process retrieved XML file (in gnode), and save variables using saveGeneralVar
// unesc means "read from snapshot file"; so unescape
function parseVarNodeGeneral(gnode,unesc) {
 var anode,isval0,varlist,cnode,bnode,bname,tbname,isarray,isval,gg,nread=0,theseVars='';
 var goop,m2i,m,m2;

  if (gnode.length>0) {                 // save variables in GENERAL
    anode=gnode[0];
    varlist='';
    for (var m=0;m<anode.childNodes.length;m++) {
       bnode=anode.childNodes[m];
       if (bnode.nodeType!=1) continue ;
       bname=strip(bnode.nodeName);
       tbname=bname.toUpperCase();
       if (tbname=='DESC') continue ;
       isarray=getAttributes(bnode,'array','');
       isval=E2_getNodeText(bnode,1,'');
       if (isarray!=='' ) {                                // parse into an array: possibly adding an offset (i.e.; starting at 1, not at 0
         adelim=getAttributes(bnode,'delim',',');
         isval0=isval.split(adelim); isval=[];
         for (var m2=0;m2<isval0.length;m2++) {         // isarray in snapshot means "this is array". In parameters, it means "this is an array, with this offset"
                goop=isval0[m2];
                m2i=m2+(1*isarray);                 // add an offset? Note: if  from snapshot, offset will be 0
                if (unesc==1) {                                 
                   isval[m2i]=unescape(goop);            // if from snapshot, it was escaped
                } else {
                   isval[m2i]=goop;
                }
          }                  // isval0 to isval (possible shift)
       } else {                   // not an array
          if (unesc==1) isval=unescape(isval);
       }

       saveGeneralVar(bname,isval,isarray);
       theseVars=theseVars+tbname+' ';
       nread++ ;
    }
  }
  return theseVars ;
}


// ===================================
// process an "expanded" USERS or REFERENCE node
function parseVarNodeURexpand(gnode,reftype) {

var m,m2,anode,anode2,aid,dnodes,avar,unodes,aval;
var vlist={} ;
var maxRefId=0 ;

reftype=strip(reftype.toUpperCase()) ;
if (reftype!='USERS' && reftype!='REFERENCE') {
  alert(' Bad reftype in parseVarNodeURexpand: '+reftype);
  return 0;
}

unodes=E2_getNodes(gnode,'record user data ');

for (var m=0;m<unodes.length;m++) {
   anode=unodes[m];
   aid=getAttributes(anode,'ID REFID','');
   if (aid==='') continue ;                      // no ID, skip

  if (reftype=='USERS') {
          if (typeof(userData[aid])=='undefined') {
               userData[aid]=new Object() ;
               userData['$IDS']=userData['$IDS'].concat(aid);
           }
   } else {
          if (typeof(refData[aid])=='undefined') {
               refData[aid]=new Object() ;
               if (isNaN(aid) || strip(aid)==='') {
                        alert('Bad referenceId = ' +aid);
                        return 0;
               }
               maxRefId=Math.max(maxRefId,aid); 
               if (aid!=0) {                                     // id 0 is NOT added to list
                   refData['$IDS']=refData['$IDS'].concat(aid);        
                   refData['$MAXREFID']=maxRefId;
               }
           }
   }

   dnodes=E2_getNodes(anode,'*') ;              // get all nodes 
   for (var m2=0;m2<dnodes.length;m2++){
        anode2=dnodes[m2];
        avar=strip(anode2.nodeName.toUpperCase()) ;
        isind=getAttributes(anode2,'element','');
        if (isind!=='') {
             avar=avar+'['+strip(isind)+"]";
        } else {
           avar=cvtToArrayName(avar)     // convert x_n_ to x[n]
        }
         
        vlist[avar]=1;
        aval=E2_xmlToString(anode2,1,1)
        if (reftype=='USERS') {
           if (typeof(userData['$VARLOOKUP'][avar])=='undefined') {
              userData['$VARLOOKUP'][avar]=1;
              userData['$VARS']=userData['$VARS'].concat(avar);
          }
           userData[aid][avar]=aval;
        } else {
           if (typeof(refData['$VARLOOKUP'][avar])=='undefined') {
              refData['$VARLOOKUP'][avar]=1;
              refData['$VARS']=refData['$VARS'].concat(avar);
          }
          refData[aid][avar]=aval;
        }
   }                            // m2
}                               // m

var varray=[];
for (var m in vlist) varray=varray.concat(m);
return varray.join(' ');

}


// ===================================
// read stuff in USERS, currentParticipants, or REFERENCE section; and copy to userData or refData
// isecape =1 if the variables have been escaped. This is needed when reading a SNAPSHOT file (that has reference and user variables escaped
//            It should NOT be used when reading an administrative parameters file (i.e; when called due to a GETPARAMS)

function parseVarNodeUR(gnode,reftype,isescape) { 

  var anode,bnode,nread=0,varlist,m,rval;
  var rnodes,rid,rnode,rval,rdelim,valx,m2,avar,isexpanded
  var svars='',bvar='' ;
  if (arguments.length<3) isescape=0;
  var maxRefId=0 ;

   anode=gnode[0]; 

// is this an "expanded" syntax
  isexpand=getAttributes(anode,'expanded expand','');

  if (isexpand=='1') {
        svars=parseVarNodeURexpand(anode,reftype ) ; 
        return svars ;
  }

   bnode=E2_getNodes(anode,'vars');
   if (bnode.length==0) {
         alert('Error in '+reftype+' section of XML file '+bvar+'  : no <VARS>.  Perhaps you loaded parameter files before participants have joined? ');
         return 0 ;
    }
    varlist=strip(E2_getNodeText(bnode[0],1,' '));
    if (varlist=='') {
         alert('Error in '+reftype+' section of XML file '+bvar+'  : empty <VARS>. Perhaps you loaded parameter files before participants have joined? ');
         return 0 ;
    }
   varlist=varlist.replace(/,/g,' ');
   svars=varlist ;
   varlist=getWord(varlist);

   for (var m=0; m<varlist.length;m++) {
       rval=strip(varlist[m].toUpperCase());
       if (reftype=='USERS') {
          if (typeof(userData['$VARLOOKUP'][rval])=='undefined') {
              userData['$VARLOOKUP'][rval]=1;
              userData['$VARS']=userData['$VARS'].concat(rval);
           }
       } else if (reftype=='currentParticipants') {
          if (typeof(currentParticipants['$VARLOOKUP'][rval])=='undefined') {
              currentParticipants['$VARLOOKUP'][rval]=1;
              currentParticipants['$VARS']=currentParticipants['$VARS'].concat(rval);
           }
        } else {
          if (typeof(refData['$VARLOOKUP'][rval])=='undefined') {
              refData['$VARLOOKUP'][rval]=1;
              refData['$VARS']=refData['$VARS'].concat(rval);
           }
        }
  }

  bnode=E2_getNodes(anode,'vals');
  if (bnode.length==0) {
         alert('Error in '+reftype+' section of XML file '+bvar+'  : no <VALS> ');
         return 1 ;
   }
  rdelim=getAttributes(bnode[0],'delim',' ');

  if (reftype=='USERS' || reftype=='currentParticipants') {
      rnodes=E2_getNodes(bnode[0],'user record row ');
  } else {
      rnodes=E2_getNodes(bnode[0],'record  user row');
  }

  for (var m=0;m<rnodes.length;m++) {
       rnode=rnodes[m];
       rid=getAttributes(rnode,'id','');
       if (rid=='') {
          alert('Error: row in <vals> does not have and ID ');
          return nread ;
       }
       if (reftype=='USERS') {
          if (typeof(userData[rid])=='undefined') {
               userData[rid]=new Object() ;
               userData['$IDS']=userData['$IDS'].concat(rid);
           }
      } else if (reftype=='currentParticipants') {
          if (typeof(currentParticipants[rid])=='undefined') {
               currentParticipants[rid]=new Object() ;
               currentParticipants['$IDS']=currentParticipants['$IDS'].concat(rid);
           }
      } else {
          if (typeof(refData[rid])=='undefined') {
               refData[rid]=new Object() ;
               if (isNaN(rid) || strip(rid)==='') {
                        alert('Bad reference Id = ' +rid);
                        return 0;
               }
               maxRefId=Math.max(maxRefId,rid); 
               if (rid!=0) {                                     // id 0 is NOT added to list
                   refData['$IDS']=refData['$IDS'].concat(rid);
                   refData['$MAXREFID']=maxRefId;
               }
           }
      }
       rval=E2_getNodeText(rnode,1,'');
       valx=getWord(rval,0,rdelim) ;
       for (var m2=0; m2<varlist.length;m2++) {
         avar=strip(varlist[m2].toUpperCase());
         var tval=valx[m2];
         if (isescape==1) tval=unescape(tval) ;
         if (reftype=='USERS') {
              if (m2<valx.length) {
                 userData[rid][avar]=tval;
              } else {
                userData[rid][avar]='';
              }
           } else if (reftype=='currentParticipants') {
              if (m2<valx.length) {
                 currentParticipants[rid][avar]=valx[m2];               // vars for current participants are never escaped
              } else {
                currentParticipants[rid][avar]='';
              }
           } else {
              if (m2<valx.length) {
                 refData[rid][avar]=tval;
              } else {
                 refData[rid][avar]='';
              }
          }                     //reftype
       }                        // varlist.length
       nread++ ;
  }                                   // rnodes.length
  return svars ;
}

// =================
// Make an XML file using instructions.  Return the contens of this XML file. 
// anode contains instructions on what variables to use
// note that general, users, message, and references sections all save using escaped values (or comma delimited arrays where each element is escaped

function makeClientParametersXML(anode,isPostCap) {

var gnode,gstuff,ustuff,useTypes=0;
if (arguments.length<2) isPostCap=0;

if (typeof(anode)=='string') {
  anode=' '+anode.toUpperCase()+' ' ;
  useTypes=1; 
} 

var afile='' ;

afile='<?xml version="1.0" encoding="UTF-8" ?> \n' ;
afile=afile+'<parameters> \n ';
afile=afile+'<created> '+svy_currentTime(1,2)+' </created>   \n ';

gnode=['*']; doall=0;
if (useTypes==0) {
  gnode=E2_getNodes(anode,'GENERAL');
} else {
  if (anode.indexOf('GENERAL')>-1) doall=1;
}
if (gnode.length>0) {                   // process GENERAL node
   gstuff=makeGUparametersXMLGeneral(gnode[0],doall);
   afile=afile+'   <general> \n ';
   afile=afile+gstuff + ' \n ';
   afile=afile+'   </general> \n \n ';
}


gnode=['*']; doall=0;
if (useTypes==0) {                              // message NOT written to snapshot file
  gnode=E2_getNodes(anode,'MESSAGE');
} else {
  if (anode.indexOf('MESSAGE')>-1) doall=1;
}

if (gnode.length>0) {                   // process MESSAGE node
   gstuff=makeGUparametersXMLMessage(gnode[0],doall,isPostCap);
   afile=afile+'   <message> \n ';
   afile=afile+gstuff + ' \n ';
   afile=afile+'   </message> \n \n ';
}


gnode=[0];
if (useTypes==0) {
  gnode=E2_getNodes(anode,'USERS');
} else {
  if (anode.indexOf(' USERS ')>-1) doall=1;
}

if (gnode.length>0) {                   // process USERS node
   gstuff=makeGUparametersXMLUsers(gnode[0],doall);
//displayStringInWindow('hahrhr' +gstuff,1,1);
//return -1 ;
   afile=afile+'   <users> \n ';
   afile=afile+gstuff + ' \n ';
   afile=afile+'   </users> \n \n ';

}

gnode=['*'];
if (useTypes==0) {
   gnode=E2_getNodes(anode,'REFERENCE');
} else {
  if (anode.indexOf(' REFERENCE ')>-1) doall=1;
}
if (gnode.length>0) {                   // process REFERENCE node
   gstuff=makeGUparametersXMLReference(gnode[0],doall);
   afile=afile+'   <reference> \n ';
   afile=afile+gstuff + ' \n ';
   afile=afile+'   </reference> \n \n ';
}



afile=afile+'</parameters> \n ';

return afile ;
}



//=========================== 
// given an array containing variables (or other strings), create an object suitable for "checking for existence"
// uses stripped, capitalized versions of elements of vlist as object indices
function arrayToCheckObject(vlist) {
var aword,m;
var check1={};
var vlista=getWord(vlist);
for (var m=0;m<vlista.length;m++) {
        aword=strip(vlista[m].toUpperCase()) ;
        check1[aword]=1;
}
return check1 ;
}


//======================================
// The next several functions create strings containing an xml representation of a given database


// ======= General data
// take the GENERAL control panel database and create a string variable containing xml version of it
// the record will contain a <ROUND> node, containing the value of the useRound argument

function makePostDataGeneral(useround,vlist) {
var i,m,aval,doall,aval3,i2,i22 ;
var kct=0;
var atime=new Date(); atime=atime.toLocaleString() ;

var stuff='<!-- '+atime+ '--> \n <data><round>'+useround+'</round> \n     ';

var check1={},doall=0;
vlist=strip(vlist);

if (strip(vlist)=='' || vlist=='*') {
   doall=1;
} else {
  check1=arrayToCheckObject(vlist) ;
}

// Note: for general variables, if its an array, create seperate entries for each value in array, using nodeName_ith_ identify index in the array
for (var i=0;i<generalVars['$VARS'].length;i++) { ;
  avar=generalVars['$VARS'][i] ;  avar=strip(avar.toUpperCase()) ;
  if (doall==1 || typeof(check1[avar])!='undefined') {
     aval3=generalVars[avar];
     if (aval3[0]!=='' && aval3[0]!='0') {                 // an array
        for (var i2=0;i2<aval3[1].length;i2++) {
          i22=i2 ;
          stuff=stuff+'<'+avar+'_'+i22+'_>'+aval3[1][i2]+'</'+avar+'_'+i22+'_> ';
          kct++ ;
          if (kct>=8) {                             // prettify
            stuff=stuff+' \n    '; kct=0;
          }
        }
     } else {                           // not an array
        stuff=stuff+'<'+avar+'>'+aval3[1]+'</'+avar+'> ';
     }
     kct++ ;
     if (kct>=8) {                             // prettify
         stuff=stuff+' \n    '; kct=0;
     }
  }                                                      // else, not specified so skip
}
stuff=stuff+ '\n </data> ';
return stuff;
}


// ======= users data
function makePostDataUsers(useround,vlist) {
var i,m,aval,doall,ir,iv,avar,kct ;

var kct=0;
var atime=new Date(); atime=atime.toLocaleString() ;
var stuff='\n <!-- '+atime+'  for round ' + useround+ ', #  clients = '+ currentParticipants['$IDS'].length+ '  --> \n ';

var check1={},doall=0;
vlist=strip(vlist);

if (strip(vlist)=='' || vlist=='*') {
   doall=1;
} else {
   vlist=expandVarlistWild(vlist,'U',1);
   check1=arrayToCheckObject(vlist);
}


for (var ir=0;ir<currentParticipants['$IDS'].length;ir++) { ;
  aid=currentParticipants['$IDS'][ir];
  if (typeof(userData[aid])=='undefined') continue ;             // skip if not in currentParticipants list
  stuff=stuff+'\n <data><round>'+useround+'</round><id>'+aid+'</id> \n     ';
  for (var iv=0;iv<userData['$VARS'].length;iv++) {
     avar=userData['$VARS'][iv] ;  avar=strip(avar.toUpperCase()) ;
    if (doall==1 || typeof(check1[avar])!='undefined') {
        aval='' ;
        if (typeof(userData[aid][avar])!='undefined') aval=userData[aid][avar];
        avar=cvtFromArrayName(avar);
        stuff=stuff+'<'+avar+'>'+aval+'</'+avar+'> ';
     }
     kct++ ;
     if (kct==6) {                             // prettify
         stuff=stuff+' \n    '; kct=0;
     }
  }                                                      // variable
  stuff=stuff+ '</data> \n ';
}                                               // client
stuff=stuff+ '\n  ';
return stuff;
}

// ======= participants data
function makePostDataParticipants(useround,vlist) {
var i,m,aval,doall,ir,iv,avar,kct ;

//currentParticipants['$IDS']=[] ;
//currentParticipants['$VARS']=[] ;
//currentParticipants['$VARLOOKUP']={};
//currentParticipants['$TIME']='' ;

var kct=0;
var atime=new Date(); atime=atime.toLocaleString() ;
var stuff='\n <!-- '+atime+'  for round ' + useround+ ', #  clients = '+ currentParticipants['$IDS'].length+ '  --> \n ';
stuff=stuff+'\n <!-- retrived from server at ' +currentParticipants['$TIME'] + '  --> \n ';

var check1={},doall=0;
vlist=strip(vlist);

if (strip(vlist)=='' || vlist=='*') {
   doall=1;
} else {
  vlist=expandVarlistWild(vlist,'P',1);
  check1=arrayToCheckObject(vlist);
}

for (var ir=0;ir<currentParticipants['$IDS'].length;ir++) { ;
  aid=currentParticipants['$IDS'][ir];
  stuff=stuff+'\n <data><round>'+useround+'</round><id>'+aid+'</id> \n     ';
  for (var iv=0;iv<currentParticipants['$VARS'].length;iv++) {
     avar=currentParticipants['$VARS'][iv] ;  avar=strip(avar.toUpperCase()) ;
    if (doall==1 || typeof(check1[avar])!='undefined') {
        aval='' ;
        if (typeof(currentParticipants[aid][avar])!='undefined') aval=currentParticipants[aid][avar];
        avar=cvtFromArrayName(avar);
        stuff=stuff+'<'+avar+'>'+aval+'</'+avar+'> ';
     }
     kct++ ;
     if (kct==6) {                             // prettify
         stuff=stuff+' \n    '; kct=0;
     }
  }                                                      // variable
  stuff=stuff+ ' </data> \n  ';
}                                               // client
stuff=stuff+ '\n  ';
return stuff;
}


// ======= results data
function makePostDataResults(useround,vlist) {
var i,m,aval,doall,ir,iv,avar,kct ;


var kct=0;
var atime=new Date(); atime=atime.toLocaleString() ;
var stuff='\n <!-- '+atime+'  for round ' + useround+ ', #  clients = '+ currentParticipants['$IDS'].length+ '  --> \n ';

var check1={},doall=0;
vlist=strip(vlist);

if (strip(vlist)=='' || vlist=='*') {
   doall=1;
} else {
  vlist=expandVarlistWild(vlist,'R',1);
  check1=arrayToCheckObject(vlist) ;
}
 // do the user specific records
for (var ir=0;ir<currentParticipants['$IDS'].length;ir++) { ;
  aid=currentParticipants['$IDS'][ir] ;
  if (typeof(resultsData[aid])=='undefined') continue ;             // skip if not in currentParticipants list
  stuff=stuff+'\n <data><round>'+useround+'</round> <id>'+aid+'</id> \n     ';
  for (var iv=0;iv<resultsData['$VARS'].length;iv++) {
     avar=resultsData['$VARS'][iv] ;  avar=strip(avar.toUpperCase()) ;
     if (doall==1 || typeof(check1[avar])!='undefined') {
        aval='' ;
        if (typeof(resultsData[aid][avar])!='undefined') aval=resultsData[aid][avar];
        avar=cvtFromArrayName(avar);
        stuff=stuff+'<'+avar+'>'+aval+'</'+avar+'> ';
     }
     kct++ ;
     if (kct==6) {                             // prettify
         stuff=stuff+' \n    '; kct=0;
     }
  }                                                   // variable
  stuff=stuff+ '\n </data> ';
}                                       // id 

// and for * id
aid='*' ;
for (var ir=0; ir<1; ir++) {
    if (typeof(resultsData[aid])=='undefined') continue ;             // skip if not in currentParticipants list
    stuff=stuff+'\n <data><round>'+useround+'</round><id>'+aid+'</id> \n     ';
    for (var iv=0;iv<resultsData[aid]['$VARS'].length;iv++) {
       avar=resultsData[aid]['$VARS'][iv] ;  avar=strip(avar.toUpperCase()) ; 
       if (doall==1 || typeof(check1[avar])!='undefined') {
          aval='' ;
          if (typeof(resultsData[aid][avar])!='undefined') aval=resultsData[aid][avar];
          avar=cvtFromArrayName(avar);
          stuff=stuff+'<'+avar+'>'+aval+'</'+avar+'> ';
       }
       kct++ ;
       if (kct==6) {                             // prettify 
           stuff=stuff+' \n    '; kct=0;
       }
  }                                                      // var 
}                                               // aid record 

stuff=stuff+ '</data> \n  ';



return stuff;
}                         

    
// ======= history & custom data
// useround: the round value to use when writing data
// vlist00 : list of variables to write
// showtype0: include a type attribute in the XML element
// gexpand: expand arrays (1 or 2: create seperate element for all values in an array,0 = use comma delimited content in a single element
// sround: for .S variables: the round to use (typicallly, will be the same as useround:
//         special value: -1 means "working from the last round, find the latest round that contains this variable
// sroundSkip : used ONLY if sround=-1.  If sroundSkip=1, then try to find a non-blank value (that is, if a value is
//              defined for round T but it equals, look at prior round. And stop when a non-blank value is found

function makePostDataHistory(useround,vlist00,showtype0,gexpand,sround,sroundSkip) {
var vlist,vlist0,iv,iv0,avar,vv,atype,aval,aid,sRound,showtype,vfoo,tmp,uround;
var kct=0,lenstuff=0;
var atime=new Date(); atime=atime.toLocaleString() ;
var stuff='\n <!-- '+atime+'  for round ' + useround+ ', #  clients = '+ currentParticipants['$IDS'].length+ '  --> \n ';

showtype=1;

if (arguments.length>2) showtype=showtype0;

var check1={};
vlist=strip(vlist00);

if (strip(vlist)=='' || vlist=='*') {
  alert('Error in makePostDataHistory: bad (empty, or just a *) varlist= ' +vlist);
  return 1 ;
}
//var vfoo=vlist;
vlist=expandVarlistWild(vlist,'G',1);                     // default is GENERAL variables
vlist0=getWord(vlist);
//alert('orig='+vfoo+'\n after expand ='+vlist);
vlist=[];

for (var iv0=0;iv0<vlist0.length;iv0++) {
     avar=vlist0[iv0];
     if (avar=='*') {
         alert('Error in makePostDataHistory: * (with no type) wildcard not allowed when creating HISTORY or CUSTOM ');
         return 1 ;
     }
     vv=parseAt(avar,'.');
     if (vv.length==1) {
         alert('Error in makePostDataHistory: bad variable (no .type) : ' +avar);
         return 1 ;
     }
     atype=strip(vv[1].toUpperCase())  ; 
    
     if (atype!='U' && atype!='G' && atype!='P' && atype!='R' && atype!='S') {
         alert('Error in makePostDataHistory: bad variable (bad .type) : ' +avar);
         return 1 ;
     }

     avar=strip(vv[0].toUpperCase()) ;

     if (atype=='G') {                  // GENERAL
        if (typeof(generalVars[avar])=='undefined') {
           iv=vlist.length;
           vlist[iv]={} ;
           vlist[iv].type=atype; vlist[iv].name=avar ;
           vlist[iv].useval='';
        } else {                                // found a value
            tmp= readGeneralVar(avar,'');
            if (gexpand==='0' || typeof(tmp)!=='object' || typeof(tmp.length)=='undefined')  {   // not an array, or expansion is suppressed
               iv=vlist.length;
               vlist[iv]={} ;
               vlist[iv].type=atype; vlist[iv].name=avar ;  
               vlist[iv].useval=tmp;
            } else {                            // an array, and gexpand>0 -- create seperate entries in vlist for  each element
               for (var jj=0;jj<tmp.length;jj++) {       
                  if (gexpand==='2' && typeof(tmp[jj])=='undefined') continue ;  // skip undefineds?
                  iv=vlist.length;
                  vlist[iv]={} ;
                  vlist[iv].type=atype; vlist[iv].name=avar+'['+jj+']' ;
                  vlist[iv].useval=tmp[jj];
                }
            }                                           // tmp an array
        }                                       // avar is a general var

     } else {                           // not a general var
        iv=vlist.length;
        vlist[iv]={} ;
        vlist[iv].type=atype; vlist[iv].name=avar ;
     }   
}

// find most recent round  in submittedData

if (vlist.length==0) {
        if (strip(vlist00).toUpperCase()=='*.S') {
            alert('Problem: *.S variable specified (in a save custom or history), but submitted variables were not retrieved (did you specify <getSubmitted>?) ');
        } else {
            alert('Problem: empty variable list (in a save custom or history): '+vlist00);
        }
}


// do the user specific records


for (var ir=0;ir<currentParticipants['$IDS'].length;ir++) { ;
  aid=currentParticipants['$IDS'][ir] ;
  stuff=stuff+'\n <data><round>'+useround+'</round> <id>'+aid+'</id> \n     ';
  for (var iv=0;iv<vlist.length;iv++) {
     avar=vlist[iv].name;
     atype=vlist[iv].type;
     aval='';uround='';
     if (atype=='G') {                  // GENERAL
        aval=vlist[iv].useval  ;                        // defined in vlist creation

     } else if (atype=='U') {           // USERS
         if (typeof(userData[aid][avar])!='undefined') aval=userData[aid][avar] ;

     } else if (atype=='S') {           // SUBMITTED
            aval=extractSubmittedValue(sround,aid,avar,sroundSkip,'',useround);
            if (typeof(aval)=='object') {
              uround=aval[1]; aval=aval[0];
            }

//             if (typeof(submittedData[sround])!='undefined' &&  typeof(submittedData[sround][aid])!='undefined' && typeof(submittedData[sround][aid][avar])!='undefined') {
//                   aval=submittedData[sround][aid][avar] ;  
//             }

     } else if (atype=='R') {           // RESULTS
         if (typeof(resultsData[aid])!='undefined' && typeof(resultsData[aid][avar])!='undefined') {
             aval=resultsData[aid][avar] ;
         } else {                                                // check * id
             if (typeof(resultsData['*'][avar])!='undefined') {
                 aval=resultsData['*'][avar] ;
             }
         }

     } else if (atype=='P') {           // PARTICIPANTS
         if (typeof(currentParticipants[aid][avar])!='undefined')   aval=currentParticipants[aid][avar] ;
     }                                  // must be one of the above

     avar=cvtFromArrayName(avar);
     if (showtype==1) {
        if (uround!=='') {
            stuff=stuff+'<'+avar+' round="'+uround+'" type="'+atype+'">'+aval+'</'+avar+'> ';
        } else {
            stuff=stuff+'<'+avar+' type="'+atype+'">'+aval+'</'+avar+'> ';
        }
     } else {
        stuff=stuff+'<'+avar+'>'+aval+'</'+avar+'> ';
     }

     if ( (stuff.length-lenstuff)>100 ) {          // prettify
         stuff=stuff+' \n    '; lenstuff=stuff.length ;
     }

/* == 
     kct++ ;
    if (kct==6) {                             // prettify
         stuff=stuff+' \n    '; kct=0;
     }
=== */
  }                                                   // variable
  stuff=stuff+ '\n </data> ';
}                                       // id 


return stuff;
}


//=========
// extract a value from submited data. Supports "most recent round containing this variable" and "skip if blank"
// options
function extractSubmittedValue(sround,aid,avar,sroundSkip,adef,nowround) {
var aval,itt;
aval=adef;           
if (sround>-1) {                        // get from an explicit round
  if (typeof(submittedData[sround])!='undefined' &&  typeof(submittedData[sround][aid])!='undefined' && typeof(submittedData[sround][aid][avar])!='undefined') {
     aval=submittedData[sround][aid][avar] ;  
  }
  return aval;
}

// else, a "use value from round it is most recently defined" variable
for (var itt=nowround;itt>-1;itt--) {
  if (typeof(submittedData[itt])!='undefined' &&  typeof(submittedData[itt][aid])!='undefined' && typeof(submittedData[itt][aid][avar])!='undefined') {
      aval=submittedData[itt][aid][avar] ;  
      if (sroundSkip==1) {
         if (strip(aval)!=='') return [aval,itt] ;            // not  blank, so return it
      } else {
         return [aval,itt] ;                    // blank is okay
      }
  }
}
return [adef,-1] ;                    // not found, return default
}

// iscus showt useround
//======================
// Expand a variable list that has * wildcarded names. 
// arg2 is default type (if a word in varlist does not have a .x 
function expandVarlistWild(vlist,deftype,addtype) {
  if (arguments.length<2) deftype='U'; 
  if (arguments.length<3) addtype=0 ;

var vlistnew,vlist0,m,vv,iswild;
var avar,amatch,m2,chkVs,usetype,useword,nmatch;

vlistnew='';
vlist0=getWord(vlist);
for (var m=0;m<vlist0.length;m++) {
   aword=strip(vlist0[m].toUpperCase()) ;
   if (aword.indexOf('*')<0) {                  // no *, so no expansion
        vlistnew=vlistnew+' '+aword;
        continue
   }

// if here, * so do expansion
   vv=parseAt(aword,'.');
   if (vv[0]=='') continue ;
   useword=vv[0] ;
   usetype=vv[1];
   if (usetype=='') usetype=deftype ;

   chkvs=[];
   if (usetype=='U' || usetype=='D')  chkVs=userData['$VARS'];
   if (usetype=='C' || usetype=='P')  chkVs=currentParticipants['$VARS'];
   if (usetype=='R') chkVs=resultsData['$VARS'];
   if (usetype=='G') chkVs=generalVars['$VARS'];
   if (usetype=='S') chkVs=submittedData['$varList'];
   nmatch=0;
   for (var m2=0;m2<chkVs.length;m2++){                     // check all vars for a match, add all that match
        avar=strip(chkVs[m2].toUpperCase());
        amatch=matchWild(useword,avar,1);               // note: match original vars to requested vars -- this is done to support wildcard matching
        if (amatch!='') {                   // got a match
             nmatch++ ;
             if (addtype==1) {
               vlistnew=vlistnew+' '+avar+'.'+usetype ;            
             } else {
               vlistnew=vlistnew+' '+avar ;            
             }
        }
   }                                  // vars of this type
  if (nmatch==0) {
        var zz=confirm('Warning in expandVarList: no match for variable (type = '+usetype+') '+useword+'\n '+chkVs.join(' | ') );
        if (!zz) return 0 ; 
  }
   
}                               // for vlist0

return vlistnew ;
} 


// ============= make xml: from <GENERAL>. Return string containing xml of the general variables (don't bother with surrounding elements).  ;
//  anode contains instructions on what variables to use
// note use of escaping (especially for arrays, where elements are escaped and commas seperate these escaped values)
// doall=1 means "save all variables". This does NOT include resultsData values (since doall is only used to make SNAPSHOT files

function makeGUparametersXMLGeneral(gnode,doall) {
  var gvars,m,newvarsb,gvars2,aword,tmp,newvars,afile,aline,useword,isarray,aval,m0 ;
  var sarray,tval,mm,avv,vinfo,tmp,avar,aid,tnew,tlist;
  var j,didaddr ;

  gvars='';

  if (arguments.length<2) doall=0;

  if (doall!=1) {
      gvars=E2_getNodeText(gnode,1,' ');
      if (gvars=='') return '' ;                     // no var specified
   }

  afile=''; newvars='';
  gvars=' '+gvars+' ';


  if (doall==1 ) newvars=readGeneralVar().join(' ') ;    // doall means Use All General variables
  gvars2=getWord(gvars);

 for (m=0 ;m<gvars2.length;m++) {                        // expand gvars ($varname expansion)
             aword=strip(gvars2[m].toUpperCase());
             if (aword=='*')  {
                  newvars=newvars+' '+readGeneralVar().join(' ');
                  continue ;
             }

             if (aword=='*CHANGE.G' || aword=='*C.G' || aword=='*C' || aword=='*CHANGE' )  {
                  tlist=readGeneralVar();
                  tnew='';
                  for (var itt=0;itt<tlist.length;itt++) {
                      aword=tlist[itt] ;
                      if (makeGuEqual(aword)==1)  tnew=tnew+' '+aword;
                  }
                if (tnew!=='') writeDataMessage('General variables changed='+tnew,3);
                 newvars=newvars+' '+tnew;
                  continue ;
             }

             if (aword=='*.R') aword='$$RESULTS' ;  // $RESULTS is saved when resultsData is created
             if (aword=='*.F') aword='$$SUBMITFORMLIST' ;  // $SUBMITFORMLIST is added to when a FORM is submitted
             if (aword=='*.A') {
                  for (var mm=0;mm<currentParticipants['$VARS'].length;mm++) {
                      newvars=newvars+' '+currentParticipants['$VARS'][mm]+'.A ';
                  }
                  continue ;
             }        
             if (aword.substr(0,1)=='$') {                      // expand! For example, $SUBMITFORMLIST for all variables "changed" by FORMs
                 tmp=getVarInfo(aword.substr(1),'value','CONTROL') ;
                 newvars=newvars+' '+tmp ;
              } else {
                 newvars=newvars+' '+aword;
              }
 }

// newvarsb is the expanded list of general/results variables to use. Might include duplicates
 newvarsb=getWord(newvars);

 var gotlist={} ;
 didaddr=0;

 for (var m=0;m<newvarsb.length;m++) {                  // create
         aline=''; 
         aword=strip(newvarsb[m].toUpperCase());
         if (typeof(gotlist[aword])!='undefined') continue ;  //  already wrote

         gotlist[aword]=1;

         if (aword.indexOf('.R')>-1) {                    // a resultsData parameters?
             var goo=parseAt(aword,'.');  aword=goo[0];
             aval=''; around=''; sarray='';
             if (typeof(resultsData['*'][aword])!='undefined') {
                  tval=resultsData['*'][aword];
                  if (typeof(tval)=='object' && typeof(tval.length)!='undefined') {             // is an array
                        aval=[];
                        sarray=' array="0" ' ;
                        for (var mm=0;mm<tval.length;mm++) aval[mm]=escape(tval[mm]);
                        aval=aval.join(',');
                  } else {
                      aval=escape(resultsData['*'][aword]);
                  }
             }
             if (typeof(resultsData['$ROUND'])!='undefined')  around=escape(resultsData['$ROUND']);

// check for [ in word (a single element of an array)
            if (aword.indexOf('[')<0) {                 // not a x[i] syntax             
                 aline=aline+'        <'+aword+'.'+around+sarray+'>'+aval+'</'+aword+'.'+around+'> \n '; 
            } else {                                    // an array element
                avv=parseUsing(aword,'[',']',1);
                aword=avv[0];
                sarray=' element="'+avv[1]+'" ';
                aline=aline+'        <'+aword+'.'+around+sarray+'>'+aval+'</'+aword+'.'+around+'> \n '; 
            }

         } else {                                     //  ::::: a generaldata

            sarray=' ' ;
            if (aword.indexOf('[')>-1) {                 // check for a x[i] syntax             
                avv=parseUsing(aword,'[',']',1);
                aword=avv[0]; 
                vinfo=getVarInfo(aword,'','GENERAL');
                aval=vinfo.value[avv[1]];  isarray=0 ;    
                useword=vinfo.name ;
                sarray=' element="'+avv[1]+'" ';
            }  else {                           
               vinfo=getVarInfo(aword,'','GENERAL');
               aval=vinfo.value;  isarray=vinfo.array ;  
               useword=vinfo.name ;
            }


  // values of general variables that were written to a postcap (used by *C)
            if (typeof(generalVars['$lastSaved'])=='undefined') generalVars['$lastSaved']={};
             generalVars['$lastSaved'][useword]=aval ;          // used by *CHANGE.G

             aline='       <'+useword +' '+sarray+' ' ;
             if (isarray==1) {
                 var newval=[] ;
                 aline=aline+' array="0" ';                     // 0 means is an array, and don't add offset
                 for (var m0=0;m0<aval.length;m0++) newval[m0]=escape(aval[m0]);   // might also write empty 0th, etc elements (if offset was added)
                 newval=newval.join(',');
             } else {
                var newval=escape(aval);
             }
             aline=aline+'>'+newval+'</'+useword+'> \n ';
         }

         afile=afile+aline

   }                                    // newvarsb  gnode
   return afile ;
} // readG


//====================
// check to see if a general variable has changed since last time saved
// (if it doesn't exist in last time saved, it's changed
function  makeGuEqual(aword) {

   var vinfo,oldvals,isarray,ii;

    if (typeof(generalVars['$lastSaved'])=='undefined' || typeof(generalVars['$lastSaved'][aword])=='undefined') return 1  ;

    vinfo=getVarInfo(aword,'','GENERAL');
    newvals=vinfo.value;


   if (typeof(generalVars)!=='undefined' && typeof(generalVars['$lastSaved'])!=='undefined') {
       oldvals=generalVars['$lastSaved'][aword] ; 
    }

    if (typeof(newvals)!==typeof(oldvals)) return 1 ;

    if (typeof(newvals)!=='object' && typeof(oldvals)!=='object') {
         if (newvals==oldvals) return 0 ;
        return 1;
    }
    if (typeof(newvals.length)=='undefined' || typeof(oldvals.length)=='undefined') return 1 ;
    if (newvals.length !== oldvals.length) return 1 ;
    for (var ii=0; ii<newvals.length;ii++) {
        if (newvals[ii]!==oldvals[ii]) return 1 ;
    }
    return 0 ;


}

// ============= make xml: from <MESSAGE>. Return string containing xml of the message variables
//  anode contains instructions on what variables to use
// note use of escaping (especially for arrays, where elements are escaped and commas seperate these escaped values)
// doall=1 means "save all variables". This does NOT include resultsData values (since doall is only used to make SNAPSHOT files

function makeGUparametersXMLMessage(gnode,doall,isPostCap) {
  var gvars,m,newvarsb,gvars2,aword,tmp,newvars,afile,aline,useword,isarray,aval,m0 ;
  var sarray,tval,mm,avv,vinfo,tmp,avar,aid,ddavar;
  var j,mopts,ncp,goo,atmp ;

  gvars='';

  if (arguments.length<2) doall=0;

  gvars=E2_getNodeText(gnode);
  if (gvars=='') return '' ;                     // no var specified
  afile=''; newvars='';
  gvars=' '+gvars+' ';
  gvars2=getWord(gvars);

// first: the message_options
   mopts=getVarInfo('message_options.c','value','control');
   aline='        <message_options type="1">'+escape(mopts)+'</message_options>';

   afile=afile+aline+'\n';

  if (doall==1) gvars2=['*'];
  ncp=currentParticipants['$VARS'].length;
 if (ncp==0) {
     if (isPostCap==1) {
        writeDataMessage('Warning: &lt;postCap&gt; action with &lt;message&gt;: but currentParticipants database is empty!\n '+
                                'You can use the <span style="color:blue">participants - update</span> generic option to read participant information \n'+
                                ' from the server and then create currentParticipants');
                dsurveyFocus('_topBar_'); 
     } else {
        writeDataMessage('Warning: there are no currentParticipants variables! ');
     }
 }
 for (m=0 ;m<gvars2.length;m++) {                        // expand gvars ($varname expansion)
             aword=strip(gvars2[m].toUpperCase());
             if (aword=='*')  {
                  for (var mm=0;mm<ncp;mm++) {
                      newvars=newvars+' '+currentParticipants['$VARS'][mm]+'.P ';
                  }
                  newvars=newvars+' ID.P ';
                  continue ;
             }
             if (aword=='*.P') {
                  for (var mm=0;mm<ncp;mm++) {
                      newvars=newvars+' '+currentParticipants['$VARS'][mm]+'.P ';
                  }
                  newvars=newvars+' ID.P ';
                  continue ;
             }        
             newvars=newvars+'  '+aword;         // not a * type of word
 }

// newvarsb is the expanded list of general/results variables to use. Might include duplicates
 newvarsb=getWord(newvars);
 var gotlist={} ;

 if (typeof(messageData['$capVars'])=='undefined') { 
    messageData['$capVars']={};                    // these are cumulative
    messageData['$capVars']['$list']=[];           // viewMessageData() will display these values
    messageData['$capVars']['$ct']=0;           // viewMessageData() will display these values
 }

 for (var m=0;m<newvarsb.length;m++) {                  // create
         aline=''; 
         aword=strip(newvarsb[m].toUpperCase());
         if (typeof(gotlist[aword])!='undefined') continue ;  //  already wrote

         gotlist[aword]=1;

         if (aword.indexOf('.P')>-1) {                    //currentParticipant var
             goo=parseAt(aword,'.');  avar=goo[0];
             if (avar=='IDS') avar='ID' ;
             if (avar!=='ID'  && typeof(currentParticipants['$VARLOOKUP'][avar])=='undefined') {
                alert('Error in makeGUparameters: no such .P variable= '+aword);
                return 0;
             }
             tmp=''; atmp=[] ;
             for (var j=0;j<currentParticipants['$IDS'].length;j++) {
                  aid=currentParticipants['$IDS'][j];
                  if (avar=='ID') {
                      am=aid;
                  } else {
                       am=strip(currentParticipants[aid][avar]);
                  }
                 if (j>0) tmp=tmp+',';
                 tmp=tmp+escape(am);
                 atmp=atmp.concat(am);
             }

            ddavar=avar.replace(/\[|\]/g,'_')
             aline='        <'+ddavar+' type="P" >'+tmp+'</'+ddavar+'> \n';   
             if (typeof(messageData['$capVars'][ddavar])=='undefined') {
                messageData['$capVars']['$list']=messageData['$capVars']['$list'].concat(ddavar);
             }
             messageData['$capVars'][ddavar]=atmp;
             messageData['$capVars']['$ct']=Math.max(messageData['$capVars']['$ct'],atmp.length);

        } else if (aword.indexOf('.U')>-1) {                    // usert var
             var goo=parseAt(aword,'.');  avar=goo[0];
             if (typeof(userData['$VARLOOKUP'][avar])=='undefined') {
                alert('Error in makeGUparameters: no such .U variable= '+aword);
                return 0;
             }
             tmp=''; atmp=[] ;
             for (var j=0;j<currentParticipants['$IDS'].length;j++) {  // note: do for currentParticipants, not users (shouldnt be a difference, but who knows
                  aid=currentParticipants['$IDS'][j];              // check for no such user. If so, use a ''
                  if (typeof(userData[aid])=='undefined' || typeof(userData[aid][avar])=='undefined') {
                    am='';
                  } else {
                     am=strip(userData[aid][avar]);
                     am=escape(am);
                  }
                  if (j>0) tmp=tmp+',';
                  tmp=tmp+am;
                  atmp=atmp.concat(am);
             }

             ddavar=avar.replace(/\[|\]/g,'_')
             aline='        <'+ddavar+' type="U" >'+tmp+'</'+ddavar+'> \n';   
             if (typeof(messageData['$capVars'][ddavar])=='undefined') {
                messageData['$capVars']['$list']=messageData['$capVars']['$list'].concat(ddavar);
             }
             messageData['$capVars'][ddavar]=atmp;
             messageData['$capVars']['$ct']=Math.max(messageData['$capVars']['$ct'],atmp.length);

         } else {                                     //  ::::: a generaldata
            alert('makeGUparametersXMLMessage error: a .U or a .P variable was not specified: '+aword+'\n'+gvars);
            return ''
         }

         afile=afile+aline

   }                                    // newvarsb  gnode
   return afile ;
}





// ============= make xml: from <USERS>. Return string containing xml of the USER variables (don't bother with surrounding elements) ...
//  anode contains instructions on what variables to use -- see sample_control.xml for the details
// variables stored to <USER> can be from userData, currentParticipants, and/or resultsData

function makeGUparametersXMLUsers(gnode,doall) {
  var gvlist,glookup,gstring,m,gvars ;
  var ids='';
  var av1,av2,m2,av1,goo,useround,av0;
  var usetype,usename,newname,varlist,afile,varlist2,aline,asArray ;
  var abase,igoo,mu,tt,wildvar,chkvs,atype,m2,thisvar;

  if (arguments.length<2) doall=0;

  if (gnode==0) {
        ids='*' ;
       asArray=[];
  } else {
      ids=strip(getAttributes(gnode,'ID','*'));
      asArray=strip(getAttributes(gnode,'asArray',''));
      if (asArray!='') {
          asArray=asArray.toUpperCase() ;
          asArray=getWord(asArray) ;
      } else {
          asArray=[] ;
      }
   }

   if (ids=='') ids='*' ;
   ids=' '+ids.toUpperCase()+' ';

   if (doall==1) {
        gvars='*'
    } else {
        gvars=E2_getNodeText(gnode,1,' ');
   }


  useround='';
  if (typeof(resultsData['$ROUND'])!='undefined') useround=strip(resultsData['$ROUND']) ;    // round to use (when appending to .R data
  useround=strip(useround.toUpperCase());

gvlist=getWord(gvars.replace(/,/g,' '));
glookup=new Object();
gstring='' ;

for (var m=0;m<gvlist.length;m++) {             // expand string
  av1=strip(gvlist[m].toUpperCase()) ;
  if (av1=='*' || av1=='*.U' ||av1=='*.D'  ) {
      for (var m2=0;m2<userData['$VARS'].length;m2++)  {  gstring=gstring+' '+strip(userData['$VARS'][m2]) } ;
   }
//writeDoStatus('vvU '+gstring);

   if (av1=='*' || av1=='*.C'  || av1=='*.P' ) {
       for (var m2=0;m2<currentParticipants['$VARS'].length;m2++) {  gstring=gstring+' '+strip(currentParticipants['$VARS'][m2]+'.C') };
   }
//writeDoStatus('vvC '+gstring);

   if (av1=='*' || av1=='*.R'  ) {
       for (var m2=0;m2<resultsData['$VARS'].length;m2++)  {  gstring=gstring+' '+strip(resultsData['$VARS'][m2]+'.R') } ;
   }
//writeDoStatus('vvR '+gstring);

   if (av1=='*' || av1=='*.U' ||av1=='*.D'  || av1=='*.C'  || av1=='*.P' ||  av1=='*.R') continue ;   
   
   if (av1.substr(0,1)=='$') {                          // expand contents of a control variable?
        av2=av1.substr(1) ;
        av2=getVarInfo(av2,'value','CONTROL') ;
        gstring=gstring+' '+av2;
        continue ;
   }
//writeDoStatus('vv$ '+gstring);

   if (av1.indexOf('*')==-1  ) {                // got a wildcard?
     gstring=gstring+' '+av1 ;                  // not a wildcard
     continue ;
   }

// if here, * as wildcard
    goo=expandVarlistWild(av1,'U',1);
    gstring=gstring+' '+goo;



}                                       // list is now expanded
gvlist=getWord(gstring) ;                 // copy to glookup (this removes duplicates)


for (var m=0;m<gvlist.length;m++)   {
   usetype='';
   av1=gvlist[m]; av1=strip(av1.toUpperCase()) ;
   goo=av1.split('=');
   if (goo.length==1){                  // not a rename
       goo=av1.split('.');
       if (goo.length==1 || goo[1]=='') {    // not a x.n variable
          usetype='U';                         //userData is the default
          usename=goo[0];                     // get rid of of the .
          newname=usename;
       } else {                          // check .x
           if (!isNaN(goo[1]))  continue ;        // .n stuff may exist if information read from snapshot file (since priorresults are saved in USERS area)
                                                // if this is the case, just skip this variable

            usename=goo[0];                     // get rid of of the .
            newname=usename;

            if (goo[1]=='U' || goo[1]=='D')  usetype='U';
            if (goo[1]=='C' || goo[1]=='P')  usetype='C';
            if (goo[1]=='R')   usetype='R'
            if (usetype=='') {
                alert('ERROR in makeGUParametersXMLUsers -- bad type in varlist: '+av1+' ('+gvlist.join(' | '));
                writeDoStatus('ERROR in makeGUParametersXMLUsers -- bad type in varlist: '+goo+'|'+av1+' ('+gvlist.join(' | '),0,'INTER');
                return 1;
            }

            if (usetype=='C') newname='_'+newname ;                       // prepend a _
            if (usetype=='R') {
               newname=newname+'.'+useround ;          // default is to append a .RoundNumber
               if (asArray.length>0) {                                        // asArray renaming
                  tt=strip(usename.toUpperCase());
                  for (var mu=0;mu<asArray.length;mu++) {
                      abase=asArray[mu];
                      if (abase==tt.substr(0,abase.length)) {            // match
                           newname=usename.substr(0,abase.length)+'['+usename.substr(abase.length)+'].'+useround;
                           break;
                      }                                          // abase==newname
                  }                                                     // mu
               }                                                          // asArray
            }                                                       // usetype=r
       }                                                  // default .type
   } else {                                             //
      newname=goo[0];                                           // note: no _ will be prepended (so add one explicitily if need be)
      newname=newname.replace('.R','.'+useround);                  // only append .roundNum to newname if a .R is in the newname

      usename=goo[1];
      goo=usename.split('.');
      usename=goo[0];

      if (goo.length==1 || goo[1]=='U' || goo[1]=='D')  usetype='U';
      if (goo[1]=='C' || goo[1]=='P')  usetype='C';
      if (goo[1]=='R')   usetype='R'
   }
   glookup[newname]={};
   glookup[newname].usetype=usetype ;
   glookup[newname].usename=usename ;
}                                               // vars in gvlist

varlist2=''; 
gvlist=[];
for  (var m in glookup) {                       // make list of variables to create
  if (varlist2!=='') varlist2=varlist2+','
  varlist2=varlist2+m ;
  gvlist=gvlist.concat(m);
}

afile='    <VARS>'+ varlist2  + '</VARS> \n ';
afile=afile+'    <VALS delim=","> \n ';

for (var m=0; m<currentParticipants['$IDS'].length ;m++) {
     aid=strip(currentParticipants['$IDS'][m].toUpperCase());
     if (ids!=' * ' && doall!=1) {                             // check agains list of ids
           if (ids.indexOf(' '+aid+' ')<0) continue ;                      // don't do this id
     }
     aline='      <user id="'+aid+'">';
     for (var m2=0;m2<gvlist.length;m2++) {
          aword=gvlist[m2] ;
          aval='' ;
          usename=glookup[aword].usename ;
          usetype=glookup[aword].usetype ;
          if (usetype=='U')  {                 //  userData
               if (typeof(userData[aid])!='undefined' && typeof(userData[aid][usename])!='undefined') aval=userData[aid][usename] ;
          }
          if (usetype=='C')  {                 //  currentParticipants
               if (typeof(currentParticipants[aid][usename])!='undefined') aval=currentParticipants[aid][usename] ;
          }
          if (usetype=='R')  {                 //  resultsData
               if (typeof(resultsData[aid])!='undefined' && typeof(resultsData[aid][usename])!='undefined') aval=resultsData[aid][usename] ;
          }
          aline=aline+escape(aval) ;                   // escape to avoid problems
          if (m2<gvlist.length-1) aline=aline+',';
       }
       aline=aline+'     </user> \n ';
       afile=afile+aline ;
  }
  afile=afile+'    </VALS> \n ' ;
  return afile ;
}



// ============= make xml:   Return string containing xml of the CURRENTPARTICIPANT variables (don't bother with surrounding elements) ...
//  anode contains instructions on what variables to use

function makeGUparametersXMLcurrentParticipants(gnode,doall) {
  var gvars,m,m2,newvarsb,gvars2,aword,tmp,newvars,afile,aline,useword,isarray,aval,ndo ;
  var ids='';
  gvars='';

  if (arguments.length<2) doall=0;
  
  if (doall!=1) {
    ids=strip(getAttributes(gnode,'ID','*'));
    if (ids=='') ids='*' ;
    ids=' '+ids.toUpperCase()+' ';
    gvars=E2_getNodeText(gnode,1,' ');
   }

   if (gvars.indexOf('*')>-1 || doall==1 ) {
        gvars2=currentParticipants['$VARS'] ;
   } else {
       gvars2=getWord(gvars);  
   }
   newvars='';
   for (m=0 ;m<gvars2.length;m++) {                        // expand gvars ($varname expansion)
             aword=strip(gvars2[m]);
             if (aword.substr(0,1)=='$') {                      // expand!
                 tmp=readGeneralVar(aword);
                 newvars=newvars+' '+tmp ;
              } else {
                 newvars=newvars+' '+aword;
              }
  }

  newvarsb=getWord(newvars.toUpperCase());

  afile='    <VARS>'+ newvarsb.join(',')  + '</VARS> \n ';
  afile=afile+'    <VALS delim=","> \n ';
  
  ndo=currentParticipants['$IDS'].length ;
  for (var m=0; m<ndo;m++) {
        aid=strip(currentParticipants['$IDS'][m].toUpperCase());
        if (ids!=' * ' && doall!=1) {                             // check agains list of ids
           if (ids.indexOf(' '+aid+' ')<0) continue ;                      // don't do this id
        }
        aline='      <user id="'+aid+'">';
        for (var m2=0;m2<newvarsb.length;m2++) {
           aword=strip(newvarsb[m2].toUpperCase());
           aval='' ;
           if (typeof(currentParticipants[aid][aword])!='undefined') aval=currentParticipants[aid][aword] ;
           aline=aline+aval ;
           if (m2<newvarsb.length-1) aline=aline+',';
       }
      aline=aline+'     </user> \n ';     
      afile=afile+aline ;
  }
  afile=afile+'    </VALS> \n ' ;
  return afile ;
}

 
// ============= make xml: from <REFERENCE>. Return string containing xml of the REFERENCE variables (don't bother with surrounding elements) ...
//  anode contains instructions on what variables to use

function makeGUparametersXMLReference(gnode,doall) {
  var gvars,m,m2,newvarsb,gvars2,aword,tmp,newvars,afile,aline,useword,isarray,aval,ndo ;
  var ids='';
  gvars='';

  if (arguments.length<2) doall=0;

  if (doall!=1) {  
    ids=strip(getAttributes(gnode,'ID','*'));
    if (ids=='') ids='*' ;
    ids=' '+ids.toUpperCase()+' ';
    gvars=E2_getNodeText(gnode,1,' ');
   }
  

  if (gvars.indexOf('*')>-1 || doall==1 ) {
        gvars2=refData['$VARS'] ;
   } else {
       gvars2=getWord(gvars);  
   }
   newvars='';
   for (m=0 ;m<gvars2.length;m++) {                        // expand gvars ($varname expansion)
             aword=strip(gvars2[m]);
             if (aword.substr(0,1)=='$') {                      // expand!
                 tmp=readGeneralVar(aword);
                 newvars=newvars+' '+tmp ;
              } else {
                 newvars=newvars+' '+aword;
              }
  }

  newvarsb=getWord(newvars.toUpperCase());

  afile='    <VARS>'+ newvarsb.join(',')  + '</VARS> \n ';
  afile=afile+'    <VALS delim=","> \n ';
  
  ndo=refData['$IDS'].length ;
  for (var m=0; m<ndo;m++) {
        aid=strip(refData['$IDS'][m].toUpperCase());
        if (ids!=' * ' && doall!=1) {                             // check agains list of ids
           if (ids.indexOf(' '+aid+' ')<0) continue ;                      // don't do this id
        }
        aline='      <row id="'+aid+'">';  
        for (var m2=0;m2<newvarsb.length;m2++) {
           aword=strip(newvarsb[m2].toUpperCase());
           aval='' ;
           if (typeof(refData[aid][aword])!='undefined') aval=refData[aid][aword] ;
           aline=aline+escape(aval) ;
           if (m2<newvarsb.length-1) aline=aline+',';
       }
      aline=aline+'     </row> \n ';     
      afile=afile+aline ;
  }
  afile=afile+'    </VALS> \n ' ;
  return afile ;
}


//
// Display  current list of users, and reference data. This does NOT save anything... it just displays 
function reviewUsersCurrent(divid) {
var iid,atd,aval,atable,abody,atr,m,m2,aword,vv2;

if (arguments.length<1) divid=actionDiv ;

// got user list (either from server, or using currentParticipants
   iid=document.getElementById(divid);
   if (!iid) {
        alert('Error in reviewUsers: no such display-div id= '+divid);
        return 1;
   }
   iid.innerHTML='';
   atd=document.createElement('h3');
   aval=document.createTextNode('List of known participants ');
   atd.appendChild(aval);
   iid.appendChild(atd);

   atd=document.createElement('div');
   aval=document.createTextNode('Note: if new participants have joined (since the last time you retrieved the list of current participants), this list of known participants will be out of date.');
   atd.appendChild(aval);
   iid.appendChild(atd);

   atable=document.createElement('table');  
   atable.setAttribute('border',1);
   atable.style.marginTop='5px';
   abody=document.createElement('tbody');
   atr=document.createElement('tr');

   atd=document.createElement('th');
       aval=document.createTextNode(' ');
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
        atr.appendChild(atd);

   atd=document.createElement('th');
       aval=document.createTextNode('ParticipantID'); 
        atd.appendChild(aval);
        atr.appendChild(atd);
  for (var m=0 ; m<currentParticipants['$VARS'].length ; m++) {                    // headers
        avar=strip(currentParticipants['$VARS'][m]);
        atd=document.createElement('th');
           aval=document.createTextNode(avar);
        atd.appendChild(aval);
        atr.appendChild(atd);
  }
  abody.appendChild(atr);


 for (var m=0;m<currentParticipants['$IDS'].length;m++) {  
        
        am=currentParticipants['$IDS'][m];
        ith=currentParticipants[am]['ID'];

        atr=document.createElement('tr');

      atd=document.createElement('th');
         aval=document.createTextNode(1*m+1);
          atd.appendChild(aval);
          atd.style.backgroundColor="cyan";
          atr.appendChild(atd);

           atd=document.createElement('td');
           atd.style.backgroundColor='#acacbc';
             aval=document.createTextNode(am);
             atd.appendChild(aval);
        atr.appendChild(atd);

       for (var m2=0 ; m2<currentParticipants['$VARS'].length ; m2++) {                    // variables for this user
          atd=document.createElement('td');
            aword=currentParticipants['$VARS'][m2];
            vv2=currentParticipants[am][aword];
            if (vv2=='') vv2=' ';
            aval=document.createTextNode(vv2);
            atd.appendChild(aval);
          atr.appendChild(atd);
       }
       abody.appendChild(atr);

     }



  atable.appendChild(abody);                    // done with table
  iid.appendChild(atable);
  return 1;

}
// userData
// =========================== 
// obtain current list of users, and display. 
//  Typically, this does NOT save anything... it just displays
//  However, if divid is a -1, then no display.  In this case, the userListNode global points to an XML file with the client info in it:
//
function reviewUsersServer(divid,ido) {

var am,aname,acontact,m,tt,atable,abody,atd,atr,iid,gnode,agnode,bnode,cnode,m2,varlist;
var anode,am,vv,vv2;

if (arguments.length<2) ido=1 ;
if (arguments.length<1) divid=actionDiv ;


if (ido==1 ) {
   toGet=getUserListURL+'session='+sessName;
   Get_xmlFile(toGet,'userListNode','retrieving an user list','reviewUsersServer',divid,2);
   return 1 ;
 }

if (divid==-1) return 1 ;                       // and userListNode can be used


// got user list (either from server, or using userData
   iid=document.getElementById(divid);
   if (!iid) {
        alert('Error in reviewUsers: no such display-div id= '+divid);
        return 1;
   }
   iid.innerHTML='';

  userStuff=toArray_userListNode(userListNode);
  if (typeof(userStuff)=='string') {
     iid.innerHTML='<b>Error</b>: '+userStuff;
     return 0
  }

  varlist=userStuff[0] ;
   atd=document.createElement('h3');
   aval=document.createTextNode('List of participants: from server ');
   atd.appendChild(aval);
   iid.appendChild(atd);
     
   atd=document.createElement('span');
   aval=document.createTextNode('Note: this is the current list of participants enrolled in the session, as reported by the server. If there have been recent enrollments, this may not match the internal (within this contol panel) list of participants.');
   atd.appendChild(aval);
   iid.appendChild(atd);

   atable=document.createElement('table');  
   atable.setAttribute('border',1);
   atable.style.marginTop='6px';
   abody=document.createElement('tbody');
   atr=document.createElement('tr');

   atd=document.createElement('th');
       aval=document.createTextNode(' ');
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
        atr.appendChild(atd);

   atd=document.createElement('th');
       aval=document.createTextNode('PartcipantID');
        atd.appendChild(aval);
        atr.appendChild(atd);

  for (var m=0 ; m<varlist.length ; m++) {
        avar=strip(varlist[m]);
        atd=document.createElement('th');
           aval=document.createTextNode(avar);
        atd.appendChild(aval);
        atr.appendChild(atd);
  }
  abody.appendChild(atr);

 for (var m=1;m<userStuff.length;m++) {  
      vv2=userStuff[m];
      atr=document.createElement('tr');
         atd=document.createElement('th');
           aval=document.createTextNode(1*m);
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
       atr.appendChild(atd);
           atd=document.createElement('td');
           atd.style.backgroundColor='#acacbc';
             aval=document.createTextNode(vv2[0]);
           atd.appendChild(aval);
       atr.appendChild(atd);

       for (var m2=0 ; m2<varlist.length ; m2++) {
          atd=document.createElement('td');
            aval=document.createTextNode(vv2[m2+1]);    // since vv[0]=id
            atd.appendChild(aval);
          atr.appendChild(atd);
       }
       abody.appendChild(atr);

     }
     atable.appendChild(abody);
     iid.appendChild(atable);
    return 1;
}


// =========================== 
// obtain current list of users, and display. 
//  Typically, this does NOT save anything... it just displays
// However, if divid is a -1, then no display.  In this case, the userListNode global points to an XML file with the client info in it:
//
function reviewUsersBoth(ido) {
var idServer,idServerList,cIds,currId,tago;
var cIdsObject=new Object() ;
var iid,toGet,serverList,atd,aval,atable,abody,atd,ath,atr,aspan,adiv,m,vv2;

if (arguments.length<1) ido=1 ;

// first: get list from server
if (ido==1 ) {
   toGet=getUserListURL+'session='+sessName;
   Get_xmlFile(toGet,'userListNode','retrieving an user list','reviewUsersBoth',2);
   return 1 ;
 }

iid=document.getElementById('_controlStatus');

if (ido==1 ) {
   toGet=getUserListURL+'session='+sessName;
   Get_xmlFile(toGet,'userListNode','retrieving an user list','reviewUsersBoth',divid,2);
   return 1 ;
 }

  serverList=toArray_userListNode(userListNode);

  userStuff=toArray_userListNode(userListNode);
  if (typeof(userStuff)=='string') {
     iid.innerHTML='<b>Error</b>: no USERS section in the user-list XML file ';
     return 0
  }

  iid.innerHTML='';

  varlist=userStuff[0] ;

  cIds=currentParticipants['$IDS'];
  for (var m=0 ;m<cIds.length;m++) {
        cIdsObject[cIds[m]]=1;
  }
  cvarlist=[];
  if (typeof(currentParticipants)!='undefined' || typeof(currentParticipants['$VARS'])!='undefined') cvarlist=currentParticipants['$VARS'];

   atd=document.createElement('h3');
   aval=document.createTextNode('List of participants: currently on server, and currently stored in currentParticipants ');
   atd.appendChild(aval);
   iid.appendChild(atd);
     
   atable=document.createElement('table');  
   atable.setAttribute('border',1);
   atable.style.marginTop='6px';
   abody=document.createElement('tbody');

// header 1
   atr=document.createElement('tr');
   atd=document.createElement('td');
   aval=document.createTextNode('  ');
   atd.setAttribute('bgcolor','cyan');
        atr.appendChild(atd);

   atd=document.createElement('td');
   aval=document.createTextNode('... ');
   atd.setAttribute('colspan',varlist.length+1);
   atd.setAttribute('bgcolor','cyan');
        atr.appendChild(atd);

   tago=new Date().getTime() ;
   tago=Seconds_ToTime(Math.ceil( (tago-currentParticipants['$TIME'])/1000)) ;
   if (currentParticipants['$TIME']=='') tago=' [not loaded yet]';
   aval=document.createTextNode(' The currentParticipants control-panel-database was updated  '+tago+' ago ');
   atd=document.createElement('td');
   atd.style.fontVariant='small-caps';
   atd.setAttribute('colspan',cvarlist.length+1);
   atd.appendChild(aval);
   atr.appendChild(atd);
   abody.appendChild(atr);


// from Server, From controlpanel (header 2)
   atr=document.createElement('tr');

   atd=document.createElement('td');
   aval=document.createTextNode('  ');
   atd.setAttribute('bgcolor','cyan');
        atr.appendChild(atd);

   atd=document.createElement('td');
   aspan=document.createElement('em');
   aval=document.createTextNode('From server ');
   aspan.appendChild(aval);
   atd.appendChild(aspan);
   atd.setAttribute('colspan',varlist.length);
        atr.appendChild(atd);

   atd=document.createElement('td');
   aval=document.createTextNode('  ');
   atd.setAttribute('bgcolor','lime');
        atr.appendChild(atd);

   atd=document.createElement('td');
   aspan=document.createElement('em');
   aval=document.createTextNode('Current value of the currentParticipants control-panel database  ');
   aspan.appendChild(aval);
   atd.appendChild(aspan);
   atd.setAttribute('colspan',cvarlist.length+1);
        atr.appendChild(atd);

   abody.appendChild(atr);

   atr=document.createElement('tr');
   atd=document.createElement('th');
       aval=document.createTextNode(' ');
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
        atr.appendChild(atd);

   atd=document.createElement('th');
       aval=document.createTextNode('ParticipantID');
        atd.appendChild(aval);
        atd.style.fontStyle='italic';
        atr.appendChild(atd);

  for (var m=1 ; m<varlist.length ; m++) {              // varlist[0] is always id
        avar=strip(varlist[m]);
        atd=document.createElement('th');
           aval=document.createTextNode(avar);
        atd.appendChild(aval);
        atr.appendChild(atd);
  }
  atd=document.createElement('td');
    aval=document.createTextNode('  ');
    atd.setAttribute('bgcolor','lime');
  atr.appendChild(atd);

   atd=document.createElement('th');
       aval=document.createTextNode('ID');
        atd.appendChild(aval);
        atd.style.fontStyle='italic';
        atr.appendChild(atd);
   for (var m=0 ; m<cvarlist.length ; m++) {            // headers for currentParticipants 
        avar=strip(cvarlist[m]);
        atd=document.createElement('th');
           aval=document.createTextNode(avar);
        atd.appendChild(aval);
        atr.appendChild(atd);
  }

  abody.appendChild(atr);                       // end of header rows

// now write out data for each participant returned by server 

 idServerList=new Object() ;
 var rowct=0;
 for (var m=1;m<userStuff.length;m++) {  
      vv2=userStuff[m];
      idServer=vv2[0];
      idServerList[idServer]=1;
      atr=document.createElement('tr');
         atd=document.createElement('th');
           rowct++  ;
           aval=document.createTextNode(rowct);
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
        atr.appendChild(atd);
           atd=document.createElement('td');
           atd.style.backgroundColor='#cacadc';
             aval=document.createTextNode(idServer);
           atd.appendChild(aval);
       atr.appendChild(atd);

       for (var m2=1 ; m2<varlist.length ; m2++) {
          atd=document.createElement('td');
            aval=document.createTextNode(vv2[m2]);    // since vv[0]=id
            atd.appendChild(aval);
          atr.appendChild(atd);
       }
       atd=document.createElement('td');
       aval=document.createTextNode('  ');
       atd.setAttribute('bgcolor','lime');
        atr.appendChild(atd);

// now check for match in currentParticipants
      if (typeof(cIdsObject[idServer])!='undefined') {      
         atd=document.createElement('td');
            atd.style.backgroundColor='#cacadc';
            aval=document.createTextNode(idServer);
         atd.appendChild(aval);
         atr.appendChild(atd);
         for (var m2=0 ; m2<cvarlist.length ; m2++) {
            atd=document.createElement('td');
            avar=cvarlist[m2];
            aval=document.createTextNode(currentParticipants[idServer][avar]) ;
            atd.appendChild(aval);
            atr.appendChild(atd);
         }
      } else {
         atd=document.createElement('td');
         aval=document.createTextNode('No match in currentParticipants ' );
         atd.setAttribute('colspan',cvarlist.length+1);
            atd.appendChild(aval);
            atr.appendChild(atd);
      }
      abody.appendChild(atr);                  // append row to tbody of table
     }

// now check if any current users are NOT in server list 
    for (var m=0; m<cIds.length;m++) {
      currId=cIds[m];;
      if (typeof(idServerList[currId])!='undefined') continue ;         // exists, so its been written
      atr=document.createElement('tr');                                 // if here, write a rowwith empty stuff in server section

       atd=document.createElement('th');
        rowct++ ;
        aval=document.createTextNode(rowct);
        atd.appendChild(aval);
        atd.style.backgroundColor="cyan";
        atr.appendChild(atd);

       atd=document.createElement('td');
         aval=document.createTextNode('No match in server supplied list ' );
         atd.setAttribute('colspan',varlist.length+1);
            atd.appendChild(aval);
            atr.appendChild(atd);

       atd=document.createElement('td');
        aval=document.createTextNode('');
        atd.appendChild(aval);
        atd.style.backgroundColor="lime";
        atr.appendChild(atd);

         atd=document.createElement('td');
            atd.style.backgroundColor='#cacadc';
            aval=document.createTextNode(currId);
         atd.appendChild(aval);
         atr.appendChild(atd);

         for (var m2=0 ; m2<cvarlist.length ; m2++) {
            atd=document.createElement('td');
            avar=cvarlist[m2];
            aval=document.createTextNode(currentParticipants[currId][avar]) ;
            atd.appendChild(aval);
            atr.appendChild(atd);
         }

        abody.appendChild(atr);                  // append row to tbody of table

     }                                  // check of all currentParticipants records
              
     atable.appendChild(abody);
     iid.appendChild(atable);
    return 1;
}

// =-======================================
// reset, or add to, currentParticipants, using a  node containing an XML file of the current participant info,
// as returned from server in response to a getUserListURL request
// return space delimited list of ids, space delimited list of names (2 element array) 

function parseUserListNode(anode,doreset) {             // save variables  to currentParticipants
var gnode,userlist,m,m2,avar,aid;
var addrlist='',namelist='',am,aname;

if (arguments.length<1) doreset='' ;
if (doreset=='RESET') {
    currentParticipants=new Object() ;             //  each object is an ID of a participant currently enrolled in this session -- currentParticipants[anid] can contain clientId, referenceId, contact, nickname
    currentParticipants['$IDS']=[] ;                      // array of IDS defined in userData
    currentParticipants['$VARS']=[] ;                  // array of user variable names 
    currentParticipants['$VARLOOKUP']=new Object();                  // object of user variable names 
    currentParticipants['$TIME']=new Date().getTime() ;                      // getTime time currentParticipants last updated
}

userList=toArray_userListNode(userListNode);       // parse to an array
// copy variables
for (var m=1;m<userList[0].length;m++) {
  avar=strip(userList[0][m].toUpperCase());
  currentParticipants['$VARS']= currentParticipants['$VARS'].concat(avar);
  currentParticipants['$VARLOOKUP'][avar]= 1 ;
}
for (var m=1;m<userList.length;m++) {
   aid=userList[m][0] ;
   currentParticipants['$IDS']=currentParticipants['$IDS'].concat(aid);
   currentParticipants[aid]={};
   for (var m2=1;m2<userList[m].length;m2++) {
        avar=currentParticipants['$VARS'][m2-1];
        currentParticipants[aid][avar]=userList[m][m2];
  }
}


for (var m=0;m<currentParticipants['$IDS'].length;m++) {
        am=currentParticipants['$IDS'][m];
        addrlist=addrlist+' '+am ;
        aname=currentParticipants[am]['NAME'];
        namelist=namelist+' '+aname ;
}
return [addrlist,namelist];



}

// =-======================================
// update currentParticipants 
function updateCurrentParticipants(ido,callwhat) {
 var toGet,gnode,tt,aname,am;
 if (arguments.length<1) ido=1 ;
 if (arguments.length<2) callwhat='' ;

if (ido==1) {
  writeDoStatus('About to update current participant-list','','RESET INTER',' update participants');
   toGet=getUserListURL+'session='+sessName ;
   Get_xmlFile(toGet,'userListNode','retrieving an user list','updateCurrentParticipants',2,callwhat);
   return 1 ;
}

// here after retrieval of userlist xml file (as stored in userListNode)

  tt=parseUserListNode(userListNode,'RESET') ;             // save variables  to currentParticipants
/* DEPRECATED  controlVars['$ADDRESSES']=tt[0];                 */
  writeDoStatus('Current participant-list retrieved. # of current participants='+currentParticipants['$IDS'].length+': <tt>'+tt[1]+'</tt>','','RESET ','retrieve participant list');
  if (callwhat!=='') {Launch_Proc(callwhat,10,1); } ;
  return 1 ;

} 


// =-======================================
// parse an XML file containing a user list (as returned by server). 
// Return an array, 1 row per "user". Each row is an array
// The 0'th row is an array of variable names
// row 1 ... are the "user" rows, with variables matching the 0th row
// column 0 is always the id. 
// If a string, it's an error message. 
// NOTE: this is similar to parseVarNodeUR(.,'currentParticipants'). 
//  However, it creates an array and returns it -- it does NOT   change the currentParticipants global


function toArray_userListNode(uNode) {

var tmp,m,anode,aid,alist,m2,res ;
var varname,vlist,av,ii,avar,aval,m1 ;


gnode=E2_getNodes(uNode,'USER');                // participant list has <USER> as top level nodes (not embedded in  <users>) .

var tmp={};                     // create an object with variables, and ids (use this to build an array)
tmp['$vars']={};
tmp['$ids']={} ;
for (var m=0;m<gnode.length;m++) {  
     anode=gnode[m];
     aid=getAttributes(anode,'id','');
     if (aid=='') continue ;                    //no id, skip
     tmp['$ids'][aid]=1; 
     tmp[aid]={} ;
     alist=E2_getNodes(anode,'*');
     for (var m2=0;m2<alist.length;m2++) {
        varname=alist[m2].nodeName.toUpperCase() ;
        tmp['$vars'][varname]=1;
        tmp[aid][varname]=E2_getNodeText(alist[m2],1,'');
     }
}

res=[]; res[0]=[];
vlist=[]; vlist[0]='ID' ;
for (var av in tmp['$vars'])  vlist=vlist.concat(av)

for (var m=0;m<vlist.length;m++) res[0][m]=vlist[m];            // 0 element of returned array is the array of varnames

for (var aid in tmp['$ids'])  {                         // 1 .. elements contain the data for record, as an array. 0 element of this array is id, others match above varlist
   ii=res.length;
   res[ii]=[];
   res[ii][0]=aid ;
   for (var m=1;m<vlist.length;m++) {
       avar=vlist[m];
       aval='';
       if (typeof(tmp[aid][avar])!='undefined') aval=tmp[aid][avar];
       res[ii][m]=aval
   }
}
return res ;

}


// =========================== 
// WRite a message to the whereWrite and whereWriteHistory divs 
// message: message to write to current results, and to save to status history
// messageHeader: a header to add to message when writing to current results (but NOT used when saveing to status history)
//  aoptions: space delimited list of options (case insensitive):
//       NOWRITE : do NOT write message to whereWrite (write to history only perhaps)
//       RESET : reset: clear whereWrite, and add a timestamp to whereWriteHistory
//       NOSAVE : do NOT save to status history 
//       WINDOW  : save current contents of whereWrite to new window
//       FINAL  :  add a "FINAL="1" attribute, and a style="display:block".
//       INTER  :  check priorFinalOnly. If 1, add a style="display:none". Used to suppress display of intermediate messages

function writeDoStatus(message,messageHeader,aoptions,resetMessage) {

  var wmessage,amm,doreset=0,nosave=0,nowrite=0,ww,towindow=0 ;
  var whereWrite,whereWriteHistory ;
  var iwhere,iwhereHistory ;
  var fin,dofinal=0;
  
  whereWrite=actionDiv ;
  whereWriteHistory=actionDivHistory ;

  if (arguments.length<3) aoptions='';
        aoptions=' '+aoptions.toUpperCase()+' ';
     if (aoptions.indexOf(' RESET ')>-1) doreset=1 ;
     if (aoptions.indexOf(' NOWRITE ')>-1) nowrite=1 ;
     if (aoptions.indexOf(' NOSAVE ')>-1) nosave=1 ;
     if (aoptions.indexOf(' WINDOW ')>-1) towindow=1 ;
     if (aoptions.indexOf(' FINAL ')>-1) dofinal=1 ;
     if (aoptions.indexOf(' INTER ')>-1) dofinal=2 ;

  if (arguments.length<4) resetMessage='';


  if (nowrite!=1) {
         iwhere=document.getElementById(whereWrite);
         if (!iwhere) {
                alert('Error in writeDoStatus: no '+whereWrite+' div ');
                return 1;
          }
   }
  if (nosave!=1) {
         iwhereHistory=document.getElementById(whereWriteHistory);
         if (!iwhereHistory) {
                alert('Error in writeDoStatus: no element with an id of  '+whereWriteHistory+ ' div ');
                return 1;
          }
   }

  if (arguments.length<2 ) messageHeader='';
  wmessage=message;
  if (messageHeader!='') wmessage=messageHeader+' : ' + message;

  if (doreset==1) {
         iwhere.innerHTML='' ;
         var tt=new Date().toLocaleString()+' <span style="font-style:oblique;font-size:90%">'+resetMessage+'</span> ' ;    

         tstamp='<hr width="40%" /><div style="margin-top:12px;background-color:#ababab;text-decoration:none ;padding-left:10%; padding-right:10%;width:50%;margin-left:25;%margin-right:25%;">   '+tt+'  </div> <hr width="70%" />';
        if (nosave!=1) iwhereHistory.innerHTML='\n '+tstamp+ ' \n ' + iwhereHistory.innerHTML;
  }

  fin='<div final="0" style="display:block"  >  \n ';                // default is to display, but mark as non-final
  if (dofinal==1) fin='<div style="display:block" final="1" >  \n ' ;  // a FINAL message
  if (dofinal==2)  {
        if (typeof(priorFinalOnly)!='undefined') {
          if (priorFinalOnly==1 )   fin='<div style="display:none" final="0" >  \n ' ; // non final, perhaps suppress?
       }
  }
  if (nowrite!=1)  iwhere.innerHTML=iwhere.innerHTML+'\n '+fin+wmessage+' \n </div> ';

  if (nosave!=1) {
         iwhereHistory.innerHTML='\n <br /> \n  '+message+' \n ' + iwhereHistory.innerHTML ;
  }

  if (towindow==1) {
        message='<div style="margin-bottom:20px;border:1px gray dotted; background-color:#eaeaeb">';
        message=message+'<a style="border:1px groove blue;margin-right:10px;background-color:#dededc" href="javascript:window.close()">Close Window</a>  ';
        message=message+'<a style="border:1px groove blue;margin-right:10px;background-color:#dededc" href="javascript: history.go(-1)">Back</a>';
        message=message+'<a style="border:1px groove blue;margin-right:10px;background-color:#dededc" href="javascript: history.go(1)">Forward</a>';
        message=message+' Current results for session <b>'+sessName+'</b>: at ' + Date()+'</div> \n \n ';
        message=message+iwhere.innerHTML ;
        var dw=window.open('','dSurveyResults',"toolbar=0,menubar=0,directories=0,scrollbars=1,resizable=1" );
        dw.document.open('text/html');
        dw.document.write(message);
        dw.document.close();
    }

  return 1;
}

     
// =========================== 
// assign a "reference id" to all current users, which is used (by the RESOLVE action)
// assign values from the REFERENCE database to each user.
//   This should be called AFTER retrieving the following information from the server:
//       i) the list of this session's participants (the 'join list')
//      ii) the parameter file (or files) that contain "reference" information. Note that "reference" information
//          consists of multiple rows, where each row contains values of a common set of variables.
//   Notes:
//        * each row (in the reference database) is identified with a unique "reference-ID" variable.
//           By matching this unique reference-ID variable
//          to a users "REFID" variable, one can assign "reference" values to a particular user. 
//        * it is quite legal for multiple users to have the same reference-id,
//        * BUT it is NOT legal for multiple users to have the same "client" id, or for multiple rows in the
//          reference  database to have the same reference-ID.
//
//  The "HOW_ASSIGN"  session-specific variable (specified in the session descriptor file) controls how this
//  assignation occurs. 
//  The following values for HOW_ASSIGN are recognized: NONE 0 NAME STOP CYCLE RANDOM RANDOM_NOREP
//
//  Note: for 0 NAME STOP and CYCLE -- the reset attribute usually has no real impact (since the same values 
//        would be assigned, given that the
//        order of the list of participants, as returned by the server, does not change)
//  For RANDOM and RANDOM_NOREP,  reset CAN make a difference (if reset="0", then only newly enrolled 
//  participants are given a "randomly drawn"  id
//  For NONE: no reference id (hence no reference variables will be assigned to participants). Use REFID of -1

// return : a message to display. If the messages starts with "0,:", then force display (of everything after the 0,). 
//              Otherwise, display only if  "intermediate message display" is enabled

function assignRefID(areset) {

var okays=' NONE 0 NAME STOP CYCLE RANDOM RANDOM_NOREP '; 
var nassign,noassign,nid,thisid,anode,ahow,ahows,m,dd,tahow,notReset;
var vv,myname,uname,rname;
var myname,rname,rid,lookups,rname2;

if (arguments.length<1) areset=1 ;                      // default is to reset (overwrite) existing value of REFID


// no current users? Nothing to assign
if (currentParticipants['$IDS'].length==0) {;                      // array of IDS defined in currentParticipants
   return  '0,<span style="font-size:150%;color:red">No participants (perhaps you forgot to use <tt> &lt;getParticipants &gt;</tt>)?</span>';
}

anode=E2_getNodes(sessionNode,'HOW_ASSIGN','');
if (anode.length==0) {
   return '0,<span style="font-size:150%;color:red">Error: HOW_ASSIGN session specific variable was not specified </span>';
}

// allowed values for reset="n"
//  1 (default) : remove all prior REFID values 
//  0 : do not change REFID if it is already  defined (is non-empty)
// 0 is used if you want to assign ids to newly enrolled participants, without changing the REFID of older participants (who have been assigned an id on a prior call to this function)

notReset=0;

tahow=E2_getNodeText(anode[0],1,' ');
ahow=strip(tahow); 
if (ahow=='') ahow='0' ; 
ahows=ahow.split('=');
ahow=ahows[0];
ahow=strip(ahow.toUpperCase());
if (okays.indexOf(' '+ahow+' ')<0) {
   return '0,<span style="font-size:150%;color:red">Error: unknown value for HOW_ASSIGN: '+ahow+'</span>' ;
}

if (ahow!=='NONE' && refData['$VARS'].length==0) {;                      // mo ref data, but how_assign is not NONE? Error.
   return '0,<span style="font-size:150%;color:red">No REFERENCE data available, using HOW_ASSIGN='+ahow+' (perhaps you forgot to use <tt> &lt;getPARAMS &gt;</tt>)?</span>';
}


// if refid not specified, used 'REFID'
if (typeof(currentParticipants['$VARLOOKUP']['REFID'])=='undefined') {
        currentParticipants['$VARLOOKUP']['REFID']=1 ;
        currentParticipants['$VARS']=currentParticipants['$VARS'].concat('REFID');
}

nassign=0; noassign=0;

if (ahow=='NONE') {                        // do NOT assign refids (so no reference data)
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
       currentParticipants['$VARLOOKUP']['REFID']=-1 ;          // signal "no reference data"
       currentParticipants[thisid]['REFID']=-1;
       noassign++ ;
  }
  return (' assignRefID:  reference ids NOT assigned. Using HOW_ASSIGN= '+ahow+'.  # notReset='+notReset);
}


if (ahow=='0') {                        // sequential assignment. id=0 is used if more users than reference rows. 
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
       if (nid>=refData['$IDS'].length) {
             currentParticipants[thisid]['REFID']=0;
             noassign++ ;
        } else {
             currentParticipants[thisid]['REFID']=refData['$IDS'][nid];
             nassign++ ;
        }
  }
  return (' assignRefID:  reference ids assigned to '+nassign+ ' users, '+ noassign +' were assigned the default (0) reference id. Using HOW_ASSIGN= '+ahow+'.  # notReset='+notReset);
}

if (ahow=='STOP') {                        // sequential assignment. id=-1 is used if more users than reference rows. 
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
       if (nid>=refData['$IDS'].length) {
             currentParticipants[thisid]['REFID']='-1';                      // '-1' means "none available"
             noassign++ ;
        } else {
             currentParticipants[thisid]['REFID']=refData['$IDS'][nid];
             nassign++;
        }
  }
  return (' assignRefID:  reference ids assigned to '+nassign+ ' users ('+noassign+' users were not assigned reference ids). Using HOW_ASSIGN= '+ahow+'.  # notReset='+notReset);
}

if (ahow=='CYCLE') {                        // sequential assignment. id=0 is used if more users than reference rows. 
  nid2=-1;

  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
       nid2++ ;
       if (nid2>=refData['$IDS'].length) nid2=0 ;
       currentParticipants[thisid]['REFID']=refData['$IDS'][nid2];
       nassign++ ;
  }
  return (' assignRefID:  reference ids assigned to '+nassign+ ' users. Using HOW_ASSIGN= '+ahow+'.  # notReset='+notReset);
}

if (ahow=='RANDOM') {                        // sequential assignment. id=0 is used if more users than reference rows. 
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
       nid2=Math.floor(Math.random()*refData['$IDS'].length)    
       currentParticipants[thisid]['REFID']=refData['$IDS'][nid2];
       nassign++ ;
  }
  return (' assignRefID:  reference ids assigned to '+nassign+ ' users. Using HOW_ASSIGN= '+ahow+'.  # notReset='+notReset);
}


if (ahow=='RANDOM_NOREP') {                        // sequential assignment. id=0 is used if more users than reference rows. 
  dd=[] ;
  if (areset==0) {
    alert("ERROR: random_norep and reset=0 not currently supported ");  
    return "0,ERROR: random_norep and reset=0 not currently supported ";;
  }
  for (var m=0;m<refData['$IDS'].length;m++) dd=dd.concat(refData['$IDS'][m]) ;
  dd.sort(function() {return 0.5 - Math.random()})        //Array elements now scrambled
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++) {
       thisid=currentParticipants['$IDS'][nid];
       if (nid>=refData['$IDS'].length) {
             currentParticipants[thisid]['REFID']='-1';                      // '-1' means "none available"
             noassign++ ;
        } else {
             currentParticipants[thisid]['REFID']=dd[nid];
             nassign++ ;
        }
  }
  return (' assignRefID:  reference ids assigned to '+nassign+ ' users ('+noassign+' users were not assigned reference ids). Using HOW_ASSIGN= '+ahow);
}

if (ahow=='NAME') {                        // assign by matching user name to refname variable (in referenceData)
   if (ahows.length<2) {
        return('0,assignRefID: error, no name to use in the referenceData was not specified: ' + tahow);
   }
   uname=ahow;                          // redundant. Did support use of variable name other than "NAME". But now we force the use of "NAME"
   rname=strip(ahows[1].toUpperCase()) ;   // typically rname will be REFNAME

   if (typeof(currentParticipants['$VARLOOKUP'][uname])=='undefined') {
        return('0,'+uname+' variable does not exist in users dataset: '+currentParticipants['$VARS'].join(' '));
   }        
   if (typeof(refData['$VARLOOKUP'][rname])=='undefined') {
        return('0,'+rname+' variable does not exist in reference dataset: '+refData['$VARS'].join(','));
   }        

   lookups=new Object() ;
   for (var m=0;m<refData['$IDS'].length;m++) {
        rid=refData['$IDS'][m];
        rname2=strip(refData[rid][rname].toUpperCase());
        lookups[rname2]=rid ;
   }
  for (var nid=0; nid<currentParticipants['$IDS'].length;nid++ ) {
       thisid=currentParticipants['$IDS'][nid];
       if (areset==0) {
          if (typeof(currentParticipants[thisid]['REFID'])!='undefined' ) {   ;                           // exists.. but is it ''?
               if (strip(currentParticipants[thisid]['REFID'])!=='' ) {notReset++ ; continue }   ;           // not blank, so no overwrite
          }
       }
        myname=strip(currentParticipants[thisid][uname].toUpperCase());
        if (typeof(lookups[myname])=='undefined')  {
           noassign++;
           currentParticipants[thisid]['REFID']=0 ;
        } else {
            nassign++ ;      
            currentParticipants[thisid]['REFID']=lookups[myname];
        }
   }
   return (' assignRefID:  reference ids assigned to '+nassign+ ' users ('+noassign+' users were not assigned reference ids). Using HOW_ASSIGN= '+ahow+'  # notReset='+notReset);
        
}

return '0,<span style="font-size:150%;color:red"> ERROR: unknown HOW_ASSIGN = ' + ahow+'</span>';

}

// RETURN  ASSIGNID


// ============================= 
// read a general variable -- possibly an array element (x[1])
// if no arguments, return list of variable names
// adefault returned if no such general variable ('' if not specified)

function readGeneralVar(aname,adefault) {
var gg,aname,vv,ind ;
if (arguments.length<2) adefault='' ;

if (arguments.length<1) return generalVars['$VARS'];

aname=strip(aname.toUpperCase()) ;
if (typeof(generalVars[aname])=='undefined') return adefault ;

vv=generalVars[aname];

if (aname.indexOf('[')>-1) {                        // seek an array element
  if (vv[0]!=1) {
        alert('readGeneralVar error: asking for array element for non-array :'+vv.join(' | ') +' :: ( ' +aname+' ) ');
        return adefault ;
  }
  ind=parseUsing(aname,'[',']');
  return vv[1][ind];
}

return vv[1];
}



// ==============================
// add a variable to the general variables
function saveGeneralVar(aname,aval,isarray) {
var gg ;
if (arguments.length<3) isarray=0;

if (arguments.length<1 || typeof(generalVars)=='undefined') {
  generalVars=new Object();
  generalVars['$VARS']=[] ;                  // array  variables defined in generalvars
  generalVars['$lastSaved']={};               // values of general variables that were written to a postcap
}
if (arguments.length<1) return 1 ;

 gg=[] ;
 gg[0]=isarray; gg[1]=aval ;gg[2]=aname ;

aname=strip(aname.toUpperCase());
if (typeof(generalVars[aname])=='undefined') generalVars['$VARS']=generalVars['$VARS'].concat(aname) ;
generalVars[aname]=gg ;
return 1 ;

}

// ==============================
//  read a user variable, frin a given clientid. If no such variable, or no such id, return defval ('' if not specified)
function readUserVar(clientid,aname,defval)     {

if (arguments.length<3) defval='';
if (arguments.length<2) return defval ;

aname=strip(aname.toUpperCase()) ;
clientid=strip(clientid.toUpperCase());

if (typeof(userData)=='undefined') return defval ;
if (typeof(userData[clientid])=='undefined') return defval ;
if (typeof(userData[clientid][aname])=='undefined') return defval ;
return userData[clientid][aname];

}


// ==============================
//  save a user variable, for a given clientid
function saveUserVar(clientid,aname,aval)     {

if (arguments.length<1 || typeof(userData)=='undefined') {
     userData=new Object() ;                  // client specific variables read from <USER> section of session specific files
     userData['$IDS']=[] ;                      // array of IDS defined in userData
     userData['$VARS']=[] ;                  // array of user variable names
     userData['$VARLOOKUP']=new Object();                  // object of user variable names
}
if (arguments.length<1) return 1 ;

clientid=strip(clientid.toUpperCase());
aname=strip(aname.toUpperCase());

if (typeof(userData[clientid])=='undefined') {
     userData[clientid]=new Object() ;
     userData['$IDS']=userData['$IDS'].concat(clientid);
}
if (typeof(userData['$VARLOOKUP'][aname])=='undefined') {
   userData['$VARLOOKUP'][aname]=1 ;
   userData['$VARS']=userData['$VARS'].concat(aname);
}
userData[clientid][aname]=aval;
return 1 ;
}



// ==============================
//  read a user variable, frin a given clientid. If no such variable, or no such id, return defval ('' if not specified)
function readResultsVar(clientid,aname,defval)     {

if (arguments.length<3) defval='';
if (arguments.length<2) return defval ;
aname=strip(aname.toUpperCase()) ;
clientid=strip(clientid.toUpperCase());

if (aname=='ROUND' && clientid=='' && typeof(resultsData['$ROUND'])!='undefined') return resultsData['$ROUND'];  
if (typeof(resultsData)=='undefined') return defval ;
if (typeof(resultsData[clientid])=='undefined') return defval ;
if (typeof(resultsData[clientid][aname])=='undefined') return defval ;
return resultsData[clientid][aname];

}

// ==============================
//  save a resultsData variable, to a given clientid
//   If clientID='*', save to the "*" client (a value used for ALL clients-- to wit, a general variable!)
//   If clientID='', and aname='ROUND', save the round for this results
// Special cases: saveResultsVar(inum) -- initialize data, set $ROUND = inum
//       saveResultsVar() -- initialize data
//       saveResultsVar('','ROUND',iround) -- set the "ROUND" (in resultsData) to be iround
// Short cut: if clientID is an array of IDs, then set each the aname resultsVar to aval for each ID listed in clientID
//             Or, if aval is also an array, then use the appropriate row of avar (for the ID in the same row of clientIDs)
// Short cut: saveResultsVar(aid,'vname[*]',varray) will create seperate resultsData variables for all elements of the varray array, using vname as
//              the array name (i.e.; vname[1] and vname[2] will be used to save the values of varray[1] and varray[2])
// If the two shortcuts are combined, then aval should be a matrix (so that the aval[i] is treated as an array

function saveResultsVar(clientid,aname,aval)     {

var isarray=0,visarray=0,useval,i,aname,alist,aclientid ;
var nameArray=0,tmp;
var jj,tname ;

if (arguments.length<1 || typeof(resultsData)=='undefined') {
     resultsData=new Object() ;                  // client specific variables read from <USER> section of session specific files
     resultsData['$IDS']=[] ;                      // array of IDS defined in userData
     resultsData['$VARS']=[] ;                  // array of user variable names
     resultsData['$VARLOOKUP']=new Object();                  // object of user variable names
     resultsData['*']={};
     resultsData['*']['$VARLOOKUP']=new Object();                  // object of *-id variable names
     resultsData['*']['$VARS']=[] ;
}
if (arguments.length<2) return 1 ;

if (typeof(clientid)=='object' && typeof(clientid.length)!='undefined')  isarray=1 ;
if (typeof(aval)=='object' && typeof(aval.length)!='undefined') visarray=1 ;

if (aname.indexOf('[*]')>-1) {
      nameArray=1 ;
      tmp=parseAt(aname,'['); aname=tmp[0];

}
aname=strip(aname.toUpperCase());

if (isarray==0  && clientid=='' && aname=='ROUND') {
    resultsData['$ROUND']=aval;
    return 1 ;
}

if (isarray==1) {
   alist=clientid;
} else {
  alist=[clientid];
}

useval=aval ;                   // if isarray, then use ith row of aval if aval is also an array

for (var i=0;i<alist.length;i++) {
  aclientid=strip(alist[i]);
  if (typeof(resultsData[aclientid])=='undefined') {
     resultsData[aclientid]=new Object() ;
     resultsData['$IDS']=resultsData['$IDS'].concat(aclientid);
  }

  if (visarray==1 && isarray==1) {                      // clientids is an array, and so is aval (aval[i] might also be an array, if aname is x[*] 
        useval=aval[i] ;
  } 

  if (nameArray==0) {
     resultsData[aclientid][aname]=useval;

     if (aclientid=='*') {
        if (typeof(resultsData['*']['$VARLOOKUP'][aname])=='undefined') {
           resultsData['*']['$VARLOOKUP'][aname]=1 ;
           resultsData['*']['$VARS']=resultsData['*']['$VARS'].concat(aname);
        }
     } else {                           // not *
         if (typeof(resultsData['$VARLOOKUP'][aname])=='undefined') {
           resultsData['$VARLOOKUP'][aname]=1 ;
           resultsData['$VARS']=resultsData['$VARS'].concat(aname);
        } 
     }                          // aclientid='*'

  } else {                                      // aname is an array, so save an array of values 
     for (var jj in useval) {
       tname=aname+'['+jj+']';
       resultsData[aclientid][tname]=useval[jj];
       if (aclientid=='*') {
          if (typeof(resultsData['*']['$VARLOOKUP'][tname])=='undefined') {
             resultsData['*']['$VARLOOKUP'][tname]=1 ;
             resultsData['*']['$VARS']=resultsData['*']['$VARS'].concat(tname);
           }
       } else {                         // not *
           if (typeof(resultsData['$VARLOOKUP'][tname])=='undefined') {
              resultsData['$VARLOOKUP'][tname]=1 ;
              resultsData['$VARS']=resultsData['$VARS'].concat(tname);
           } 
       }                                // acliendit='*'
     }                                  // j in useval

  }                                     // nameArray=0

}                                      // alist

return 1 ;
}

// resultsData


// ==============================
// save data in the z dataobject
// idVar is the "ID" variable,
// varlist is the list of variables to save. If a variable in varlist does NOT exist in z, use defval as the value
// if idVar not specified, use 'ID'
// if noast=1, then do NOT save constants to * id 

function saveDOResultsVar(z,idVar,varlist,defval,noast) {
  var jj,vs,idvals,i,avar;
  var zvars,ozvars,zconsts,ozconsts;

  if (arguments.length<2) idVar='ID' ;
  if (arguments.length<3) varlist=z['$varlist'] ;
  if (arguments.length<4)  defval=''; 
  if (arguments.length<5)  noast=0; 

   zvars=z['$varlist'] ;
   ozvars={};
   for (var i=0;i<zvars.length;i++) ozvars[zvars[i]]=1;
   zconsts=z['$constlist'] ;
//alert('zzzz '+zconsts);
   ozconsts={};
   for (var i=0;i<zconsts.length;i++) ozconsts[zconsts[i]]=1;

  if (strip(varlist)=='*') varlist=z['$varlist'] ;
  if (typeof(varlist)=='string') varlist=getWord(varlist) ;
  
   if (idVar=='ID') {
      idvals=z['$idlist'] ;  
   } else {
      idvals=z.get(idVar,'Error in saveDOResultsVar: no such idVar='+idVar);
   }
 
   if (typeof(idvals)=='undefined')  return '' ;                       // no such ID var, give up

   for (var i=0;i<varlist.length;i++) {                         // do z variables
      avar=strip(varlist[i]).toUpperCase();
      vs=defval ;  
      if (typeof(ozvars[avar])!=='undefined')  {         // a variable. 
          vs=z.get(avar,'Error: no such z variable:'+avar);
      } else {                                          // not a variable
         if (typeof(ozconsts[avar])!=='undefined')  {         // a constant?
             if (noast==0) continue ;                       // and constants not suppressed? then do below (for id=*)
         }                                      // if not a variable or a constant, use defval
      }   
//  alert(idvals+' saveing '+avar+'='+vs);
      saveResultsVar(idvals,avar,vs)     ;
    }

   if (noast!==1) {
      for (var i=0;i<varlist.length;i++) {                         // do constants
         avar=strip(varlist[i]).toUpperCase();
         if (typeof(ozconsts[avar])=='undefined') continue ;               // skip if not a constant
         vs=z[avar];
//alert('saveconst '+avar+'='+vs);
         saveResultsVar('*',avar,vs)     ;
       }
   }
  
   return 1;
}

// ==============================
//  save a ref variable, to a given refid
function saveRefVar(aname,refid,aval)     {

if (arguments.length<1 || typeof(refData)=='undefined') {
     refData=new Object() ;                  
     refData['$IDS']=[] ;                      // array of IDS defined in 
     refData['$VARS']=[] ;                  // array of ref variable names
     refData['$VARLOOKUP']=new Object();                  // object of ref variable names 
}
if (arguments.length<1) return 1 ;

refid=strip(refid.toUpperCase());
aname=strip(aname.toUpperCase());

if (typeof(refData[refid])=='undefined') {
     refData[refid]=new Object() ;
     refData['$IDS']=refData['$IDS'].concat(refid);
}
if (typeof(refData['$VARLOOKUP'][aname])=='undefined') {
   refData['$VARLOOKUP'][aname]=1 ;
   refData['$VARS']=refData['$VARS'].concat(aname);
}
refData[refid][aname]=aval;
return 1 ;
}




// =========================== 
// display and process a form 
// doHide0=1 means "<FORM> in <INIT>
//          and implies: "hide options table", "hide suggestion box",
//          include a QUIT

function doForm(useName,fname,doHide0) {

 if (arguments.length<2 || fname=='') fname='_formArea';              // this is almost always used!
 if (arguments.length<3) doHide0=0;
 formVals=[];                    // each element will be a 3x1 array : type, name, value
 radioList=new Object();
 stepsList=new Object(); stepsList['$GroupNAMES']=[];stepsList['$GroupNODES']=new Object();               // list of group names, first button nodes by group
 arrayList=new Object() ;                // list of input nodes created for an array

 var swhere='',slowc,shighc,sfmt,aid;
 var nname,vval,gval,nname,itype,ivar,ival,elist,m,elist,ado,mess1,vv,iform ;
 var snodes,m,sname,gotNode,iform,alerts,gotsubmit,a1,i1,i2,tmp,agroup,nofix,achange,achangenode;
 var atable,atbody,atr,atd,ainput,mm,gval2,useName,isdo,jstyle,jsize,jtitle,imgsize,jinc,jid,jmin,jmax,jfmt,jdef,aspan,jarrowName,jstart;
  var a1,a2,ainput,aclose,ado,aset,m2,atext,agroup,indGroup;
 var asummary,aset,avar,aval,use1,m3,fname2;
 var doclear,nextr,clearit,tmp1;
  var dohide,doNotHide,hh,doshow,bval,scrollAt;
 var rlist,rlist0,dispSugg='',iidd,xtname;

  deFocusNextTask(0) ;

// look for the form that matches usename

  snodes=E2_getNodes(controlNode,'form');
  if (snodes.length<0) {
        alert('Error: no FORM nodes in control file ');
        return 1;
   }
   useName=strip(useName.toUpperCase());

   gotNode=0;
   for (var m=0;m<snodes.length;m++) {
       aname=getAttributes(snodes[m],'name',' ');
       aname=strip(aname.toUpperCase());
       if (aname==useName) {
           gotNode=snodes[m];
           break;
        }
    } 

    if (gotNode===0) { 
        alert('Unable to find FORM node with name= '+useName);
        return 1;
    }
    iform=document.getElementById(fname);
    iform.innerHTML='';

    hh=getAttributes(gotNode,'height','70%');
    iform.height=hh;
    iform.style.height=hh;

// the use of a HIDE attribute is mostly deprecated -- see vuTasksTable instead
    dohide=strip(getAttributes(gotNode,'hide','0'));   // hide a table. THIS IS MOSTLY DEPRECATED
    if (dohide==='1') dohide='_optionsTables';
    if (dohide!=='' && dohide!=='0') {
        dohide=getWord(dohide);
        for (var m3=0;m3<dohide.length;m3++) {
             aid=document.getElementById(dohide[m3]);
             if (!aid) {
                alert('Attempting to hide (in FORM) a non-existent id: '+dohide[m3]);
                return 0;
             } else {
                aid.style.display='none';
             }
         }
    }

// use vuTasksTable to hide the tasks table (to make space)?
    if (doHide0==1){
        iidd=document.getElementById('_suggestionsBox');
        if (iidd) {
           if (iidd.style.display.toUpperCase()!=='NONE') {
                dispSugg='displayDiv("_suggestionsBox","'+iidd.style.display+'");' ;
                iidd.style.display='none';      
            }
        }
    }
    dohide=strip(getAttributes(gotNode,'vuTasksTable','-1'));   
    if (doHide0==1) dohide='none' ;                  // doHide0 argument overrides vuTasksTable attribute
    if (dohide==='')   dohide=2;                       // toggle is the default
    if (dohide!=='-1') displayDiv('_optionsTables',dohide);     // -1 signals "no vuTasksTable attribute


    iform.setAttribute('height',hh);

// now process elements in form. Look for SET and IF in order of appearance
    
    useGotNode=doFormProcess(gotNode);          // process SET and IF
    
// extract MONITOR

    E2_xmlToDom(useGotNode,iform);                      // and save to the dom tree

    findMonitors(iform,1) ;                         // process <monitor>


//  look for a <submit>
 elist=iform.getElementsByTagName('submit');
 alerts='';
  gotsubmit=1;

 if (elist.length==0) {
       gotsubmit=0;
 } else {
      ado=getAttributes(elist[0],'do dotask ','');
      if (ado=='') {
         alerts=alerts+'<p/> <b>Warning:</b>  no do attribute in &lt;submit&gt; ';
      }
 }
 if (gotsubmit==1) {
     mess1=E2_getNodeText(elist[0],1,'');
     vv=document.createElement('input');
     vv.setAttribute('type','button');
     vv.setAttribute('value',mess1);
     vv.setAttribute('name','submit1');
     vv.setAttribute('noFixMe','1');
     vv.setAttribute('do',ado);
     var noclear=getAttributes(elist[0],'noclear noclose',0);

// display="1"  is mostly deprecated -- see vuTasksTable justbelow
       doNotHide=strip(getAttributes(elist[0],'display','0'));
       if (doNotHide==='1') doNotHide='_optionsTables';
       if (doNotHide=='0') doNotHide='';
       doNotHide=getWord(doNotHide);
       fname2='';
       for (var m3=0;m3<doNotHide.length;m3++){
         fname2='displayDiv("'+doNotHide[m3]+'","block") ; ' +fname2 ;
       }

// hide/display tasks table?
     dohide=strip(getAttributes(elist[0],'vuTasksTable','-1'));   
    if (doHide0==1) dohide='block' ;                  // doHide0 argument overrides vuTasksTable attribute
     if (dohide==='') dohide=2;                       // toggle is the default
     if (dohide!=='-1') fname2=fname2+" displayDiv('_optionsTables','"+strip(dohide)+"'); "+dispSugg;    // -1 signals "no vuTasksTable attribute

      fname2=fname2+' displayDiv("'+fname+'","none") ;displayDiv(\'_formAreaArea\',\'none\'); ';
     if (noclear!=1) {
       tmp="'"+fname+"','none'";
       tmp1="'Submit form: "+useName+"'";
       var jiffy="writeLastAction(this,4,"+tmp1+");submitForm(this);"+fname2+"; return false;";
       vv.setAttribute('onClick',jiffy);
     } else {
        vv.setAttribute('onClick','writeLastAction(this,4,"+tmp1+");submitForm(this);  return false;'); 
     }
     vv.setAttribute('summary',getAttributes(elist[0],'summary',''));
      
     elist[0].innerHTML='';
     elist[0].appendChild(vv);
 }                                     // end of SUBMIT (gotsubmit)

//  look for a <quit> 
 elist=iform.getElementsByTagName('quit');
 if (elist.length>0 || doHide0==1) {
 
// this is slightly deprecated -- see vuTasksTable just below
    fname2=''; doNotHIde='0'
    if (doHide0!==1) {
      if (elist.length>0) doNotHide=strip(getAttributes(elist[0],'display','0'));
      if (doNotHide==='1') doNotHide='_optionsTables';
      if (doNotHide=='0') doNotHide='';
      doNotHide=getWord(doNotHide);
      for (var m3=0;m3<doNotHide.length;m3++){
           fname2='displayDiv("'+doNotHide[m3]+'","block") ; ' +fname2 ;
      }
   }

// vuTaskTable to hide/display tasks table?
     dohide=strip(getAttributes(elist[0],'vuTasksTable','-1'));   
     if (doHide0==1) dohide='block' ;                  // doHide0 argument overrides vuTasksTable attribute
     if (dohide==='') dohide=2;                       // toggle is the default
     if (dohide!=='-1') fname2=fname2+" displayDiv('_optionsTables','"+strip(dohide)+"'); "+dispSugg;    // -1 signals "no vuTasksTable attribute

     mess1=E2_getNodeText(elist[0],1,'');
     if (mess1=='') mess1="Quit";
     vv=document.createElement('input');
       vv.setAttribute('type','button');
        vv.setAttribute('name','quit1');
        vv.setAttribute('value',mess1);
        vv.setAttribute('noFixMe','1');
        fname2=fname2+' displayDiv("'+fname+'","none") ; displayDiv(\'_formAreaArea\',\'none\'); '+dispSugg ;
     vv.setAttribute('onClick',fname2+' ; return false;'); 
     if (doHide0!==1 || elist.length>0 ) {
          elist[0].innerHTML='';
          elist[0].appendChild(vv);
     } else {
        iform.appendChild(vv);
     }
  }                                   // quit

// now check for input, etc elements. ALso look for displayIf 
  var  elist0=iform.getElementsByTagName('*'); 
  elist=Concat_Arrays(elist0);                          // make into something other than a node list

 for (var m=0 ;m<elist.length;m++) {

         avar=getAttributes(elist[m],'displayIf','');
         if (avar!='') {
            aval=E2_doMath(avar);
            if (aval==0) elist[m].style.display='none';
         }

         nname=elist[m].nodeName.toUpperCase() ;                                // the nodeName

         if (nname=='INPUT' ||  nname=='OPTION' || nname=='TEXTAREA' || nname=='RADIO' || nname=='ARRAY' || nname=='STEP' || nname=='NUMBER' ) {                        // form element
                itype=getAttributes(elist[m],'type',''); itype=strip(itype.toUpperCase()) ;
                if (itype=='') itype='TEXT';
                ivar=elist[m].name ;
                if (typeof(ivar)=='undefined') ivar=getAttributes(elist[m],'name',' ');
                ivar=strip(ivar.toUpperCase());
                if (ivar=='') {
                        alert('Error in form: no NAME specified (in a form element) '+itype);
                         return 1 ;
                 }
                elist[m].setAttribute('useName',ivar);
                
                nofix=getAttributes(elist[m],'noFixMe','');
                ival=getAttributes(elist[m],'value','');
                aset=getAttributes(elist[m],'set','');
                if (aset=='') elist[m].setAttribute('set',ival);

                isdo=getAttributes(elist[m],'do doTask doTasks','');         // submit on click/change

                var vinfo=getVarInfo(ivar,'','GENERAL',1);

                gval=vinfo.value ;

                if (  nname=='TEXTAREA') {                            // text? then just set the value
                    elist[m].setAttribute('value',gval);
                    elist[m].setAttribute('onChange','storeFormVal(this)');
                }
                
                if (itype=='TEXT'  ) {                            // text? then just set the value
                    elist[m].setAttribute('value',gval);
                    elist[m].setAttribute('onChange','storeFormVal(this)');
                }

                if (itype=='BUTTON'  && nofix!='1') {                            // button?  no value? then just set the value
                    if (ival=='') elist[m].setAttribute('value',gval);
                    elist[m].setAttribute('onClick','storeFormVal(this)');
                }

                if (itype=='CHECKBOX' || itype=='RADIO') {                        // checkbox? Set the check box if 
                       if (strip(gval)==strip(ival)) elist[m].setAttribute('checked','yes');
                       elist[m].setAttribute('onClick','storeFormVal(this)');
                 }
                if (nname=='OPTION') {                        // checkbox? Set the check box if 
                       if (strip(gval)==strip(ival)) {
                          elist[m].setAttribute('selected','selected');
                          elist[m].selected='selected';
                       }
                      elist[m].setAttribute('onClick','storeFormVal(this)');
                 }

                if (nname=='RADIO') {                        // RADIO buttons
                       nstyle=getAttributes(elist[m],'styleNo',styleNoDefault);
                       ystyle=getAttributes(elist[m],'styleYes',styleYesDefault);
                        nextr=getAttributes(elist[m],'nextRadio',0);
                        doclear=getAttributes(elist[m],'clear close',0);             // opposite of noclear
                       if (nextr==1) {                          // a nextRadio attribute
                               elist[m].setAttribute('onClick','storeFormVal(this); return false;');
                       } else {                                 // not a nextradio
                           elist[m].setAttribute('style',nstyle);
                           elist[m].style.cssText=nstyle;
                           var clearit='';
                           if (doclear==1) clearit="displayDiv('"+fname+"','none');";
                           if (isdo!='') {                                  // a do="xx", so submit 
                               elist[m].setAttribute('onClick','storeFormVal(this);submitForm(this);'+clearit+' ; return false;');
                           } else {
                              elist[m].setAttribute('onClick','storeFormVal(this);  return false ');
                          }
                          if (strip(gval)==strip(ival)) { 
                             elist[m].setAttribute('style',ystyle);
                             elist[m].style.cssText=ystyle;
                          }
                          if (typeof(radioList[ivar])=='undefined') radioList[ivar]=[0];           // first element is used to store pointer to currently selected radio (0 means none)
                          radioList[ivar]=radioList[ivar].concat(elist[m]);
                      }                                 // not a nextRadio
                 }

                if (nname=='NUMBER') {                        //  a numberic value that can be incremented/decremented with arrows; or with a slider bar
                       jsize=getAttributes(elist[m],'size','5');
                       jtitle=getAttributes(elist[m],'title','enter a numeric value');
                       jstyle=getAttributes(elist[m],'style','');
                       jarrowName=getAttributes(elist[m],'arrow arrowName','Chevron');
                       jinc=getAttributes(elist[m],'inc increment','1');
                       jmin=getAttributes(elist[m],'min  minimum ','');
                       jmax=getAttributes(elist[m],'max maximum ','');
                       jdef=getAttributes(elist[m],'default def ','');
                       jfmt=getAttributes(elist[m],'fmt format ','');

                        jmax=getVarInfo(jmax,'value','control');
                        jmin=getVarInfo(jmin,'value','control');
                        jdef=getVarInfo(jdef,'value','control');
                        jfmt=getVarInfo(jfmt,'value','control');

                       swhere=getAttributes(elist[m],'scrollWhere where','');
                       slowc=getAttributes(elist[m],'lowColor','');
                       shighc=getAttributes(elist[m],'highColor','');
                       sfmt=getAttributes(elist[m],'fmt format ','');
                       if (isNaN(jdef)) {
                               alert('Error in NUMBER: bad default= '+jdef);
                               return 0;
                       }
                       if (isNaN(jfmt)) {
                               alert('Error in NUMBER: bad format= '+jfmt);
                               return 0;
                       }

                       if (jmin!=='' && isNaN(jmin)) {
                               alert('Error in NUMBER: bad min= '+jmin);
                               return 0;
                       }
                       if (jmax!=='' && isNaN(jmax)) {
                               alert('Error in NUMBER: bad max= '+jmax);
                               return 0;
                       }
                       if (isNaN(jinc)) {
                               alert('Error in NUMBER: bad inc= '+jinc);
                               return 0;
                       }

                       aspan=document.createElement('span');
                       ainput=document.createElement('input');
               
                       ainput.setAttribute('size',jsize);
                       ainput.setAttribute('title',jtitle);
                       ainput.setAttribute('inc',jinc);
                       ainput.setAttribute('min',jmin);   
                       ainput.setAttribute('max',jmax);
                       ainput.setAttribute('def',jdef);
                       ainput.setAttribute('fmt',jfmt);

                       if (swhere!=='') {
                             ainput.setAttribute('scrollWhere',swhere);
                             ainput.setAttribute('lowColor',slowc);
                             ainput.setAttribute('highColor',shighc);
                             ainput.setAttribute('fmt',sfmt);
                       }

                       ainput.setAttribute('usename',ivar) ;
                       if (jstyle!=='') {
                                ainput.style.cssText=jstyle ;
                          } else {
                               ainput.style.height='30px';
                               ainput.style.fontSize='15px';      
                               ainput.style.backgroundColor='#ededed';
                       }

                        imgsize=20 ;
                        if (typeof(ainput.style.height)!='undefined') {
                                imgsize=parseInt(ainput.style.height);
                                imgsize=Math.ceil(imgsize/2); ;
                        }
                        ainput.setAttribute('type','text') ;
                        ainput.setAttribute('value',gval);  ainput.value=gval;
                        ainput.setAttribute('set',gval) ;
                        jid='Number'+m;
                        ainput.setAttribute('id',jid);
                        ainput.setAttribute('onChange','incrementNumber("","'+jid +'")');      // increment number will check min and max
                        
                       scrollAt=-1;
                       if (swhere!=='') {
                               var tookie=[ainput];
                               scrollAt=initScrollValues(tookie);
                              if (scrollAt<0) return 0;               // error
                       }      



                       atable=document.createElement('table');
                          atbody=document.createElement('tbody');
                            atr=document.createElement('tr');
                              atd=document.createElement('td');
                              atd.appendChild(ainput);
                              atd.setAttribute('rowspan',2);
                            atr.appendChild(atd);                      
                       if (jarrowName!=='0') {
                               atd=document.createElement('td');
                               aimg=document.createElement('img');
                               aimg.setAttribute('src','/dsurvey/docs/icons/'+jarrowName+'Up.gif');
                               aimg.setAttribute('title','click here to increment value');
                               aimg.setAttribute('alt','up');
                               aimg.setAttribute('height',imgsize);
                               aimg.setAttribute('width',imgsize);
                               aimg.setAttribute('onClick','incrementNumber('+jinc+',"'+jid +'",'+scrollAt+')'); 
                               atd.appendChild(aimg);
                            atr.appendChild(aimg);
                        }
                        atbody.appendChild(atr);
                       if (jarrowName!=='0') {
                            atr=document.createElement('tr');
                               atd=document.createElement('td');
                               aimg=document.createElement('img');
                               aimg.setAttribute('src','/dsurvey/docs/icons/'+jarrowName+'Down.gif');
                               aimg.setAttribute('title','click here to decrement value');
                               aimg.setAttribute('alt','down');
                               aimg.setAttribute('onClick','incrementNumber(-'+jinc+',"'+jid +'",'+scrollAt+')'); 
                               aimg.setAttribute('height',imgsize);
                               aimg.setAttribute('width',imgsize);
                               atd.appendChild(aimg);
                            atr.appendChild(aimg);
                          atbody.appendChild(atr);
                        }
                        atable.appendChild(atbody);
                        aspan.appendChild(atable);
                        elist[m].parentNode.insertBefore(aspan,elist[m]); 
                        elist[m].parentNode.removeChild(elist[m]); 
                }

                if (nname=='STEP') {                        //  a sequence of stpes
                       var agroup=getAttributes(elist[m],'GROUP',ivar); agroup=strip(agroup.toUpperCase());
                       var achange=getAttributes(elist[m],'CHANGE_NODE','');
                       if (typeof(stepsList[agroup])=='undefined') {
                            stepsList[agroup]=[0];                        // list of step elements of this name (0'th element used as a counter
                            stepsList['$GroupNAMES']=stepsList['$GroupNAMES'].concat(agroup);   // list of group names
                        }
                       stepsList[agroup]=stepsList[agroup].concat(elist[m]);
                       astyle=elist[m].style.cssText ;  
                       elist[m].setAttribute('usestyle',astyle);
                       elist[m].setAttribute('stepGroup',agroup); 
                       elist[m].setAttribute('stepGroupIndex',stepsList[agroup].length-1); 
                       elist[m].style.cssText='display:none' ;          // suppress display of the <step> 
                       if (achange!=='') {
                                elist[m].setAttribute('change_node',achange);           // points to node with change instructions
                                elist[m].setAttribute('change',1);                      // used as flag
                       }
                }

                if (nname=='ARRAY') {                        // <ARRAY> of inputs
                       jlength=getAttributes(elist[m],'length nlength','');
                       jsize=getAttributes(elist[m],'size','5');
                       jstart=getAttributes(elist[m],'start','0');
                       arrayList[ivar]=[];
                        atable=document.createElement('table');
                        atbody=document.createElement('tbody');
                        atr=document.createElement('tr');
                        gval2=gval ;
                        if (jlength=='' || isNaN(jlength) ) jlength=gval2.length ;
                        for (var mm=gval2.length;mm<jlength;mm++) gval2[mm]='';         // pad if necessary
                        for (var mm=jstart;mm<jlength;mm++) {
                           atd=document.createElement('td');
                           ainput=document.createElement('input');
                           ainput.setAttribute('size',jsize);
                           ainput.setAttribute('arrayname',ivar) ;
                           ainput.setAttribute('usename',ivar) ;
                           ainput.setAttribute('type','text') ;
                           ainput.setAttribute('value',gval2[mm]);  ainput.value=gval2[mm];
                           ainput.setAttribute('set',gval2[mm]) ;
                           ainput.setAttribute('index',mm) ;
                           ainput.setAttribute('onChange','storeFormVal(this);  return false ;');

                           arrayList[ivar][mm]=gval2[mm];
                           atd.appendChild(ainput);
                           atr.appendChild(atd);
                        }
                        atbody.appendChild(atr);
                        atable.appendChild(atbody);
                        elist[m].parentNode.insertBefore(atable,elist[m]); 
                        elist[m].parentNode.removeChild(elist[m]); 

               }
          }                                   // useable form element: INPUT OPTION TEXTAREA RADIO ARRAY STEP
 }                                            // all elements in this block

// which step to display in the various "groups" 
 for (var m=0;m<stepsList['$GroupNAMES'].length; m++) {

        a1=stepsList['$GroupNAMES'][m];                     // a GROUP (identifying a  sequeence-of-<STEP>s 
        var igot=1 ;                            // default is first
        a2=stepsList[a1][1];                    //  step in this sequence to start at
        for (var jj=1; jj<stepsList[a1].length;jj++) {
              a2=stepsList[a1][jj];
              aname=getAttributes(a2,'useFirst','');
              if (aname!='') {
                 aval=E2_doMath(aname);
                 if (aval==1) {
                    igot=jj;
                    break ;
                 }                         // aval=1
              }                          // aname
        }                            // jj  (steps in this group)

        a2=stepsList[a1][igot];                    //  step in this sequence to start at
        stepsList[a1][0]=igot ;                    // store an index to which step is currently used in this sequence

        astyle=getAttributes(a2,'usestyle','');
        agroup=getAttributes(a2,'stepGroup','');
        indGroup=getAttributes(a2,'stepGroupIndex','');
        asummary=getAttributes(a2,'summary','');
        asummary=getAttributes(a2,'clear close','0');

        achangenode=getAttributes(a2,'change_node','');
        atext=expandReadsString(a2);

        ainput=document.createElement('input');
          ainput.style.cssText=astyle;
          ainput.setAttribute('type','button');
          ainput.setAttribute('stepGroup',agroup);
          ainput.setAttribute('stepGroupIndex',indGroup);
          ainput.setAttribute('summary',asummary);
          if (achangenode!=='') { 
            ainput.setAttribute('change',1);
            ainput.setAttribute('change_node',achangenode);
         }
          ainput.value=atext ;

           var clearit='';
           if (doclear==1) clearit="displayDiv('"+fname+"','none');";
          if (ado!='') {                                  // a do="xx", so submit 
              ainput.setAttribute('onClick','storeFormVal(this);submitForm(this);'+clearit+'; return false;');
          } else {
              ainput.setAttribute('onClick','storeFormVal(this);  return false;');
          }

       stepsList['$GroupNODES'][agroup]=ainput ;                 // pointer to button for this group (sequence)
       a2.parentNode.insertBefore(ainput,a2);
  }                                                     // stepslist


  displayDiv(fname,'block');                    // now display the processed form!
  xtname=document.getElementsByName('_formDisplay_');
  if (xtname.length==0) {
    alert('Error on doForm: could not find _formDisplay_');
    return 0;
  }
  displayDiv('_formAreaArea','block');  

  dsurveyFocus('_formDisplay_')  ;

/*  ===== debug    
 i_pageNow=;

  ff=i_pageNow.innerHTML ;
  ff=document.body.innerHTML ;

  var dw=window.open('','debugwindow');
  dw.document.open('text/plain');
  var t1='<html><head><title>Debug window</title><body><div style="border:2px black dotted; background-color:#999999"><tt>Debug mode: </div> \n '+ff +' \n </body></html>';
  dw.document.write(t1);
  dw.document.close();
 ===== */


  writeDoStatus(alerts,'','RESET ',' form: '+useName);

  processMonitor() ;                      // update <monitor>
 return 1;
}                      

//=============== desc
// process a node (from a <form>) for <IF> and <SET>
function  doFormProcess(anode,paneldo) {
if (arguments.length<2) paneldo=0;

var sayform='PANEL'
if (paneldo==0) sayform='FORM';
if (paneldo==0) sayform='MONITOR';

var useGotNode;
var alist,anode,anode1,aname,mm,nn,daIf,useif,m2,tmp,wasl;
var bval,avar,avar2,doshow,vinfo,aval,qq;

if (paneldo==0 || paneldo==2) {
   useGotNode=E2_xmlToDom(anode);
} else {
  useGotNode=anode;
}

alist=useGotNode.getElementsByTagName('*')   ;

if (paneldo==0) checkDoForm(alist);                                   // process doForm, doTasks, etc

mm=-1; ;
for (m=1;m>0 ; m++) {                   // leave with a break
   mm++;
   if (mm>=alist.length) break ;                // no more nodes. Note that alist is a nodelist, hence dynamically grows
   anode1=alist[mm];

   if (typeof(anode1)=='undefined' || typeof(anode1.parentNode)=='undefined' || typeof(anode1.parentNode.removeChild)=='undefined') continue ; // goofy crap can happen

   aname=anode1.nodeName;
   aname=strip(aname.toUpperCase()) ;

   if (aname=='IF') {
        qq=E2_getNodes(anode1,'THEN ELSE ');
        if (qq.length>0) {
             alert('ERROR: THEN and ELSE not supported in <'+sayform+'>  <if> ');
             return 0;
        }

        daIf=getAttributes(anode1,'if cond var condition value','');
        if (daIf=='') {
                alert('Error in <IF> in a <'+sayform+'>: missing IF condition ');
                return 0;
          }
         useif=E2_doMath(daIf);
         if (useif==0) {                        // remove the IF and its children (below)
         } else {                                       // remove IF (retain children)
//           var goon='';
             for (var m2=0; m2<anode1.childNodes.length;m2++) {  
//                if (anode1.childNodes[m2].nodeType==3) goon=goon+anode1.childNodes[m2].nodeValue+' | ';
                tmp=E2_xmlToDom(anode1.childNodes[m2]);
                anode1.parentNode.insertBefore(tmp,anode1);
             }
//        alert('gooon '+goon);
         }
        var wasl=alist.length;
        anode1.parentNode.removeChild(anode1);
        if (wasl!=alist.length)    mm=mm-1;                     // since node removed, mm should be steped back
        continue ;
    }                   // if

    if (aname=='SET') {                                 // <SET> 
       avar=getAttributes(anode1,'value var ','');
       if (avar=='') avar=E2_getNodeText(anode1);
       if (avar.indexOf('=')<0) {                         // no =, so just evaluate and display 
          bval=E2_doMath(avar);
          doshow=getAttributes(anode1,'SHOW','-1');
          if (doshow!=='0') {
             anode1.innerHTML=bval;
          } else {
             anode1.innerHTML='';
          }
          continue ;
       }
// else, an = exists
       avar2=parseAt(avar,'=');
       doshow=getAttributes(anode1,'SHOW','0');
       bval=E2_doMath(avar2[1]);
       vinfo=getVarInfo(avar2[0]);
       if (vinfo.type=='CONTROL') controlVars[vinfo.name]=bval ;
       if (vinfo.type=='GENERAL') saveGeneralVar(vinfo.name,bval) ;
       if (doshow==1) {
           anode1.innerHTML=bval;
       } else {
           anode1.innerHTML='xx';
       }
       continue ;
    }                   // SET

// find all <read> and change its value
    if (aname=='READ' || aname=="SHOW") {
        avar=getAttributes(anode1,'var name value varname ','');
        aval=getVarInfo(avar,'value','control');
        anode1.innerHTML=aval;
        continue ;
    }                                   // READ

}                       //  mm+

return useGotNode ;
}

// ===============
// increment a value in an input box (used by <NUMBER ...>
// jinc='' means "check min/max" 
function incrementNumber(jinc,useid,scrollAt) {
var zinc,jmin,jmax,jdef,jval,jval2,ii;
if (arguments.length<3) scrollAt=-1;
ii=document.getElementById(useid); 
jmin=ii.getAttribute('min');
jmax=ii.getAttribute('max');
jdef=ii.getAttribute('def');
jfmt=ii.getAttribute('fmt');

jval=ii.value;

if (jinc==='') {                                        // just check (here on typing 
  if (strip(jval)==='' || isNaN(jval)) {
        jval=jdef ;
  } else {
     jval2=(1*jval)  ;
     if (jmax!=='' && jval>(1*jmax)) jval=jmax*1;
     if (jmin!=='' && jval<(1*jmin)) jval=jmin*1 ;
  }
  ii.value=jval;
  ii.setAttribute('value',jval);
  ii.setAttribute('set',jval);
  storeFormVal(ii)
  return 1 ;
}

// if here, arrowing. '' default becomes min, or 0
zinc=0;
if (jval==='' || isNaN(jval)   )  {
  jval=jdef; zinc=-jinc;
}
if (jval==='' || isNaN(jval) ) {
   jval=jmin ;
   zinc=-jinc ;
}
if (jval==='' || isNaN(jval)  ) {
   jval=0;
   zinc=-jinc ;
}
if (!isNaN(jval)) jval=(1*jval)+zinc;

jval2=(1*jval)+jinc ;
if (jmax!=='' && jval2>(1*jmax)) jval2=jmax*1;
if (jmin!=='' && jval2<(1*jmin)) jval2=jmin*1 ;

if (jfmt!=='' && !isNaN(jfmt) ) {
  jval2=jval2.toFixed(jfmt);
}

ii.value=jval2;

ii.setAttribute('value',jval2);
ii.setAttribute('set',jval2);

goog=ii.getAttribute('value');
storeFormVal(ii) ;

if (scrollAt>-1) {                      // set scrollbar 
  doScrollValue(0,scrollAt,jval2) ;
}

return 1 ;
}


// ===============
// create a string of contents in a node, expanding <read> and <show>
function    expandReads(bnode) {
var rlist,m,avar,aval ;
var rlist=bnode.getElementsByTagName('read');
for (var m=0;m<rlist.length;m++) {
  avar=getAttributes(rlist[m],'var name ','');
  aval=getVarInfo(avar,'value','control');
  rlist[m].innerHTML=aval;
}
var rlist=bnode.getElementsByTagName('show');
for (var m=0;m<rlist.length;m++) {
  avar=getAttributes(rlist[m],'var name ','');
  aval=getVarInfo(avar,'value','control');
  rlist[m].innerHTML=aval;
}

return 1;
}            

// ===============
// find all the <read> in a node, and change their innerhtmls depending on value of the name attribute. Return in one long string
function    expandReadsString(anode) {
var rlist,m,avar,aval,bnode ;
amess='';
zo=E2_getNodeText(anode);
for (var m=0;m<anode.childNodes.length;m++) {
    bnode=anode.childNodes[m] ;
   if (bnode.nodeType==3) {
           amess=amess+' '+bnode.nodeValue ;
   } else if (bnode.nodeType==1 && ( bnode.nodeName.toUpperCase()=='SHOW' || bnode.nodeName.toUpperCase()=='READ') ) {                      // ignore comments, etc
         avar=getAttributes(bnode,'var name','');
         aval=getVarInfo(avar,'value','control');
         amess=amess+' '+aval;
   }
}
return amess ;
}

//====================
// write summary of this task to lastAction node
// e is either a string, or a dom object with a "summary" atrribute
// astyle is style to use when writing message. If not specified, use subTaskBarStyle,
// if astyle='', no special style used

function writeLastAction(e,astyle,tname) {
 var asummary,idlast,atime,vv;
 var asum2,m,vv,aword;
 if (arguments.length<2) astyle=3 ;             // default is no highlight
 if (arguments.length<3) tname='' ;
 
// convenient shorthands
 if (astyle==4) {                               // slight bold highlight 
         astyle=taskBarStyleForm ;
         if (tname==='') tname='Form';
 }
 if (astyle==3) {                               // no highlight
        astyle=subTaskBarStyle ;     
         if (tname==='')  tname='Task';
 }
 if (astyle==2) {                               // bold highlight
         astyle=subTaskBarStyleBold ;
         if (tname==='') tname='Task-list';
 }

 if (astyle==1) {                               // border and background color highlight
         astyle=taskBarStyle ;
         if (tname==='') tname='Alert';
 }


 if (typeof(e)=='string') {
        asummary=e;
 } else {
   asummary=getAttributes(e,'summary','');
 }

 if (asummary!='') {
       vv=getWord(asummary);
       asum2='';
       for (var m=0;m<vv.length;m++){
           aword=strip(vv[m]);
           if (aword.substr(0,1)=='%') {
               aword2=aword.substr(1);
               if (isNaN(aword2)) {
                   aword=getVarInfo(aword2,'value');
                }
            }
            asum2=asum2+' '+aword;
       }

       if (astyle!=='') {
              asum2='<span title="'+tname+'" style="'+astyle+'">'+asum2+'</span> ';
        } else {
                asum2='<span>'+asum2+'</span> ';
       }

       idlast=document.getElementById('_lastAction') ;
       if (idlast) idlast.innerHTML=asum2 ;

       idlast=document.getElementById('_lastActionHistory') ;
       if (idlast) {
           atime=new Date() ; atime=atime.toLocaleTimeString();
           vv='<div style="border:1px dotted #9a9aba; margin-bottom:5px">';
           vv=vv+'<span style="font-size:85%">'+atime+'  </span>'+asum2+'</div> \n '  ;
           idlast.innerHTML=vv+idlast.innerHTML ;    
        }

  }
  return 1;
}

//===================== focus
// come here on click of a form input, etc element
function storeFormVal(e) {
 var nname,vval,cclass,dlist,m,nstyle,arname,aindex,agroup ;
 var agroup, a1,ia2,a2,astyle,usename,aset,ado,atext,ainput ;
 var elist,anode,ect,iuse ;
 var achange,ichange;
 var m,arf0,arflist;
 var oo,alist,vv,doclear,clearit;
 var avar,aval,nowait, foo ;

 achange=getAttributes(e,'change','');                      // check for change actions

 var fname="_formArea";                          // used when close="1" specified

// is a change attribute set?
 if (achange!='')  {
   ichange=getAttributes(e,'change_node','');           // get the actual node, which retains the XML structure
   aset=getAttributes(e,'set','');                    // set attribute of this element
   avalue=e.value;                                    // value attribute of this element
   arf0=attNodeList[ichange];
   arflist=E2_getNodes(arf0,'*');
   for (var m=0;m<arflist.length;m++) {
     arf=arflist[m];
     aif2=1;

     aif=getAttributes(arf,'if','');
     if (aif!='') {
       aif=aif.replace(/this.set/g,aset);               // replace a "this.set" with the  "set" value of form element
       aif=aif.replace(/this.value/g,avalue);               // replace a "this.value" with the  "value" value of form element
       aif2=E2_doMath(aif);                       // evaluate an if
     }
     if (aif2==1) {                                         // depends on nodename: CONFIRM, DISPLAY SET and ALERT  
           if (arf.nodeName.toUpperCase()=='CONFIRM') {                 // <CONFIRM> input value change
               areads2=expandReadsString(arf);
               ido= confirm(areads2);
               if (!ido) {
                   writeDoStatus(' Action was not confirmed ','','NOSAVE');
                   return 1;
               }           // ido
           }               // confirm

           if (arf.nodeName.toUpperCase()=='ALERT') {          // <ALERT> in <CHANGE>
               writeAnAlert(arf,'',aset,avalue);
           }           // ido


           if (arf.nodeName.toUpperCase()=='SET') {                     // <SET> in <CHANGE>: explicitily set values 
                 alist=E2_getNodeText(arf,1,'');
                 nowait=getAttributes(arf,'nowait',0);
                 alist=alist.split(';');
                 for (var iiv=0;iiv<alist.length;iiv++){
                     vv=alist[iiv];
                     if (vv.indexOf('=')<0) {
                        alert('Error in <SET> in <CHANGE>, bad equation: '+vv); 
                        return 0;
                     }
                     vv=parseAt(vv,'=');
                     avar=strip(vv[0].toUpperCase()) ; aval=E2_doMath(vv[1]);
                     if (nowait==1) {                                   // immediatlely save value (
                          foo=getVarInfo(avar,'','control');
                          if (foo.type!='CONTROL') {  
                                alert('Error in <SET> in <CHANGE>: nowait specified for a non-control variable: '+foo.type);
                                return 1; 
                          }      
                          avar=foo.name;
                          controlVars[avar]=aval;
                     } else {                                           // save value on a DO or a submit?
                        oo=document.createElement('dochange');
                        oo.setAttribute('usename',strip(vv[0]));
                        aval=E2_doMath(vv[1]);
                        oo.setAttribute('set',aval);
                        formVals=formVals.concat(oo);
                    }
                 }
//                if (nowait==1) continue ;
          }

           if (arf.nodeName.toUpperCase()=='DISPLAY') {                         // <DISPLAY> in confirm (show elements)
                atype=getAttributes(arf,'TYPE',''); atype=strip(atype.toUpperCase());  
                if (atype=='TOGGLE') atype='';
                if (atype=='1' || atype.substr(0,1)=='Y' || atype.substr(0,1)=='D') atype='block';
                if (  atype.substr(0,1)=='S') atype='inline';
                if (  atype.substr(0,1)=='N') atype='none';

                divlist=E2_getNodeText(arf,1,'');
                divlist=getWord(divlist);
                for (var m2=0;m2<divlist.length;m2++) {
                   displayDiv(divlist[m2],atype);
                }
           }
     }                   // if condition is true, or no if condition
   }                 //  nodes
 }                   // change atribute exists

 writeLastAction(e);                 // change (confirm) did not suppress this. So note this happened in lastAction box?

// a <RADIO> node?
 if (e.nodeName.toUpperCase()=='RADIO') {
    nname=e.getAttribute('usename'); nname=strip(nname.toUpperCase());
    var cmessage=getAttributes(e,'clickMessage',0);
    if (typeof(radioList[nname])=='undefined') {
                alert('storeFormVal error: no such element in radioList = ' +nname);
                return 1;
    }
   elist=radioList[nname];

   nextr=getAttributes(e,'nextRadio',0) ;
   if (nextr==1) {
       elist=radioList[nname];
       ect=elist[0]+1;
       iuse=ect ;
       if (ect<elist.length) {
          radioList[nname][0]=ect;
          e=radioList[nname][ect];
          writeLastAction(e);
       } else {                                 // no next, so cycle back to 0 and do nothing
           nstyle=getAttributes(elist[elist.length-1],'styleNo',styleNoDefault);
           elist[elist.length-1].setAttribute('style',nstyle);    elist[elist.length-1].style.cssText=nstyle;
           radioList[nname][0]=0;
           return 1 ;
        }
    }                   //NEXTR 

    for (var m=1; m<elist.length;m++) {                         // # 1 points to currently selected radio (or 0 if none selected)
          if (e==elist[m]) {
                     iuse=m ;
                     radioList[nname][0]=m ;
                     nstyle=getAttributes(e,'styleYes',styleYesDefault);
           } else {
               nstyle=getAttributes(elist[m],'styleNo',styleNoDefault);
           }
           elist[m].setAttribute('style',nstyle);    elist[m].style.cssText=nstyle;
    }
   if (cmessage!=0 ) {
        var cstyle=getAttributes(elist[iuse],'styleClick clickStyle ',styleClickDefault);
        elist[iuse].setAttribute('style',cstyle);    elist[iuse].style.cssText=cstyle;
        elist[iuse].innerHTML=cmessage; 
    }
    formVals=formVals.concat(elist[iuse]);
    ado=getAttributes(elist[iuse],'do','');
    if (nextr==1 && ado!='') {
        submitForm(elist[iuse]);
    }

   return 1 ;

 }                      // radio


 if (e.nodeName.toUpperCase()=='INPUT') {

   arname=getAttributes(e,'arrayname','');
   if (arname!='') {                             // an ARRAY
        aval=e.value ;
        if (typeof(aval)=='undefined') aval=getAttributes(e,'value',' ');
        aindex=getAttributes(e,'index','');
        if (aval.indexOf(',')>-1) {
            aval=aval.replace(/,/g,' ');
            alert('Warning: a "," in anarray element has been removed: '+aval);
        }
        arrayList[arname][aindex]=aval;
        e.setAttribute('set',aval);
        formVals=formVals.concat(e);
        return 1 ;
   }

    itype=getAttributes(e,'type',''); itype=strip(itype.toUpperCase()) ;
    if (itype=='TEXT') {
        aval=e.value ;
        if (typeof(aval)=='undefined') aval=getAttributes(e,'value',' ');
        e.setAttribute('set',aval);
    }
    if (itype=='CHECKBOX') {
        if (e.checked) {
          aval=e.value ;
          if (typeof(aval)=='undefined') aval=getAttributes(e,'value',' ');
        } else {
            aval=getAttributes(e,'notValue',' ');
        }
        e.setAttribute('set',aval);
    }
 }

 if (e.nodeName.toUpperCase()=='TEXTAREA') {
        aval=e.value ;
        if (typeof(aval)=='undefined') aval=getAttributes(e,'value',' ');
        e.setAttribute('set',aval);
 }

 agroup=getAttributes(e,'stepgroup','');
 if (agroup=='') {
      formVals=formVals.concat(e);
  } else  {                                     //STEP : store the <step>, not the button
      a1=agroup ;
      indGroup=getAttributes(e,'stepgroupIndex','');
      
      ause=stepsList[agroup][indGroup];
      formVals=formVals.concat(ause);                   // add pointer to this <STEP>
      ia2=stepsList[a1][0];                    // currently displayed step in this sequence
      a2=stepsList[a1][ia2];                    // node for current step
      var areset=getAttributes(a2,'reset',0);
      if (areset==1) {
         ia2=1
      } else {
         ia2=ia2+1 ;                               // augment it
      }
      if (ia2<stepsList[a1].length) {           // not past end of sequence?
        stepsList[a1][0]=ia2;                    // then update currently displayed step in this sequence
        a2=stepsList[a1][ia2];                    // and set pointer to this now-the-current step
        ainput=stepsList['$GroupNODES'][agroup];
        ainput.style.cssText=getAttributes(a2,'usestyle','') ;          // and change button attributes to match this now the current step
        ainput.setAttribute('stepGroup',getAttributes(a2,'stepGroup',''));
        var igind=getAttributes(a2,'stepGroupIndex','');
         achange=getAttributes(a2,'change_node','');
        ainput.setAttribute('summary',getAttributes(a2,'summary',''));
        
        ainput.setAttribute('stepGroupIndex',igind);

        if (achange!=='') {
           ainput.setAttribute('change_node',achange);
           ainput.setAttribute('change',1);
       }      

        ainput.value=expandReadsString(a2);

        doclear=getAttributes(a2,'close clear','0');
        clearit='';
        if (doclear==1) clearit="displayDiv('"+fname+"','none');";
        ado=getAttributes(a2,'do doTask doTasks','');
          if (ado!='') {                                  // a do="xx", so submit
               ainput.setAttribute('onClick','storeFormVal(this);submitForm(this);'+clearit+'; return false;');
          } else {
              ainput.setAttribute('onClick','storeFormVal(this); return false ; ');
         }
     }                          // steplist loop
 }                              // stepgroup



return 1;

}
 
// this set value .value setAttribute
//===================== 
// come here on click of a form submit
function submitForm(e) {
var atag,aclass,m,vtype,aname,avalue,atype,founds,aline,arname ,vv,didarrays,useName,aset0;
var vinfo,gotDo='',didDo=0,vv2,tmp1,tmp2 ;

// display list of changes
founds=new Object();
formValsUse=[] ;                        // this will hold the "nodes" containing values to set
didarrays=new Object();
writeDoStatus('<b>Variables that you modified... </b>','','RESET INTER',' form submitted ') ;


for (var m=formVals.length-1;m>=0;m--) {                        // list of nodes  pointing to input elements to be processed
  anode=formVals[m];
  aname=strip(getAttributes(anode,'usename',''));
  if (aname=='' || aname=='.') continue;                              // kind of unlikely, maybe? could happen if <CHANGE> <SET> is used to set values
  vinfo=getVarInfo(aname,'','general');
  taname=strip(vinfo.name.toUpperCase()) ;
  vtype=strip(vinfo.type) ;

  arname=getAttributes(anode,'arrayname','');
  if (arname!='') {                             // an ARRAY
        jindex=getAttributes(anode,'index','');
        taname=taname+'['+jindex+']' ;
  }  

  if (typeof(founds[taname])!='undefined') continue ;                           // set by a later action
  aset=getAttributes(anode,'set',' ');
  aline=vtype+' <b> '+taname+'</b> = '+aset ;

  if (arname!='' && typeof(didarrays[arname])=='undefined')  {   // <ARRAY> will change arrayList values on the fly
         aline=aline+'&nbsp;&nbsp;&nbsp;  <tt>('+arrayList[arname].join(' , ')+ ')</tt> ';
         formValsUse=formValsUse.concat(anode) ;
         didarrays[arname]=1;
  } else {
    if (arname=='')  formValsUse=formValsUse.concat(anode) ;
  }
  writeDoStatus(aline,'','INTER');
  founds[taname]=1 ;
}

if (e.getAttribute('do')=='') {
  writeDoStatus(' <p/>  <b>Warning:</b> no DO specified for this form ');
}

// now actually change the variables

for (var m=0;m<formValsUse.length;m++) {
  anode=formValsUse[m];
  aname=getAttributes(anode,'usename','');
  vinfo=getVarInfo(aname,'','general');
  taname=vinfo.name ;
  if (taname=='.') continue ;
  gotDo=getAttributes(anode,'do','');
  aset=getAttributes(anode,'set',' ');
  vtype=vinfo.type ;


//    generalVars['VARNAME'][0] =  1 if this should be treated as an array
//    generalVars['VARNAME'][1] =   value. If an array, this will be an array
//   generalVars['VARNAME'][2] =   uncapitalized version of the varname

  isarray=0;
  arname=getAttributes(anode,'arrayname','');
  if (arname!='') {                             // an ARRAY
      isarray=1;
      aset=arrayList[arname] ;
  }   else {
    if (vtype=='GENERAL' && aname.indexOf('[')>-1) {                // special case: a x[i] syntax
        vv2=parseArrayIndex(aname);
        taname=strip(vv2[0]).toUpperCase();
        tmp1=generalVars[taname];
        if (tmp1[0]!=1) {
            aset0=[];
        } else {
          aset0=tmp1[1];
        }
        jindex=vv2[1];
        aset0[jindex]=aset ;
        aset=aset0;
        isarray=1;
    }    
  }

  if (vtype=='GENERAL'){
        saveGeneralVar(taname,aset,isarray);
        submitFormList=submitFormList.concat(taname);

  } else if (vtype=='SERVER'){
        alert('Warning: can not change value of serverVar: ' +taname );
  } else {
       controlVars[taname]=aset ;
  }


}

// summary of changes
writeDoStatus('# of variables modified= ' + formValsUse.length,'','INTER') ;

// and do something with the changed variables 
useName=getAttributes(e,'do',gotDo);
if (useName=='') {
  if (didDo!=1) {
    writeDoStatus('<p/> No DO action specified: variables are changed to controlPanel databases, but nothing else will be done','','INTER');
  } else {
    writeDoStatus('<p/> DO action in an element.','','INTER');
  }
} else {
  writeDoStatus(' Do action:<tt> '+useName+'</tt> \n ','','INTER');
  doStuff(useName) ;
}

processMonitor() ;                      // update <monitor>

formVals=[];
return 1;
} 


// ===================================
// set value of a contrl variable. REturns prior value (if no second arg, then do NOT set control variable value
function setControlVar(avar,aval) {
var oldval,avar;
 if (typeof(controlVars)=='undefined') {
        alert('Error in setControlVar: controlVars global not defined ');
        return 0;
  }

  avar=strip(avar).toUpperCase();
  if (typeof(controlVars[avar])=='undefined') {
        oldval=undefined;
  } else {
     oldval=controlVars[avar] ;
  }
  if (arguments.length>1) controlVars[avar]=aval;
  return oldval;
}


// ===================================
// return info on a variable:  x.name, x.value, x.type, x.defined. x.array 
// if second arg, just return that field (2nd arg can be name,value,type,defined, or array (case insensitive
// 3rd arg sets what the default type is (no prepended character, no .xxx) -- default is CONTROL
// 4th arg is used rarely: if 1, then parse "array elements" (stuff.array=2)
// note: @xxx means "xxx server variable"  (or .S or .SERVER)
//       %xxx means "xxx control varaible" (or .C or .CONTROL)
//       #xxx means "xxx general variable" (or .G or .GENERAL)
//
// The next 3 are NOT YET SUPPORTED
//       xxx.RG means "xxx resultsData variable for the * id"
//       xxx.RU means "xxx resultsData vector for the all the clientIDs"
//       xxx.U  or xxx.USERS means "xxx user variable" -- a vector for all ids
// Note: the  values in .RU and .U arrays are matched by position to the ids in currentParticipants['$IDS']
//
// For control vars, undefined variable yields a value of ''

function getVarInfo(avar,rtype,deftype,elemOk) {
var stuff=new Object() ;
var rtypes=' defined value name type array ';
var vv,tmp1;
var arf,arfo,vval,daid ;

if (arguments.length<4) elemOk=0 ;
stuff.defined=0 ; stuff.value='' ; stuff.name='' ; stuff.type='' ; stuff.array=0 ;
var a1,i1;

if (arguments.length<2) rtype=''; 
rtype=strip(rtype.toLowerCase()) ;
if (arguments.length<3) deftype='CONTROL'; 
deftype=strip(deftype.toUpperCase()) ;

if (deftype!='CONTROL' && deftype!='SERVER' && deftype!='GENERAL' ) {
        alert('Error in getVarInfo: no such deftype = ' + deftype);
        return 0;
}

if (rtype!='') { 
   if (rtypes.indexOf(' '+rtype+' ')==-1) {
        alert('Error in getVarInfo: bad type = ' +rtype);
        return 0;
   }
}

 
avar=strip(avar.toUpperCase()) ;

if (!isNaN(avar)) {                                     // its a number
        stuff.type='NUMBER';
        stuff.name=avar ;
        stuff.defined=1 ; stuff.value=avar ; 
        if (rtype!='') return stuff[rtype];
        return stuff ;
}


a1=avar.substr(0,1);
avar=avar+' ';

// .xx syntax overrides @xx syntax
itype='';
i1=avar.indexOf('.SERVER ');
if (i1==-1) i1=avar.indexOf('.S ');
if (i1>-1) {
    itype='SERVER';
    avar=avar.substr(0,i1);
}
if (itype=='') {
  i1=avar.indexOf('.CONTROL ');
  if (i1==-1) i1=avar.indexOf('.C ');
  if (i1>-1) {
     itype='CONTROL';
     avar=avar.substr(0,i1);
  }
}
if (itype=='') {
  i1=avar.indexOf('.GENERAL ');
  if (i1==-1) i1=avar.indexOf('.G ');
  if (i1>-1) {
     itype='GENERAL';
     avar=avar.substr(0,i1);
  }
}


if (a1=='%' || a1=='#' || a1=='@') {
  avar=avar.substr(1);  
  if (itype=='') {                      // .xx trumps @xx 
        if (a1=='#') itype='CONTROL';
        if (a1=='%') itype='GENERAL';
        if (a1=='@') itype='SERVER';
   }
}

if (itype=='') itype=deftype;                   // if no @xx or xx.type, use deftype
avar=strip(avar);
  
if (itype=='SERVER') {
        stuff.type='SERVER';
        stuff.name=avar ;
        if (typeof(serverVars[avar])!='undefined') {
                stuff.defined=1 ; stuff.value=serverVars[avar] ; 
        }
        if (rtype!='') return stuff[rtype];
        return stuff ;
}

if (itype=='GENERAL') { 
   stuff.type='GENERAL'; 
   stuff.name=avar ;
   if (elemOk==1 && avar.indexOf('[')>-1) {             // special case: array index
        vv=parseArrayIndex(avar);
        tmp1=generalVars[strip(vv[0]).toUpperCase()];
        if (typeof(tmp1[1][vv[1]])!='undefined') {
           stuff.value=tmp1[1][vv[1]];
           stuff.defined=1 ; 
        }
   } else  {
      if (typeof(generalVars[avar])!='undefined') {
         stuff.defined=1 ; 
         stuff.value=readGeneralVar(avar,'') ;
         if (typeof(stuff.value)=='object'  && typeof(stuff.value.length)!='undefined') stuff.array=1 ;
      }
   }
   if (rtype!='') return stuff[rtype];
   return stuff;
}

// default is control
if (itype=='CONTROL') { 
    if (typeof(controlVars)=='undefined') {
        alert('Error in getVarInfo: controlVars global not defined ');
        return 0;
    }

    stuff.type='CONTROL';  stuff.name=avar ;
    if (typeof(controlVars[avar])!='undefined') {
                 stuff.defined=1 ; stuff.value=controlVars[avar] ; 
    }


   if (rtype!='') return stuff[rtype];
   return stuff;
}


alert('error in getvarinfo, should never see this ');
return 0;

}


//================================================ 
// alternate variable lookup functions for svyMath 

function xdoMathVarExists_control(avar,Dnode) {
 var nvar,ac1,ac2 ;
  avar=strip(avar);
  if (!isNaN(avar)) return 1 ;                  // numbers always exists
   ac1=avar.substr(0,1);
   ac2=avar.substr(avar.length-1);
  if ( (ac1=="'" && ac2=="'") ||   (ac1=='"' && ac2=='"') ) return 1  ; // strings always exist
 
  nvar=getVarInfo(avar,'DEFINED');
  return nvar ;
}

// ================
// lookup a variable. Special cases, that lookup hard coded javascript variables (case sensitive)
//E
//PI
//Otherwise, check for some other conditions, and then call getVarInfo


function xdoMathVar_control(avar,anode) {

var tavar,nvar,zz ;
  var tavar=avar.toUpperCase() ;
  if (tavar=='E') return Math.E ;
  if (tavar=='PI') return Math.PI ;
  if (avar.replace(/\s/g,'')=="''") {
        zz=strip(avar); 
        zz=zz.substr(1,zz.length-2);
        return zz;
  }

  nvar=getVarInfo(avar,'VALUE');                // some kind of variable OR vector 
  return nvar ;
}


//======================================
// Write a "data message" to a fixed div, click on the div and it goes away
// amess :the message
// lifespan: how long until his message is deleted (seconds)
// fade: message fades away, rather then just disappears
// if lifespan=-1, close box immediately (do not display message)
// if autoClose=0, then suppress the click-anywhere-in-box closer
function writeDataMessage(amess,lifespan,fade,autoClose) {
  var arf,dd,aa;
  if (arguments.length==0) amess='Status messages written here.' ;
  if (arguments.length<2) lifespan=0 ;
  if (arguments.length<3) fade=0 ;
  if (arguments.length<4) autoClose='1' ;

  if (lifespan<0) {
    dd=document.getElementById('_dataMessageDiv');
    if (dd) {
         dd.style.display='none';          
         return ;
     } else {
        alert('error in writeDataMessage: can not find _dataMessageDiv ');
        return 0;
    }
  }

  dd=document.getElementById('_dataMessageDiv');
  if (!dd) {
     aa=document.createElement('div');
     aa.style.cssText=styleDataMessage ;
     aa.style.display='none';
     aa.setAttribute('id','_dataMessageDiv');

     document.body.appendChild(aa);
     dd=document.getElementById('_dataMessageDiv');
/*===
     if (autoClose=='1') {
         dd.setAttribute('onClick',"writeDataMessageClick()");
         alert('autoclose ');
     } else {
         dd.setAttribute('onClick',"alert(33)");
         alert('autoclosexx ');
     }
=== */
   }  
   displayDiv('_dataMessageDiv',1);
   if (autoClose=='1') {
         dd.setAttribute('onClick',"writeDataMessageClick()");
   } else {
         dd.setAttribute('onClick'," ");
   }


   if (autoClose=='1') {               // if autoClose on, then do NOT include onclick for this span
       arf='<div style="margin:-9px -2px 0px -9px; text-align:center;width:12px;font-size:12px;background-color:#ababab"  ';
       arf=arf+ ' title="click here to close this message window"   > ';
   } else {                             // autoClose off, then add an onclick 
       arf='<div style="margin:-9px -2px 0px -9px; text-align:center;width:12px;font-size:12px;background-color:#ababab" ';
       arf=arf+   ' title="click here to close this message window" onClick="writeDataMessageClick()"  > ';
   }

   arf=arf+'x</div>'; 


   dd.innerHTML=arf+' '+amess;
   dd.style.display='block';
   dd.style.color='black';
   dd.style.opacity=1.0;                                // fixes a click-to-close on faded div
   dd.style.filter='alpha(opacity=100)';


   dd.style.zIndex='40';
   if (lifespan>0 && !isNaN(lifespan)) {
      if (fade==0) {
           aa="displayDiv('_dataMessageDiv','none')";
           setTimeout(aa,lifespan*1000);
       } else {
           aa="fadeDiv('_dataMessageDiv',0.5,"+lifespan+")";
           setTimeout(aa,500);
       }
   }

   return 1 ;
}

//===========
function writeDataMessageClick(a) {

var dd=document.getElementById('_dataMessageDiv');
if (!dd) return 0;
if (dd.style.display.toUpperCase()=='NONE') {
        dd.style.display='block';
} else {
        dd.style.display='none';
}

return 1;
}


// ======================
// fade a div or span
function fadeDiv(adiv,tsec,totsec){ 
var iopac,fopac,aa,ii,newtsec,vv;
ii=document.getElementById(adiv);
newtsec=tsec+0.5 ;
if (newtsec>=totsec) {
      aa="displayDiv('_dataMessageDiv','none')";
      setTimeout(aa,500);
     return 1;
}
vv=(newtsec/totsec);
//cc=Math.ceil(vv*255); 
//ii.style.color="rgb("+cc+","+cc+","+cc+")";
fopac=1-parseFloat(vv);
iopac=parseInt(vv*100);
ii.style.opacity=fopac;
ii.style.filter='alpha(opacity='+iopac+')';
aa="fadeDiv('_dataMessageDiv',"+newtsec+","+totsec+")";

setTimeout(aa,500);
return 1; 
}

// ====================================
// upload a local file. 
//
// arguments:
// adiv: boxNumber used to display file-selection form (that is used by the participant
//              If it does not exist, it will be created
// defname: local file to read (optional, used as a hint)
// args1:  if 1, also create resultsData (after making workdata) . 2=add a "create results data" button. 0=don't
//           if 2 element array, [0] element is as above, [1] element if =1 means "read as CSV" file
//             (if not array, read as xml file)
// checkRound: if makeResults=1, check nowRound against workData max round (confirm box if not match)
// useRound: if not '', use this round to make results data. Otherwise, use maximum round number
// rndRead : variable name to save value of round used to make resultsData


// to read a file from the drive into the browser, javascript security requires a hack
// a) upload the file to a server (using an HTML FORM containing a file-upload input field)
//      A form is created, and displayed in a moveable box, that will contain a file-upload input field
// b) the server's response consists of the contents of this just uploaded file 
// c) these contents are placed in new "frame" (actually, an iframe) that is created below
//   (otherwise, the server response would overwrite the current window ... not a good thing!)
// d) after this response is completed,  a frame-specific onLoad function (gooba) is run.
//   This gooba function reads the contents of the iframe, and saves to internal databases (to workData and resultsData)


function readLocalXmlFile(adiv0,defname,args1,checkRound0,usernd,rndRead,usename,iat) {
var aform,fr,hiddenfield,ie,gooba,makeResults0,isCSV ;
var tt,pp,m,iframeName,coo,llist,mybox ;
var checkRoundR,useRoundR,roundReadR,makeResultsData,ustNameA,iatA;

if (arguments.length<1) adiv0=5;
if (arguments.length<2) defname='';
if (arguments.length<3) {
  makeResults0=''; 
  isCSV=0;       
} else {
  if (typeof(args1)=='object' && typeof(args1.length)!=='undefined') {
        makeResults0=args1[0];
        isCSV=args1[1];
  } else {
        makeResults0=args1;
        isCSV=0;
  }
}
if (arguments.length<4 ) checkRound0='';
if (arguments.length<5 ) usernd='' ;
if (arguments.length<6 ) rndRead='' ;
if (arguments.length<7 ) usename='' ;
if (arguments.length<8 ) iat=-1 ;

// the moveable box used to display the form 
adiv0=strip(adiv0);
if (isNaN(adiv0)) {
  alert('Error in readLocalXmlFile: invalid boxNumber (for displaying form) = '+adiv0);
  return 0;
}
mybox=adiv0;
if (exists_moveBox(mybox)) {
  open_moveBox(mybox)  ;
} else {
  create_moveBox(mybox,['65%','14%'],['45%','32%'],'Read a local file.')  ;              // create a moveable box      
  write_moveBox(mybox,'Read a local file  <a href="#" style="margin-left:50px" title="Close this box" onClick="close_moveBox('+mybox+'); return false">close</a>');
  open_moveBox(mybox);
}

checkRoundR=checkRound0 ;               // these are used by gooba internal function
useRoundR=strip(usernd) ;                   
roundReadR=rndRead ;            
makeResultsData=makeResults0;                    
iframeName='_readLocalXmlFile';                 // where the server's response is placed
useNameA=usename ;
iatA=iat;

aform= document.createElement("form");          // for the input fields requesting a file name

// (remove residual iframeName elements? 
llist=document.getElementsByName(iframeName);
if (llist.length>0) { 
  tmp=llist.length;
  for (var m=0;m<tmp.length;m++) llist[0].parentNode.removeChild(llist[0]); 
}

aform.setAttribute("method",'POST');                // create a POST form that will send a file to the serve
    aform.setAttribute("action",postFileURL );
    aform.setAttribute("enctype","multipart/form-data");
    aform.setAttribute("target",iframeName);            // the server echos the file back to an iframe (created below)
    aform.setAttribute('onsubmit','readLocal_helper1('+mybox+')' );

    var fr=document.createElement('iframe');
    fr.setAttribute("name",iframeName);                // used by target

    fr.setAttribute("id",iframeName );                  // used by getElementByID
    fr.setAttribute("src",'//javascript:void()' );
    fr.style.cssText="width:0;height:0;";
    fr.style.display='none';
    fr.setAttribute('firstCall',1);
    fr.onload=gooba;                                    // call the local function gooba when this iframe is loaded
    document.body.appendChild(fr);              // attach this "recieve xml file from server" iframe (see gooba for caution on first load

    hiddenField = document.createElement("input");              // create a hidden field with info that is sent to server
    hiddenField.setAttribute("type", "hidden");
    hiddenField.setAttribute("name", uploadFileName);
    hiddenField.setAttribute("value", uploadFileValue);
    aform.appendChild(hiddenField);

    hiddenField = document.createElement("input");              // the FILE input element that asks participant to enter name of  file to be uploaded
    hiddenField.setAttribute("type", "file");
    hiddenField.setAttribute("id", 'readLocalFileValue');               
    hiddenField.setAttribute("name", 'myname');
    aform.appendChild(hiddenField);

    hiddenField = document.createElement("input");
    hiddenField.setAttribute("type", "submit");
    hiddenField.setAttribute("value", 'Upload ');
    aform.appendChild(hiddenField);

    if (makeResultsData==2) {                   // add a Create resultsData button?
       hiddenField = document.createElement("div");              // the FILE input element that asks participant to enter name of  file to be uploaded
         hiddenField.style.backgroundColor='#eaeaea'; hiddenField.style.color="#777780"; 
         hiddenField.style.fontSize='95%'; hiddenField.style.padding="3px";
         hiddenField.style.border='2px dotted lime';
         hiddenField.style.width='30%';

         hiddenField.setAttribute('id','iSetRButton');
         hiddenField.setAttribute('set','0');
         hiddenField.setAttribute('onClick','setRButton(this)');
         hiddenField.setAttribute('title','Click here to CREATE resultsData');
         fr=document.createTextNode('Create resultsData?');
         hiddenField.appendChild(fr);
       aform.appendChild(hiddenField);
    }

       hiddenField = document.createElement("div");              // the FILE input element that asks participant to enter name of  file to be uploaded
         hiddenField.style.backgroundColor='#eaeaea'; hiddenField.style.color="#777780"; 
         hiddenField.style.fontSize='95%'; hiddenField.style.padding="3px";
         hiddenField.style.border='2px dotted lime';
         hiddenField.style.width='30%';

         hiddenField.setAttribute('id','iSetCSVButton');
         hiddenField.setAttribute('set','0');
         hiddenField.setAttribute('onClick','setCSVButton(this)');
         hiddenField.setAttribute('title','Read as a CSV file' );
         fr=document.createTextNode('Read as CSV?');
         hiddenField.appendChild(fr);
       aform.appendChild(hiddenField);

    ie=document.createElement('div');
      ie.style.height='100px';
      ie.style.backgroundColor='#ababba';
      ie.appendChild(aform);  

     writeContent_moveBox(mybox,ie.innerHTML);
     open_moveBox(mybox);


   return 1  ;                  // note that on mozilla, this leave main window content un changed.

// -----------
//* local function to call when iframe is loaded. iframe is loaded when the FILE input response occurs, whose target is the iframe 
//   Note that the XML file that iframe should contain contain one or more instances of <DATA> nodes. 
//   Any other "primary" nodes are ignored.
//   Each <DATA> node (aka "DATA record") will contain subnodes that define variables.
//   The tagnames of these subnodes are the variable names.
//    Note that an ID, and usually a ROUND, is specified for each <DATA>
// Note: appending an iframe is treated as a load, to gooba is called. Hence the need to use 'firstcall' to ignore this useless load 

   function gooba(e) {

    var ret,nn,goo,tt,maker=0, ii ,aret,astring ;
    var aid,around,varlist,ww,ret,ids,rounds,goo2,m,m2,nname2,anode,aval ;

    maker=makeResultsData ;                                   // used if direct call (not onclick call)

    if (this.getAttribute('firstCall')==1) {            // don't want to do anything on attaching to tree
        this.setAttribute('firstCall',2);
        return 0;
     }

    writeDataMessage('... file has been retrieved; please wait while it is processed ...'); 
    nn=document.getElementById('iSetCSVButton');
    if (nn)   isCSV=getAttributes(nn,'set',0);

    if (makeResultsData==2) {                               // check button
      var nn=document.getElementById('iSetRButton');
       if (nn)  {
          checkRoundR=0;
          maker=getAttributes(nn,'set',0);
        }
    }

    ret = frames[iframeName].document.getElementsByTagName("body")[0].innerHTML;    // iframename specified in parent function

   if (strip(isCSV)!=='1') {
       nn=document.createElement('div');
       nn.innerHTML=unescape(ret);
       goo=nn.getElementsByTagName('DATA');   // get all DATA nodes in this XML file
       delete nn ;                                  // some cleanp
   } else {
       aret=unescape(ret); 
       aret=aret.substr(5);
       aret=aret.substr(0,aret.length-6);  // strip <pre> and </pre> 
       astring=csvToXML(aret) ;
       nn=document.createElement('div');
       nn.innerHTML=astring;
       goo=nn.getElementsByTagName('DATA');   // get all DATA nodes in this converted-from-csv file
       delete nn ;                                  // some cleanp
   }
//alert(astring+' goo ='+goo.length);
  ii=document.getElementById('readLocalFileValue');
  make_workData(goo,'*','Local file: '+ii.value);                         // use ALL rows

  writeContent_moveBox(mybox,'File uploaded: '+ii.value+' <br/> The data in this file was saved to the <tt>workData</tt> internal database.<br/> <em>Summary of data:</em><div style="margin-left:5%">  IDs= ' +workData['ids'].join(' ')+'<br />Rounds= ' +workData['rounds']+'<br />'+' Variables=' +workData['vars'].join(' ')+'<br/></div>','','RESET',' xml file uploaded');
  
  if (maker==1) {                 // create resultsData from workData
      zz=workToResults(checkRoundR,useRoundR,roundReadR) ;
      if (zz==1) writeContent_moveBox(mybox,'<hr width="50%"/>  resultsData created from workData using round '+resultsData['$ROUND'],1);
  }

 if (isCSV) {
  writeDoStatus('CSV File uploaded: '+ii.value+' <br/> The data in this file was saved to the <tt>workData</tt> internal database.<br/> <em>Summary of data:</em><div style="margin-left:5%">  IDs= ' +workData['ids'].join(' ')+'<br />Rounds= ' +workData['rounds']+'<br />'+' Variables=' +workData['vars'].join(' ')+'<br/></div>','','RESET',' xml file uploaded');
 } else {
  writeDoStatus('XML File uploaded: '+ii.value+' <br/> The data in this file was saved to the <tt>workData</tt> internal database.<br/> <em>Summary of data:</em><div style="margin-left:5%">  IDs= ' +workData['ids'].join(' ')+'<br />Rounds= ' +workData['rounds']+'<br />'+' Variables=' +workData['vars'].join(' ')+'<br/></div>','','RESET',' xml file uploaded');
 }
  writeDataMessage('File retrieval complete.',-1); 

  if (iatA>0)    {                      // do task? then do next action
       iatA++ ;
       doStuff(useNameA,2,iatA);                        
  }
  return 1 ;
}               // end of readLocalXmlFile

}                               // end of readlocalxmlfile

//====
// helper function for readLocalXMLfile
function readLocal_helper1(ith) {
 writeDataMessage('Please wait while the file is retrieved ...'); 
 return true ;
}
 


// ===================================
// convert CSV string (say, as read from a file) to an XML structure
// the first row MUST contain variable names
// An ID and a ROUND variable MUST be specified
function csvToXML(ret) {
var aret,varline,vals=[],vars,alines,iround=-1,iid=-1,iv,i3,aval1,ith,xmlstring,astring,avar,aline ;
 aret=unescape(ret);

 alines=aret.split('\n');

varline=alines[0];
vars=parseCSVline(varline);
for (iv=0;iv<vars.length;iv++) { 
   avar=strip(vars[iv]).toUpperCase();
   if (avar=='ROUND') iround=iv;
   if (avar=='ID') iid=iv;
   avar=' '+avar;
   avar=strip(avar.replace(/\n/g,' '));
   vars[iv]=avar;
}
if (iround<0) {
  alert('Error in CSV file: no ROUND variable (in: '+varline); 
  return 0;
}
if (iid<0) {
  alert('Error in CSV file: no ID variable (in: '+varline);  
  return 0;
}

ith=0;
for (var iv=1;iv<alines.length;iv++) {
  aline=alines[iv] ;
  if (strip(aline)=='') continue ;
  vals[ith]=parseCSVline(aline) ;
  for (var i3=vals[ith].length;i3<vars.length;i3++) vals[ith][i3]=''
  ith++;
}

xmlstring='';
for (var iv=0;iv<vals.length;iv++) {
 astring='<DATA><ROUND>'+vals[iv][iround]+'</ROUND><ID>'+vals[iv][iid]+'</ID>';
 for (var i3=0;i3<vars.length;i3++) {
        if (i3==iid || i3==iround) continue;
        aval1=' '+vals[iv][i3];
        aval1.replace(/\n/g,' ');  
        aval1=strip(aval1); 
        astring=astring+'<'+vars[i3]+'>'+aval1+'</'+vars[i3]+'> ';
 }

 xmlstring=xmlstring+astring+'</DATA>\n';
}

//displayStringInWindow(xmlstring,0,1); 


return xmlstring ;
}

//=======
// parse a csv string and return in an array
function parseCSVline(aline) {

var j,achar,quoteon,ith,nchars,aword,line1;

quoteon=0;
ith=0;
aword='';
nchars=0;
line1=[];
for (var j=0;j<aline.length;j++) {
  achar=aline.substr(j,1);
  if (quoteon==1) {                        // quote is on, so build a string until next quote
      if (achar=='"') {
             quoteon=0;
             continue ;
      } else {
               nchars++ ;
               aword=aword+achar
               continue ;                       // skip the " character
      }
   } else {                             // not in quote
       if (achar=='"') {                // " starts a quote
             quoteon=1;
             continue ;
       }
       if (achar==',') {                // , signals end of word
               line1[ith]=aword ;
               ith++;
               aword='';
               nchars=0;
               continue ;
        } else {
                nchars++ ;
                aword=aword+achar
                continue ;
        }
   }
}                        
line1[ith]=aword ;
return line1;
}


// ===================================
// give a dom node, copy it's contents to workData. 
// Reads <data> nodes from this dom node, and copy to workData
// goo:  domlist of <DATA> nodes
// rlist: list of rounds (*=save all rounds)
// whereFrom: string describing source of this data
// isSubmit : if 1, from submitData (so process differently
// expandG : if non '', then expand "arrays" from type="G" nodes (using "," as a delimiter. 1: use xx_n, 2: use xx[n]

function make_workData(goo,rlist,whereFrom,isSubmit,expandG) {
   var tt,ids,rounds,atime,atype ;
   var m,goo2,goo,awork,aid,around,m2,anode,nname2,aval,varlist,ww;
   var rr,vvars,vvar1,vval1,vvars,vvals,mv;
   var nname3,tmpa,iss;

   if (arguments.length<2) rlist='*' ;
   if (arguments.length<3) whereFrom='n.a. ' ;
  if (arguments.length<4) isSubmit=0;
  if (arguments.length<5) expandG=0;

  if (expandG!==0 && expandG!==1 && expandG!==2) {
        alert('make_workData error: expandG value is wrong: '+expandG);
        return 0;
   }

   var allvars={};                                   // object containing variable names that have been specified in any child of a DATA node
   var ndid=0;

    tt='';
    ids={}; ids['*']=1 ;
    rounds={}; rounds['*']=1;

// reset workdata
     workData={};
     workData['*']={} ;
     workData['*']['*']={};


// process the contents of goo -- which may be from an xml file
    for (var m=0;m<goo.length;m++) {                    // all the <DATA> nodes

       var awork={} ;                                    // store variables for current <DATA> node
       if (isSubmit==1) {                       // from a submitData archive
           aid=getAttributes(goo[m],'id','');
           ids[aid]=1;
           around=getAttributes(goo[m],'round','');
           rounds[around]=1;
           atime=getAttributes(goo[m],'time','');
           awork['TIME']=atime;                       // save the value, for this <DATA> record, of this variable
           allvars['TIME']=1;                        // set a flag indicating that this variable exists
           rr=E2_getNodes(goo[m],'vars');
           vvars=getWord(E2_getNodeText(rr[0],1,''));
           rr=E2_getNodes(goo[m],'vals');
           vvals=E2_getNodeText(rr[0],1,'').split(',');
           for (var mv=0;mv<vvars.length;mv++) {
                vvar1=cvtFromArrayName(vvars[mv]);
                nname2=strip(vvar1.toUpperCase());
                vval1=unescape(vvals[mv]); 
                aval=vval1.replace(/\&/gi,'&amp;');
                awork[nname2]=aval;                       // save the value, for this <DATA> record, of this variable
                allvars[nname2]=1;                        // set a flag indicating that this variable exists
           }
   
        } else {                                // not SUBMITTED data

          goo2=Concat_Arrays(goo[m].getElementsByTagName('*'));            // clone all children of this DATA node (Concat_Arrays does the cloning)
          awork={} ;                           // for storing variables for this <DATA> record.
          aid='*'; around='*';                // note: default of * means "for all rounds or all ids"
          for (var m2=0;m2<goo2.length;m2++){              // examine each of the DATA node children
           anode=goo2[m2];
           nname2=strip(goo2[m2].nodeName.toUpperCase());
           atype=getAttributes(goo2[m2],'type','');
           aval=E2_getNodeText(goo2[m2]);
           if (nname2=='ID') {                            // ID varname is special
                aval=strip(aval);
                if (aval==='') aval='*' ;
                aid=aval;
                ids[aid]=1;
            } else if (nname2=='ROUND') {                 // ROUND varname is special
                 aval=strip(aval);
                 if (aval==='') aval='*' ;
                 around=aval;
                 if (rlist!='*') {                          // do a round check 
                        if (rlist.indexOf(around)<0) continue ;
                 }
                 rounds[around]=1;
             } else {                                      // save to awork if NOT id and NOT round

               if (expandG==0 || atype!=='G' || aval.indexOf(',')<0 ) {  // no expansion, not a GENERAL, no comma...
                  awork[nname2]=aval;                       // save the value, for this <DATA> record, of this variable
                  allvars[nname2]=1;                        // set a flag indicating that this variable exists
               } else {                         // expand GENERAL array
                   tmpa=aval.split(',') ;
                   for (iss=0;iss<tmpa.length;iss++) {
                       nname3=nname2+'_'+iss+'_';
                       if (expandG==2) nname3=nname2+'['+iss+']';
                       awork[nname3]=tmpa[iss];                       // save the value, for this <DATA> record, of this variable
                       allvars[nname3]=1;                        // set a flag indicating that this variable exists
                    }                                   // expand x,y,z ...
               }                                        // expand?
            }                                           // save to awork (not ID or ROUND)
          }                                 // (m2) all children of this <DATA> now read
     }                                  // (isSubmit) not SUBMITTED

// The subnodes for this <DATA> have now been read. Now save to the proper round/id in workData
     if (rlist!='*') {                          // do a round check 
        if (rlist.indexOf(around)<0) continue ;
     }

// Notes on * value for aid, and around
// Note that if aid='*', the values in this record are for ALL ids (in this round)
//      simiarly, if around='*', the values in this record are for all rounds (of this participant)
//          both ='*' -- for all rounds, all ids

// make sure object "tree" is ready (initialize if this is the first <DATA> node for this round or for this id
     if (typeof(workData[around])=='undefined') workData[around]={};
     if (typeof(workData[around][aid])=='undefined') {
                workData[around][aid]={};
     }

     varlist=[] ;
     ndid++ ;
     for (var ww in awork ) {                            // move from temp to permanent storage (for this record)
           varlist=varlist.concat(ww) ;         // an array of variables specifie for this <DATA> record
           workData[around][aid][ww]=awork[ww];  // the value of this variable for this round & client. Note that the [ct] is NOT supported -- so multiple >DATA> nodew s (for a single round/client) will overwrite each other
     }
      workData[around][aid]['vars']=varlist ;

  }                                   // (m) end of this DATA record

// info on all records
    workData['ids']=[];
    workData['rounds']=[];
    workData['vars']=[];

   for (var ww in ids ) {                            // lsit of all ids
        workData['ids']=workData['ids'].concat(ww)
   }
   for (var ww in rounds ) {                            // lsit of all records
       workData['rounds']=workData['rounds'].concat(ww) ;
   }
   for (var ww in allvars ) {                            // lsit of all variables
       workData['vars']=workData['vars'].concat(ww) ;
   }
  workData['source']=whereFrom ;

  writeDoStatus('For: '+whereFrom+'; the number of DATA rows stored in workData= ' +ndid,'','');
  return 1;
}


// =================================== nn wherefrom allvars 'roun
// create resultsData from workData.
// chkRound: optional. If not '', should be a variable name. This variable should contain the 
//           value of the "current round".  If the "round used in workdata" does NOT match chkRound
//           an alert is issued. Note that the "round used" is either the largest round, or the round
//           specified in usernd0
// usernd0: Optional. A variable name. It should contain the value of the round you wish to use (the 
//         round in workData you wish to extract data from, and write to resultsData). If not specified
//         use the maximum round in workData

function    workToResults(chkRound,usernd0,roundReadR)  {

var rlist,maxround,curlist,rdata,gotall,uvarsLookup,uvars,im,nowid,vlist,iv,aval,avar,m,uvars2,cround ;

if (arguments.length<3) roundReadR='';

if (roundReadR!='') {                                   // set to blank by default
  var vv=getVarInfo(roundReadR) ;
  if (vv.type=='CONTROL') {
        controlVars[vv.name]='' ;
  }
  if (vv.type=='GENERAL') {
        saveGeneralVar(vv.name,'');
  }
}

resultsData={} ;                                // zap existing results data
if (typeof(workData)=='undefined')  return 0   ;        // no workdata, give up
usernd0=strip(usernd0);

rlist=workData['rounds'];
if (usernd0==='') {
  maxround=-11111111111111 ;
  for (var im=0;im<rlist.length;im++)  {
      if (!isNaN(rlist[im])) {
         maxround=Math.max(maxround,rlist[im]);
      }
  }
} else {                                // use the useRound attribute
  maxround=getVarInfo(usernd0,'value','control');
}

if (isNaN(maxround)) {
    alert('workToResults error: bad maximum round number = ' +maxround + '( in ' + rlist);
   return 0;
}

curlist=workData['ids'] ;

 if (chkRound!='') {
   cround=getVarInfo(chkRound,'value','control');  
   if (cround!=maxround)  {
       var qq=confirm('Warning: the results round ('+maxround+') is not equal to the current round ('+cround+'). Do you want create resultsData? ');
       if (!qq) {
        writeDoStatus('Results round not created (mismatch between current round and resultsData round ') ;
        return 0;
      } else {
        writeDoStatus('Warning: creating resultsData even though there is a mismatch between current round and resultsData round ') ;
      }
   }
 }                              

if (roundReadR!='') {
  var vv=getVarInfo(roundReadR) ;
  if (vv.type=='CONTROL') {
        controlVars[vv.name]=maxround ;
  } 
  if (vv.type=='GENERAL') {
        saveGeneralVar(vv.name,maxround);
  } 
}

rdata=workData[maxround] ;
resultsData['$ROUND']=maxround ;
resultsData['$IDS']=curlist ;
resultsData['*']={};

gotall=1;
if (typeof(workData['*'])=='undefined') gotall=0;
uvarsLookup={};

for (var im=0; im<curlist.length;im++) {                        // FOR each ID
  nowid=curlist[im];
  if (typeof(resultsData[nowid])=='undefined') resultsData[nowid]={}; 
  vlist=[] ;

  if (typeof(workData[maxround])!='undefined' && typeof(workData[maxround][nowid])!='undefined')  vlist=workData[maxround][nowid]['vars'];
  if (gotall==1 && typeof(workData['*'][nowid])!='undefined'  && typeof(workData['*'][nowid]['vars'])!='undefined'  )  {
      if (workData['*'][nowid]['vars'].length>0)  vlist=Concat_Arrays(vlist,workData['*'][nowid]['vars']);
   }
  for (var iv=0;iv<vlist.length;iv++) {                         // just use variables defined for be liberal: create all variables for each id 
     aval=null;                                                           // null used for now
     avar=strip(vlist[iv].toUpperCase()) ;
     if (typeof(workData[maxround])!='undefined' &&  typeof(workData[maxround][nowid])!='undefined') {              // data for this id?
           if (typeof(workData[maxround][nowid][avar])!='undefined') aval=workData[maxround][nowid][avar] ;
     }                                                  //  no data for this round / id
    if (aval==null) {                            // not round specific value. Perhaps all-round value ( * round)
         if (gotall==1 && typeof(workData['*'][nowid])!='undefined') {              // default (all rounds) data for this id?
                     if (typeof(workData['*'][nowid][avar])!='undefined') aval=workData['*'][nowid][avar] ;     // all round data for this variable and id
          }                                       // '*' round exists for this id
    }                                            // aval='' 
    if (nowid!='*' && typeof(uvarsLookup[avar])=='undefined') uvarsLookup[avar]=1;
    if (aval==null) aval='';                                    // no find, use ''
    resultsData[nowid][avar]=aval ;
  }
}

uvars=[];
for (var m in uvarsLookup) uvars=uvars.concat(m);               // all variables mentioned in at least one user (either in maxround or in * round
resultsData['$VARS']=uvars ;

// now find general (* id) variables
uvarsLookup={};
vlist=[] ;
if (typeof(workData[maxround])!='undefined' && typeof(workData[maxround]['*'])!='undefined')  vlist=workData[maxround]['*']['vars'];

if (gotall==1 && typeof(workData['*']['*'])!='undefined' && typeof(workData['*']['*']['vars'])!='undefined'  )  {

  if (workData['*']['*']['vars'].length>0)  vlist=Concat_Arrays(vlist,workData['*']['*']['vars']);
}

for (var iv=0;iv<vlist.length;iv++) {                            //  create all variables for each id 
     aval=null;                                                           // null used for now
     avar=strip(vlist[iv].toUpperCase()) ;
     if (typeof(workData[maxround])!='undefined' && typeof(workData[maxround]['*'])!='undefined') {              // non client specific data for this round?
           if (typeof(workData[maxround]['*'][avar])!='undefined') aval=workData[maxround]['*'][avar] ;
     }                                                  //  no data for this round / id
    if (aval==null) {                            // not round specific value. Perhaps all-round value ( * round)
         if (gotall==1 && typeof(workData['*']['*'])!='undefined') {              // default (all rounds) data for this id?
                     if (typeof(workData['*']['*'][avar])!='undefined') aval=workData['*']['*'][avar] ;     // all round data for this variable and id
          }                                       // '*' round exists for this id
    }                                            // aval='' 
    if (typeof(uvarsLookup[avar])=='undefined') uvarsLookup[avar]=1;
    if (aval==null) aval='';                                    // no find, use ''
    resultsData['*'][avar]=aval ;
}

uvars2=[];
for (var m in uvarsLookup) uvars2=uvars2.concat(m);               // all variables mentioned in at least one user (either in maxround or in * round
resultsData['*']['$VARS']=uvars2 ;

return 1;


}


// -----------
//  function to toggle set attribute when  click on create results_data button
function setRButton(athis) {  
iset=getAttributes(athis,'SET','');  
if (iset!=1) {
  athis.setAttribute('set',1);
  athis.style.backgroundColor='lime'; 
  athis.style.color='blue';
  athis.style.fontSize='100%';
  athis.setAttribute('title','Click here to NOT create resultsData');
} else {
   athis.setAttribute('set',0);
   athis.style.backgroundColor='#eaeaea'; 
   athis.style.color="#777780"; 
   athis.style.fontSize='95%'; 
   athis.setAttribute('title','Click here to CREATE resultsData');
}
return 1 ;
}               //end of rbutton local function
 


// -----------
//  function to toggle set attribute when  click on create results_data button
function setCSVButton(athis) {  
iset=getAttributes(athis,'SET','');  
if (iset!=1) {
  athis.setAttribute('set',1);
  athis.style.backgroundColor='lime'; 
  athis.style.color='blue';
  athis.style.fontSize='100%';
  athis.setAttribute('title','Click here to read as XML');
} else {
   athis.setAttribute('set',0);
   athis.style.backgroundColor='#eaeaea'; 
   athis.style.color="#777780"; 
   athis.style.fontSize='95%'; 
   athis.setAttribute('title','Click here to read as CSV');
}
return 1 ;
}               //end of CSVbutton local function
 


//========================
//==============
// Initialize the  "scroll value" bar, save stuff to scrollValueStorage and write default scroll bar to screen
 
function doScrollValueInit(iat) {
var m,goo,astyle;
var red0,blue0,green0,ruse,buse,guse,rdiff,bdiff,gdiff;
var scrollValueId,ac16,aaval ;
var aadd='';
var barWidth,maxVal,minVal,iDivs,ipad,afmt;
var fillN,atitle,fillNuse ;
var barName,amess1 ;
var ac16s=[];
var tvalue,afrac,vdiff;

if (arguments.length<1) ido=0 ;                                 // if no argument, start with "empty" scroll bar

barName=strip(scrollValueStorage[iat].barName).toUpperCase() ;


if (typeof(scrollValueStorage[iat])=='undefined') {
  alert('Error: undefined scrollValueStorage['+iat+']');
  return 1;
}
scrollValueId=scrollValueStorage[iat].barID ;
red0=scrollValueStorage[iat].red0 ;
green0=scrollValueStorage[iat].green0 ;
blue0=scrollValueStorage[iat].blue0 ;
rdiff=scrollValueStorage[iat].rdiff ;
gdiff=scrollValueStorage[iat].gdiff ;
bdiff=scrollValueStorage[iat].bdiff ;

barWidth=scrollValueStorage[iat].barWidth ;

maxVal=scrollValueStorage[iat].amax ;           // max and min values
minVal=scrollValueStorage[iat].amin ;
iDivs=scrollValueStorage[iat].nDivs                    // # of divisions

afmt=scrollValueStorage[iat].afmt ;


ipad=Math.ceil(barWidth/iDivs) ;    // width of each division in the scroll bar

for  (var m=0;m<=iDivs;m++) {
    ruse=Math.ceil(red0+(m*rdiff));  guse=Math.ceil(green0+(m*gdiff)); buse=Math.ceil(blue0+(m*bdiff));
    ac16s[m]='#'+ruse.toString(16)+guse.toString(16)+buse.toString(16);

}
scrollValueStorage[iat].ac16=ac16s ;

//aadd='<div style="background-color:yellow" id="goo"> zz</div>';
//aadd=aadd+'<span style="padding:10px;border:1px solid green" id="'+barName+'_DIV" onMouseOut="scrollBarLeave('+iat+')" >' ;

aadd='';
aadd=aadd+'<span style="padding:12px" id="'+barName+'_DIV"  isOuter="1" IDscroll="'+iat+'" >' ;  // mouseout event added here

// left border (minval)
//  goo='<span class="sbLeft"  id="'+barName+'__0" onClick="doScrollValue(0,'+iat+'); scrollValueFill(0,'+iat+')"  title="'+minVal+'"  > &nbsp;</span>  \n';
//  aadd=aadd+goo ;


// Clickable blocks 
fillN=' style="padding-left:'+ipad+'px " ';
vdiff=maxVal-minVal ;

for  (var m=0;m<=iDivs;m++) {
    fillNuse=fillN; amess1=''; 

    afrac=m/iDivs ;
    tvalue=minVal+(vdiff*afrac) ;
    if (!isNaN(afmt)) tvalue=tvalue.toFixed(afmt);
    atitle=' title="'+tvalue+'"';

    if (m==0) {
        atitle='title="'+minVal+'" ';  
        fillNuse=' style="padding-left:'+(2*ipad)+'px " ';
    }
    if (m==iDivs) {
        atitle='title="'+maxVal+'" ';
        fillNuse='style="padding-left:'+(2*ipad)+'px " ';
    }
    goo='<span '+atitle+' class="scrollBarNo" id="'+barName+'_'+m+'" '+ fillNuse +' onMouseOver="doScrollValue('+m+','+iat+')"  onClick="scrollValueFill('+m+','+iat+')"   >'+amess1+' </span>  \n';
    aadd=aadd+goo ;
}

// right border (max)
//goo='<span class="sbRight" id="'+barName+'__'+iDivs+'"  onClick="doScrollValue('+iDivs+','+iat+');scrollValueFill('+iDivs+','+iat+')"    title=" '+maxVal+'"  >&nbsp; </span>  \n';
//aadd=aadd+goo ;

aadd=aadd+'</span>';

scrollValueId.innerHTML=aadd;                     // and update the scroll var

                                                         
return 1 ;

}                               // end of doScrollValue


//==============
// leaving a scroll bar -- but check for internal mouseouts
function scrollBarLeave(e) {
if (!e) var e = window.event;                   
var iat,iatd,iouter,reltart,vv,iid,vid;

var relTarg = e.relatedTarget || e.toElement;   // the element the mouseout is going INTO (so if mouseout in v1, and you go from v1 to v2, then reltarg points to v2
try {
   var iouter=relTarg.getAttribute('isOuter');     // the isOuter attribute of the element that the mouseout went to (NOT the element with the mouseout)
}
catch(err) {                    // wierd error? just catch it and give up
  return 0 ;
}

if (iouter!=='1') return 0;

iat=this.getAttribute('IDscroll');              // the IDscroll attribute of the element with the mouseout
vid=scrollValueStorage[iat].myID ;
doScrollValue(0,iat,vid);               // and reset to currently set value

//if (typeof(igoo)=='undefined') igoo=0;
//igoo=igoo+1;
//iatd=this.getAttribute('id');
//vv =scrollValueStorage[iat].barName;
//var iid=document.getElementById('goo');
//iid.innerHTML=iid.innerHTML+' | '+ iat+' '+ iatd +' '+igoo ;

return 0;
}


//==============
// Create a "scroll value" bar. Mouseover the bar and the bar fills in from left edge to mouse location.
// Click on the bar and a value, that depends on the mouse location within the bar, will be written to an input field.
 
function doScrollValue(ido,iat,athis) {
var m,goo,astyle;
var red0,blue0,green0,ruse,buse,guse,rdiff,bdiff,gdiff;
var scrollValueId,ac16,aaval ;
var aadd='';
var barWidth,maxVal,minVal,iDivs,ipad;
var barname;
var useid ;

if (arguments.length<1) ido=0 ;                                 // if no argument, use min value
if (arguments.length<2) iat=0 ;                                 // use first scroll bar

if (typeof(scrollValueStorage[iat])=='undefined') {
  alert('Error: undefined scrollValueStorage['+iat+']');
  return 1;
}
scrollValueId=scrollValueStorage[iat].barID ;
barname=strip(scrollValueStorage[iat].barName).toUpperCase() ;
lastIdo=scrollValueStorage[iat].lastIdo ;

maxVal=scrollValueStorage[iat].amax ;           // max and min values
minVal=scrollValueStorage[iat].amin ;
iDivs=scrollValueStorage[iat].nDivs                    // # of divisions

if (arguments.length==3) {                      // typed in value to scroll bar conversion
  if (typeof(athis)!='object') {
        aaval=athis*1;
  } else {
       aaval=athis.value*1 ;
  }
  if (isNaN(aaval)) {
      ido=0 ;
  } else { 
      if (aaval<=minVal) {
         ido=0;
      } else if (aaval>=maxVal) {
         ido=iDivs ;
      } else {
         ido=Math.ceil(iDivs*(aaval-minVal)/(maxVal-minVal));
      }
  }
}


// We moved right (past prior position). So use color ramp to change color values Blocks < current value (to the left of the cursor). Note use of color-ramp
if (ido>lastIdo) {
  for  (var m=lastIdo;m<=ido;m++) {
    useid=scrollValueStorage[iat].barIds[m] ;
    useid.setAttribute('class','scrollBarYes');  
    useid.className='scrollBarYes';
    useid.setAttribute('class','scrollBarYes'); 
    useid.setAttribute('className','scrollBarYes');
    useid.style.backgroundColor=scrollValueStorage[iat].ac16[m] ;
    useid.style.borderLeftColor=scrollValueStorage[iat].ac16[m] ;
   }
}

// Blocks > current value (to the right of the curso)
// We moved left (before prior position). So set previously colored spans to the no color 
if (ido<lastIdo) {
  for (var m=ido;m<=lastIdo;m++) {
    useid=scrollValueStorage[iat].barIds[m] ;
    useid.setAttribute('class','scrollBarNo'); 
    useid.className='scrollBarNo';
       useid.setAttribute('class','scrollBarNo');      useid.setAttribute('className','scrollBarNo');
    useid.style.backgroundColor='#8899aa'; 
    useid.style.borderLeftColor='#aaaaaa';
   }
}

scrollValueStorage[iat].lastIdo=ido ;

return 1 ;

}                               // end of doScrollValue


// ============
// set value when click on 
// assumes that scrollValueStorage[iat] is properly setup
function scrollValueFill(im,iat) {
var vv0,vv1,afrac,vdiff,afmt,newvalue,valueBoxID,ddiv;

vv0=scrollValueStorage[iat].amin ;
vv1=scrollValueStorage[iat].amax ;
ddiv=scrollValueStorage[iat].nDivs ;
afmt=scrollValueStorage[iat].afmt ;

vdiff=vv1-vv0 ;
afrac=im/ddiv ;
newvalue=(1*vv0)+(vdiff*afrac) ;
valueBoxId=scrollValueStorage[iat].myID ;
var tt=newvalue.toFixed(afmt);
valueBoxId.value=tt ;

valueBoxId.setAttribute('value',tt);
valueBoxId.setAttribute('set',tt);
storeFormVal(valueBoxId)


}


//===================
// intialize scrollValue input fields.
function initScrollValues(dolist) {
var idiv,iadd,amin,amax,afmt,barID,sid,barWidth,nDiv,iido,aid ;
var lowc,highc,jdef,m,tbarid;

if (typeof(scrollValueStorage)=='undefined') scrollValueStorage=[];

var rdiff,gdiff,bdiff;
for (var iido=0;iido<dolist.length;iido++) {                    // -- a list of nodes that are scrollValue

var red1=171,green1=205,blue1=240 ;                
var red0=Math.ceil(red1/1.3);green0=Math.ceil(green1/1.3),blue0=Math.ceil(blue1/1.3 ) ;    

idiv=dolist[iido];

iadd=scrollValueStorage.length ;                
scrollValueStorage[iadd]={} ;                   // store parameters for this "scollValue"
amin=getAttributes(idiv,'min minimum ','0'); 
if (isNaN(amin)) amin=0;
amax=getAttributes(idiv,'max maximum ','100');

afmt=getAttributes(idiv,'fmt format','0');

jdef=getAttributes(idiv,'def default','0'); 

barID=getAttributes(idiv,'where bar scrollWhere barid','scrollValue');

sid=document.getElementById(barID);
if (!sid) {
  alert('Error in initScrollValues: bad where (for scrollBar): '+barID);
  return -1 ;
}

barWidth=getAttributes(sid,'width barWidth ',100);
if (isNaN(barWidth) ) {
  alert('Error in initScrollValues ('+ado+'): bad barWidth in '+barID+' = ' +barWidth) ;
  return -1;
}

nDiv=getAttributes(sid,'cell cells div divs  ',33); if (isNaN(nDiv)) nDiv=33 ;


//       lowColor="22aa88" highColor="55ff99"  /><br/>

clow=getAttributes(idiv,'color0 lowColor leftColor','');
if (clow!=='') {
  if (clow.substr(0,1)=='#') clow=clow.substr(1);
  red0=parseInt(clow.substr(0,2),16);
  green0=parseInt(clow.substr(2,2),16);
  blue0=parseInt(clow.substr(4,2),16);
}

chigh=getAttributes(idiv,'color1 highColor rightColor','');
if (chigh!=='') {
  if (chigh.substr(0,1)=='#') chigh=chigh.substr(1);
  red1=parseInt(chigh.substr(0,2),16);
  green1=parseInt(chigh.substr(2,2),16);
  blue1=parseInt(chigh.substr(4,2),16);
}

red1=Math.max(red1,red0);  green1=Math.max(green1,green0) ; blue1=Math.max(blue1,blue0);        // basic values used to create color ramp for left-of-cursor divisions
rdiff=(red1-red0)/nDiv; bdiff=(blue1-blue0)/nDiv ; gdiff=(green1-green0)/nDiv ;

scrollValueStorage[iadd].barName=barID;

scrollValueStorage[iadd].myID=idiv;
scrollValueStorage[iadd].amin=parseFloat(amin) ;
scrollValueStorage[iadd].amax=parseFloat(amax) ;
scrollValueStorage[iadd].afmt=afmt ;
scrollValueStorage[iadd].barID=sid  ;
scrollValueStorage[iadd].barWidth=barWidth ;
scrollValueStorage[iadd].nDivs=parseInt(nDiv) ;

scrollValueStorage[iadd].red0=red0 ;
scrollValueStorage[iadd].green0=green0 ;
scrollValueStorage[iadd].blue0=blue0 ;
scrollValueStorage[iadd].gdiff=gdiff ;
scrollValueStorage[iadd].rdiff=rdiff ;
scrollValueStorage[iadd].bdiff=bdiff ;
scrollValueStorage[iadd].lastIdo=0 ;

//  initialize the bar
doScrollValueInit(iadd);


// a shortcut (do it here so as to give a teeny pause)
scrollValueStorage[iadd].barIds=[] ;
tbarid=strip(barID).toUpperCase();
for  (var m=0;m<=scrollValueStorage[iadd].nDivs;m++) {
    aid=document.getElementById(tbarid+'_'+m );
    scrollValueStorage[iadd].barIds[m]=aid;
}

// add a mouse out
aid=document.getElementById(tbarid+'_DIV');

/* ===
     if (window.attachEvent){
        chkElements.attachEvent('on'+etype,efunc); // ie 
     } else {
        chkElements.addEventListener(etype,efunc,false);
    }
=== */

if (window.attachEvent){
    aid.attachEvent('onmouseout',scrollBarLeave); // ie 
} else {
   aid.addEventListener('mouseout',scrollBarLeave,false);
}

//aid.addEventListener('mouseout',scrollBarLeave,false);


// and set it to correct position
doScrollValue(0,iadd,idiv);  

// add a onchange event to value box (that will change scroll bar position on change in the value box)
var onc=idiv.getAttribute('onChange');
if (onc!=='') {
  onc=onc+';'+'doScrollValue(0,'+iadd+',this)';
} else {
  onc='doScrollValue(0,'+iadd+',this)' ;
}
idiv.setAttribute('onChange',onc);

}                                       // dolist

return iadd ;
}


