/* library of message send/retrieve functions

Note: these libraries use a few globals, that should be initialized as follows (you can
change a few of the values):
  messageData={};
  messageData['MESSAGE_OPTIONS']='DISABLE' ;   // messages are disabled     
  messageData.on=1 ;                                    // if set to 0, messages are disabled
  messageData.messages=[];
  messageData.notes=[];


 each row should be an object with three fields: .full, .shown, and .row
    messageData.messages[i].full=[];                  // a semi-permanent storage for retrieve messages (used in full message display
    messageData.messages[i].shown=[];                // was this message shown to participant (or otherwise viewed)
    messageData.messages[i].row=[];                  // row used in table of messages

  clientMessageOptions={}; 
  clientMessageOptions.isOpen=0;                

  clientMessageOptions.myname= name of participant. For example:
      clientMessageOptions.myname=Read_Global('myname',' ERROR: no NAME specified.');
  clientMessageOptions.myid=id of participant. For example:
     clientMessageOptions.myid=Read_Global('MYID',' ERROR: no ID specified ');

  You might also find useful the following (set in svylibe2.js, when "message" button is created)
        clientMessageOptions.buttonId=itmp;
        clientMessageOptions.buttonIdOrigStyle=itmp.style.display ;


  For the SEND=IF ... to work, the following should exist:
        messageData['$capVars'] 
   In particular:
    messageData['$capVars']['$list']=[];           List of variable (actually, arrays) saved to <message>
    messageData['$capVars']['$ct']=0;            Maximum elements in these arrays
    messageData['$capVars'][varname][i=0...$ct] : Values of the varname array


These libraries also use two "moveable boxes", numbered 7 and 8. Is the primary area
used for display, with 8 used as needed.

 dsurveyMessage(alist)
   Set general options, for storage in the clientMessageOptions global.
  
   For example: how (or whether) to show message button
   Alist should be a comma delimited string -- see sampleDescriptor.xml for the details
   (under message_options)

   If alist not specified, then use the current value of messageData['MESSAGE_OPTIONS']

   dsurveyMessage will also start "polling", if a non-zero poll value is specified (i.e.; poll=30).
      or... stop polling if poll=0
   Note that polling will continue until a new poll option is detected, which only happens
   when a new section is started, one that reads a CAP file

function doClientMessages() 
    Displays read/send message form (in movebox 7). 

    Notes:
      * clientMessageOptions is used by doClientMessages. 
         This means that before doClientMessages is called ...
            dsurveyMessage should be called, with an "alist" (or a value of
            messageData['MESSAGE_OPTIONS']) that "enables" messages
     * doClientMessages is often called when the participant hits a button with an 
       approrpiate onClick (for example, as setup by <message> in <init> in a spec file

        
Other functions. Casual programmers should just use doClientMessage (which uses these functions).

   readClientMessages()

      download messages from server and display them
      A destructive read is done: the server-side message box file will be deleted after the read 
      Note that readClientMessages REQUIRES that clientMessageOptions.myid
      and clientMessageOptions.myname4 be set correctly.


    showClientMessage(ido)  
           display (in moveBox 8) the idot'h message  of the participant's
           message database, which is created by readClientMessage.
           This is typically invoked by clicking on the a message-specific button created by
           readClientMessages
      
    choseAddress_send()     (a front-end to composeClientMessage)
          display  a list of addresses, that the client can then click on. The clicked on
          address will be used as the recipient of the message  
    
    byName_send(uname)    (a front-end to composeClientMessage)
        Resolves uname to a clientId, and then calls composeClientMessage

    composeClientMessage is used by choseAddress_send and byName_send.
    It is also used inthe form created by readClientMessages (to reply to a message)

    composeClientMessage can be called standalone -- just be sure the aid and aname
    are correctly "aligned"

    composeClientMessage(aid,aname,oldSubject)
          Compose a message, and then send it.
                aid: clientID -- this is used as an adddres
                aname: name to send to. This is displayed, and is NOT used to form the address.
                asubject: Displayed in subject line. Can be  ''

             This creates a form that has a subject textbox and a content textbox.
             After entering stuff in both textboxes, the   participant then clicks a "send" button. 
             Which causes transmitClientMessage to be called
   
            transmitClientMessage(ido,aid,aname)   -- used by composeClientMessage
                   Transmit a message to the server (using subject, etc set in composeClientMessage)


function  messageVar('varname',i)
   Extract value of a varname variable from messageData, for the ith participant.
     If i is not specified, the value of  messageData'$index'] will be used.
         If this is undefined, an error message is displayed.
   

*/


//=====================
// change client message options (as stored in the global var clientMessageOptions )


function dsurveyMessages(alist) {

 var dd,dd2 ;
 var aword,alist,tmp,tmp2,ic,tt ;

 var rrr=Math.random();

 if (arguments.length<1)  {
    alist=messageData['MESSAGE_OPTIONS'];
 } else {
     messageData['MESSAGE_OPTIONS']=alist ;
 }

  if (messageData.on==0) return 0 ;                      //  note: <message> in <init> sets this to 1

  tmp=strip(alist);
  tmp2=getWord(tmp,0,',');

  for (var ic=0;ic<tmp2.length;ic++) {
            aword=strip(tmp2[ic]).toUpperCase();

            if (aword=='') continue ;
            if (aword=='NOTIFY') clientMessageOptions.notify=1 ;
            if (aword=='NONOTIFY') clientMessageOptions.notify=0 ;
            if (aword=='NOTES') clientMessageOptions.notes=1 ;
            if (aword=='NONOTES') clientMessageOptions.notes=0 ;
            if (aword=='SHOW') clientMessageOptions.show=1 ;
            if (aword=='NOSHOW') clientMessageOptions.show=0 ;
            if (aword=='ENABLE') clientMessageOptions.enable=1 ;
            if (aword=='DISABLE') clientMessageOptions.enable=0 ;
            if (aword=='NOTESONLY') clientMessageOptions.enable=2 ;
            if (aword=='NOSEND' || aword=='NO' ) clientMessageOptions.send='NO' ;
            if (aword=='ADMINSEND' || aword=='ADMIN') clientMessageOptions.send='ADMIN' ;
            if (aword=='SENDALL') clientMessageOptions.send='ALL' ;
            if (aword.substr(0,7)=='SENDIF=' || aword.substr(0,7)=='IFSEND=') {
                  clientMessageOptions.send='IF' ;
                  clientMessageOptions.sendIf=aword.substr(7) ; 
            }

            if (aword=='NOPOLL') clientMessageOptions.poll=0 ;
            if (aword.substr(0,5)=='POLL=') {
                tt=strip(aword.substr(5));
                if (isNaN(tt)) {
                    alert('Error in dsurveyMessage: poll value is not a number: '+aword);
                    return 0;
                }
                clientMessageOptions.poll=Math.max(0,tt) ;   // 0 means "no polling"
            }
   }

// do a few of the options

  if (clientMessageOptions.show==1) {
    clientMessageOptions.buttonId.style.display=clientMessageOptions.buttonIdOrigStyle;
  } else {
    clientMessageOptions.buttonId.style.display='none' ;
  }

  if (clientMessageOptions.enable==0) {
    clientMessageOptions.buttonId.style.display='none' ;
    close_moveBox(7);
    close_moveBox(8);
    return 0 ;
  }

  if (clientMessageOptions.enable==2) {                         // notesonly
//  clientMessageOptions.buttonId.style.display='none' ;
    close_moveBox(7);
//  close_moveBox(8);
//    doMessageNotes()
    return 0 ;
  }

// start polling?
   if (parseInt(clientMessageOptions.poll)!==0)  {
        dd=new Date();
        dd2=dd.valueOf();
        Launch_Proc('doClientMessages',clientMessageOptions.poll*1000,1,dd2);
   }

  return 1;
}


//=====================
// A main function that reads/sends messagse (used by participants)
// note: on polling,    messageData['$lastRetrieve'] is checked (it is time of last "read" --
//        If it is after the time of the polling occurred (which is
//        the value of the arecent variable, then don't process (but do poll again).
//        This is meant to prevent auto retrieval when a manual retrieval has just occurred


function doClientMessages(ido,arecent) {

var i1,i2,i3,tmp,tmp1,tmp2,amess,stuff,oldbkg;
var dd,dd2,dda,dda2,rlast ;

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

if (typeof(clientMessageOptions)=='undefined') {
   alert('Error in doClientMessage: clientMessageOptions global variable has not been created');
  return 0;
}
if (typeof(messageData)=='undefined') {
   alert('Error in doClientMessage: messageData global variable has not been created');
  return 0;
}

if ( typeof(clientMessageOptions.buttonId)=='undefined') return 0 ;            // give up

if (typeof(clientMessageOptions.buttonBackgroundColor)=='undefined') {
    clientMessageOptions.buttonBackgroundColor=clientMessageOptions.buttonId.style.backgroundColor;
}

if (exists_moveBox(7)==0 || exists_moveBox(8)==0 ) {
   alert('Error in doClientMessages: moveBox 7 and/or 8 were not created ');
  return 0;
}

if (messageData.on!==1) return 0 ;              // never turned on (say, if no <message> in <init>

if (clientMessageOptions.enable== 0 )  {
   close_moveBox(7);
   close_moveBox(8);
   return 0 ;           // was disabled
}


  if (clientMessageOptions.enable==2) {
//  clientMessageOptions.buttonId.style.display='none' ;
    close_moveBox(7);
//  close_moveBox(8);
    doMessageNotes()

// notes only: don't display retrieve messages screen

    return 0 ;           // was disabled
  }


// messaging is alive and well. So check if a polling call

dd=new Date();
dd2=dd.valueOf();
dda2=dd2+1 ;                        // make sure "last check" time is after "last read" time

if (arecent>0) {                                // this is from an auto call. Check to see if it is needed
   if (clientMessageOptions.poll==0) return 0 ;   // polling off, so give up
   rlast=messageData['$lastRetrieve'];
   if (rlast>arecent)  {                           // a manual call to doClientMessage, perhaps. So don't do again
      Launch_Proc('doClientMessages',clientMessageOptions.poll*1000,1,dda2);  // but DO continue polling
      return 0;
   }
   readClientMessages(1,1);          // download messages from server, write to moveBox (which might be "closed"
   messageData['$lastRetrieve']=dd2 ;                    // change date of "last time data retrieved from server
   Launch_Proc('doClientMessages',clientMessageOptions.poll*1000,1,dda2);  //  continue polling
   return 1 ;
}                       // note: polling does NOT imply displaying messages

// if here, non-polling call to here (i.e.; participant hit a button
if (clientMessageOptions.isOpen==1) {           // already open, so close
  clientMessageOptions.isOpen=0;
  close_moveBox(7);
  close_moveBox(8);
  return 0;
}
clientMessageOptions.isOpen=1;          // was close, so open  messages box
open_moveBox(7);

readClientMessages(1);          // download messages from server, and display them (with action buttons)

messageData['$lastRetrieve']=dd2 ;                    // change date of "last time data retrieved from server

return 1;
}


//=====================
// download messages from server and display them (destructive read: the server-side message box
// file will be deleted after the read

function readClientMessages(ido,isauto) {

var toGet,nread,alines,aline,m,j,tt,tt1,tt2,atime1,ngoo,avu,ilen,isread,m1;
var awords,atime,d,atime1,afrom,ato,arow,amess,asubject,asubjectUse,amessageUse,afromId,afromUse,afromName;
var iok,addrList,m0,aid,aid2,notifys,tmp,notifyName;
var aheader,asubjectUse0,afrom1,aurgent,amessage0 ;

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

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled

if (typeof(messageData.nthRead)=='undefined') messageData.nthRead=0;
if (typeof(messageData.compress)=='undefined') messageData.compress=1;

if (ido==1) {                   // retreive messages

//clientMessageOptions.buttonId.style.border="1px solid #efefef";
 clientMessageOptions.buttonId.style.backgroundColor="cyan";

   arand=''+Math.random(); arand=arand.substr(3,8);  // cache buster
   toGet=messageRetrieve+'session='+sessName+'&cbust='+arand+'&erase=1&client='+clientMessageOptions.myid;
//   alert('arf='+toGet);
   Get_File(toGet,'readMessageResults','read messages','readClientMessages',2 ) ;

  amess='<input type="button" value="close" onClick="close_moveBox(7)" /> '+
      '<span style="margin:1px 30px 1px 30px;background-color:#eeddee" '+
      '       title="Querying server for new messages. ..." '+
      '><span style="color:green">Retrieving messages from server ....</span> ';
    writeHeader_moveBox(7,amess);
   if (messageData.nthRead<1) writeContent_moveBox(7,'Wait while messages are retrieved ... ');
    messageData.nthRead++ ;
   return 0;
}

//('xxxx='+readMessageResults);
d =new Date();
tt=d.toLocaleTimeString();
nread=0;
notifys=[]; notifyName={} ;

// append the current messages to the prior messages
if (typeof(readMessageResults)=='string' && readMessageResults!=='') {

alines=readMessageResults.split('\n');

for (m=0;m<alines.length;m++) {
  aline=alines[m];
  if (strip(aline)=='') continue ;

  ilen=messageData.messages.length;
  messageData.messages[ilen]={}           ;             // holds the .full, .shown, and .row fields

  messageData.messages[ilen].full= aline;                // for use in large view
  messageData.messages[ilen].shown=0    ;                // was the message shown to participant

  awords=aline.split(',');
  aurgent=awords[0];
  atime=awords[1];
  d =new Date();
  d.setTime(atime);
  tt1=d.toLocaleTimeString()
  tt2=d.getHours()+':'+d.getMinutes();
  atime1='<span title="'+tt1+'" style="font-size:80%">'+tt2+'</span>';

  asubject=unescape(awords[5]);
  asubjectUse0=removeHTMLTags(asubject);

  afromId=awords[2] ;
  afromName=awords[3];

// can a reply be sent?
  iok=1;                                  // if not IF, it can be sent
  if (clientMessageOptions.send=='IF') {
   iok=0;
   addrList=addressListMake() ;

   for (var m0=0;m0<addrList.length;m0++) {
      m1=addrList[m0];
      if (typeof(messageData['ID'])=='undefined')  continue ;
      aid2=messageData['ID'][m1];
      if (aid2==afromId) {
         iok=1;
         break;
     }
   }
  }

  asubjectUse=asubjectUse0;
  if (asubjectUse.length>40) asubjectUse=asubjectUse.substr(0,40);

  if (iok==1) {
     afromUse='<span onClick="composeClientMessage(\''+afromId+'\',\''+afromName+'\',\'Reply to:'+asubjectUse+'\')"  '+
                 ' style="background-color:#cddefe;font-variant:small-caps" ' +
                  'title="Click to send a reply" >'+afromName+'</span>';
   } else {
     afromUse='<span style="background-color:#cddefe;font-variant:small-caps" >'+afromName+'</span>';
   }

  ato=_mbStrip(awords[4]);              // not used on participants side (admin uses it)
  avu='<span style="background-color:#eaea99;font-size:80%;padding-right:3px;" title="click to view this message" '+
       '     onClick="showClientMessage('+ilen+'); messageData.messages['+ilen+'].shown=1">vu</span>';
  asubjectUse='<span style="font-style:oblique;background-color:#fdfefd;padding-left:4px">'+avu+asubjectUse+'</span>';
  amessage0=unescape(awords[6]);
  amessage=removeHTMLTags(amessage0);

  if (messageData.compress==0) {
     amessageUse='<div style="font-size:12px;overflow:auto;height:36px">'+amessage+'</div>';
  } else {
     if (amessage.length>33) {
        amessageUse='<div style="font-size:12px;overflow:hidden">'+amessage.substr(0,33)+' ' +
        '<span style="margin-left:6px;height:3px;background-color:gold">...</span></div>';
    } else {
        amessageUse='<div style="font-size:12px;overflow:hidden">'+amessage+' </div>';
   }
  }

  tmp=checkIfNotify(aurgent,afromName);

/* == multi urgent message version (not currently used
  if (tmp==1) {
     afrom1='<span title="Message sent by ..." style="background-color:#cddefe;font-variant:small-caps" >'+afromName+'</span>';
     arow='<tr><td width="15%">'+afrom1+'</td>'+
        '<td width="35%" ><div style=" overflow:auto;height:36px">'+asubjectUse0+'</div></td> '+
        '<td width="50%" ><div style="font-size:12px;overflow:auto;height:60px">'+amessage+'</div> </td>'+
        '</tr>';
     notifys=notifys.concat(arow);
     afrom1=strip(afromName).toUpperCase();
     notifyName[afrom1]=afromName ;
  }
===*/

  if (tmp==1) {                 // an urgent message!
     afrom1='<span style="background-color:#cddefe;font-variant:small-caps" >'+afromName+'</span>';
     aheader='<input type="button" value="close" onClick="close_moveBox(8)" /> '+
                ' '+afrom1+': <span title="Message subject" style="font-style:oblique">'+asubjectUse0+'</span> ';
     write_moveBox(8,aheader,amessage0);
     open_moveBox(8);
  }

  arow='<td width="4%" >'+atime1+ '</td> ' +
        ' <td width="24%">'+afromUse+'</td>'+
        '<td width="35%">'+asubjectUse+'</span></td>'+
        '<td width="35%">'+amessageUse+'</td>' ;
  nread++;

  messageData.messages[ilen].row=arow;          //  what to diisplay in table of messages

}                               // for m
}                               // readMessageResults

if (nread==0) {                         // current messages

  clientMessageOptions.buttonId.style.backgroundColor=clientMessageOptions.buttonBackgroundColor;
  clientMessageOptions.buttonId.setAttribute('title','Click here to send/retrieve messages (and notes)');
  amess='<span style="margin:1px 15px 1px 3px;background-color:#eeddee" '+
      '       title="No new messages (last query at '+tt+')"'+
      '><span style="color:blue">Current</span> messages '+
      ' (<a href="#" title="Click to retrieve new messages" onClick="readClientMessages(1);return false;">'+
      'retrieve messages</a>)</span> ';
} else {

  clientMessageOptions.buttonId.style.backgroundColor="lime";
  clientMessageOptions.buttonId.setAttribute('title','Click here to send/retrieve messages (and notes):  currently there are '+nread+' new messages,');

  amess='<span style="margin:1px 15px 1px 3px;background-color:#eeddee" '+
      '       title="'+nread+' messages retrieved at '+tt+'"> '+
      '  <span style="border:1px solid lime;margin:2px; ">Current</span>  messages '+
      '  (<a href="#" title="Click to retrieve new messages" onClick="readClientMessages(1);return false;">'+
      'retrieve</a>)' +
     '</span>';
 }
 amess=amess+'<span style="font-size:90%;background-color:#99aaee;margin:1px 3px 1px 4px;padding-left:4px;float:right">';

 if (clientMessageOptions.send!=='NO') {                        // compose message
       amess=amess+' '+
         '<input style="font-size:90%" type="button" value="New message" title="Compose and send a message ..." '+
                ' onClick="choseAddress_send()" /> ';
  }
  if (strip(clientMessageOptions.notes)!=='0') {                        // compose message
     amess=amess+' '+
         '<input style="font-size:90%;margin:1px 2px 1px 2px;" type="button" value="Notes" title="Read/write notes for yourself..." '+
                ' onClick="doMessageNotes()" /> ';
  }

 amess=amess+' ' +                                             // close
     '<span style="float:right;margin:1px 3px 1px 4px"> '+     
        '<input style="font-size:90%"  type="button" value="close" onClick="close_moveBox(7)" /> '+
     '</span>';
 
 amess=amess+ '</span> ' ;


// no new messages, just update header display
if (nread==0 && messageData.nthRead>1 && isauto==1) {
   writeHeader_moveBox(7,amess);
   return 0;
}

atable='<div style="margin-bottom:16px"><table cellpadding="2" rules="rows" ><tr>'+
        '<td width="2%">.</td><th>Time</th>'+
        '<th> <span style="background-color:#cddefe;font-variant:small-caps">From</span></th>'+
      '<th style="font-style:oblique">Subject</th><th>Message<th></tr>'; 

ngoo=0;
for (var j=messageData.messages.length-1;j>-1 ; j--) {
  arow=messageData.messages[j].row;
  isread=messageData.messages[j].shown;
  if (isread==0) {
     atable=atable+'<tr><td width="2%"><span style="background-color:lime;font-size:80%">'+j+'</span></td>'+arow+'</tr>\n' ; 
 } else {
     atable=atable+'<tr><td width="2%"><span style="background-color:#efefef;font-size:80%">'+j+'</span></td>'+arow+'</tr>\n';    
 }
 ngoo++;
}

if (messageData.messages.length==0) {
  atable=atable+'<tr><td colspan="3"><span style="font-style:oblique">No messages</span></td></tr>'; 
}

atable=atable+'<tr style="font-size:80%;background-color:#dedede">'+
        '<td width="2%">.</td><th>Time</th>'+
        '<th><span style="background-color:#cddefe;font-variant:small-caps">From</span></th>'+
        '<th  style="font-style:oblique">Subject</th><th>Message<th></tr>'; 

atable=atable+'</table> <hr width="50%" /></div>';

write_moveBox(7,amess,atable);

/*  === no longer used (for urgent messages)
if (notifys.length>0) {                         // notify on reciept
  tmp='';
  d =new Date();
  d.setTime(atime); 
  tt1=d.toLocaleTimeString()
  for (var m0 in notifyName) tmp=tmp+' '+notifyName[m0] ;
  aheader='<b> Messages were recieved from: '+tmp+'</b> (@ '+tt1+')' +
        '<span style="float:right;margin-right:10px"> '+
        '<input type="button" value="close" onClick="close_moveBox(8)" /></span>';
  atable='<table rules="rows">'+
           '<tr><th width="15%">From</th>'+
           '<th width="35%" >Subject</th><th width="50%" >Message</th> </tr>';
  amess=atable+notifys.join(' ')+'</table>';
  write_moveBox(8,aheader,amess); 
  open_moveBox(8);
}
==== */

return 0;
}

// ===============
// notify on reciept of urgent message
function checkIfNotify(aurgent,afromName) {
//  if  (clientMessageOptions.poll>0) {
    if (clientMessageOptions.notify!==0) {
        if (aurgent>0) return 1;
    }
//  }
  return 0;
}

//=============
// send a "note" to your self (saved in message box, and locally
function doMessageNotes() {
var aa,amess;

 amess='<span style="font-size:100%;background-color:#eeddee;margin:1px 3px 1px 4px;padding-left:4px;float:left">'+
   ' Compose a note, and then '+
   ' <input type="button" style="font-size:90%" value="save it" title="Click here to save your note" '+
   '          onClick="saveMessageNote()" />'+
  '</span>' ;

 amess=amess+' ' +                                             // close
   '<span style="float:right;margin:1px 3px 1px 4px;background-color:#99aaee"> '+     
      '<input style="font-size:90%"  type="button" value="view old notes"  title="View old notes" onClick="viewMessageNotes()" /> '+
      '<input style="font-size:90%"  type="button" value="close" title="close this box (and do NOT save a note)" onClick="close_moveBox(8)" /> '+
   '</span>';

aa='<form name="notes1"><table cellspacing="2">'; 
aa=aa+' ' +
   '<tr><th>Subject:</th> '+
       '<td><input type="text" name="noteSubjectArea" size="80" value=" " /></td> '+
   '<tr><th>Message:</th> '+
    '   <td><textarea  name="noteContentArea" rows="6" cols="80"> </textarea> </td>'+  
   '</tr></table></form>';

write_moveBox(8,amess,aa);
open_moveBox(8); 
top_moveBox(8);  

return 1;


} 

//===================== 
// save a note to notes area of messageData
function saveMessageNote(ith) {
var asubject,acontent,i,d,tt;                                           
asubject=document.notes1.noteSubjectArea.value;
acontent=document.notes1.noteContentArea.value;

if (typeof(messageData.notes)=='undefined') messageDatea.notes=[];

i=messageData.notes.length;

d =new Date();

messageData.notes[i]=[];
messageData.notes[i].time=d;
messageData.notes[i].subject=asubject;
messageData.notes[i].content=acontent;

viewMessageNotes(i) ;

return 1
}

//===================== 
// view prior notes
function viewMessageNotes(ith) {
var amess,i,atext,arow,tt,tt2,a1,a2,d;

 amess='<b>Your notes</b> ';
 amess=amess+' ' +                                             // close
   '<span style="float:right;margin:1px 3px 1px 4px;background-color:#99aaee"> '+     
      '<input style="font-size:90%"  type="button" value="close" onClick="close_moveBox(8)" /> '+
      '<input style="font-size:90%"  type="button" value="write a new note"   onClick="doMessageNotes()" /> '+
   '</span>';

 atext='<div style="border:1px solid gray">Notes</div>';
 atext=atext+'' +
     '<table rules="rows" cellpadding="2"><tr> '+
     '<td width="10%">Time</td><th width="30%"> <span style="font-style:oblique">Subject</span></th><th width="60%">Content</th></tr>';
 for (var i=0;i<messageData.notes.length;i++) {
   d=messageData.notes[i].time;
   tt=d.toLocaleString();
   a1=d.getHours(); if (a1<10) a1='0'+a1 ;
   a2=d.getMinutes() ; if (a2<10) a2='0'+a2 ;
   tt2=a1+':'+a2;
   arow='<tr><td width="10%"><span title="'+tt+'" style="font-size:90%">'+tt2+'</span></td> '+
          ' <td width="30%"><span style="font-style:oblique">'+messageData.notes[i].subject+'</span></td>  '+
           '<td width="60%"><div style="white-space:pre">'+messageData.notes[i].content+'</div></td></tr>';
   atext=atext+arow;
 }
 atext=atext+'</table>' ;

write_moveBox(8,amess,atext);
open_moveBox(8); 
top_moveBox(8);  

  return 1 ;
}


//===================== 
// display (in moveBox 8) the idot'h message  (from those containied in the participant's
// message box

function showClientMessage(ido) {


var aline,awords,atime,d,tt1,tt2,atime2,afrom,ato,aheader,asubject,acontent,afromId,afromName,aurgent;
 aline=messageData.messages[ido].full ;

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled

 awords=aline.split(',');
  aurgent=awords[0];
  atime=awords[1];
  d =new Date();
  d.setTime(atime); 
  tt1=d.toLocaleTimeString()
  tt2=d.getHours()+':'+d.getMinutes();
  atime1='<span title="'+tt1+'" style="font-size:80%">'+tt2+'</span>'; 
  afromId=awords[2];
  afromName=awords[3];

  ato=_mbStrip(awords[4]);              // not used on participants side (admin uses it)
  asubject=unescape(awords[5]); 
  amessage=unescape(awords[6]); 

aheader='<input type="button" value="close" onClick="close_moveBox(8)" /> '+
      '<span style="margin:1px 5px 1px 5px;background-color:#deddde">'+tt1+'</span>'+
      '<span style="margin:1px 1px 1px 1px;background-color:#cddefe;font-variant:small-caps">From: '+afromName+'</span>'+
      '<span style="margin:1px 3px 1px 3px;background-color:#eeddee">Subject: '+asubject+'</span>';

open_moveBox(8);
write_moveBox(8,aheader,amessage);
return 1 ;
 
}

//=====================
// send a message
// aid : who to send a mesage to. If '', then 
function composeClientMessage(aid,aname,oldSubject) { 
var aa,aname,amess,ato,areply ;

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled

if (arguments.length<1) aid='';
if (arguments.length<2) aname='';
if (arguments.length<3) oldSubject='';

areply=' message ';
oldSubject=strip(oldSubject);
if (oldSubject!=='')    areply=' reply ';

aname=strip(aname);

open_moveBox(8);

amess='<input type="button" value="close" onClick="close_moveBox(8)" /> ';
if (aname=='') {
   amess=amess+   '<span style="margin:1px 30px 1px 30px;background-color:#ffeedd">Compose a message</span> ';
} else {
   amess=amess+   '<span style="margin:1px 30px 1px 30px;background-color:#ffeedd">Send a '+areply+' to '+aname+'</span> ';
}
if (clientMessageOptions.send!=='NO') {
   amess=amess+' ' +
       '<input type="button" value="Send" title="Send your message" onClick="transmitClientMessage(1,\''+aid+'\',\''+aname+'\')" /> ';
}

aa='<form name="message1"><table cellspacing="2">'; 


aa=aa+' ' +
   '<tr><th>Subject:</th> '+
       '<td><input type="text" name="messageSubjectArea" size="80" value="'+oldSubject+'" /></td> '+
   '<tr><th>Message:</th> '+
    '   <td><textarea  name="messageTextArea" rows="6" cols="80"> </textarea> </td>'+  
   '</tr></table></form>';

write_moveBox(8,amess,aa);
top_moveBox(8);  

return 0;
}

//==== 
// A front-end to composeClientMessage
//    Display a list of addresses. After participant clicks on an address, a compose message form,
//    using this name (and underlying clientId) is displayed -- and pick an address
// Use   clientMessageOptions.send
//            if (aword=='NOSEND') clientMessageOptions.send='NO' ;
//            if (aword=='ADMINSEND') clientMessageOptions.send='ADMIN' ;
//            if (aword=='SENDALL') clientMessageOptions.send='ALL' ;
//            if (aword.substr(0,7)=='SENDIF=') {
//                  clientMessageOptions.send='IF' ;
//                  clientMessageOptions.sendIf=aword.substr(7) ;
//            }



function choseAddress_send(asubject) {
var atable,ith,arow,aid,aname,anickname,a1,addrList,m0,myname,anameuse;
atable='<b>Chose who to send your message to: </b> (click on the name) <br/>';
// clientMessageOptions.isOpen=0;               

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled
if (clientMessageOptions.send=='NO' ) return 0 ;

if (arguments.length<1)  asubject='';

// only permit message to ADMIN?
if (clientMessageOptions.send=='ADMIN' ) {
   a1='<a title="Click here to send a message to the administrator"  href="#" '+
       'onClick="composeClientMessage(\'admin\',\'Admin\',\''+asubject+'\');return false" >'+
      ' Send a message to the administrator</a>';
  priorContent_moveBox(8);
  writeContent_moveBox(8,a1);
  open_moveBox(8);  
  return 0;
}

myname= strip(clientMessageOptions.myname).toUpperCase() ;

addrList=addressListMake() ;

atable=atable+'<div style="border:1px dotted #bdbdbd;margin:5px 3% 5px 3%"><table cellpadding="4"  >'; 

ith=1;
arow='<tr>';
  a1='<a title="Administrator"  href="#" '+
       'onClick="composeClientMessage(\'admin\',\'Admin\',\''+asubject+'\');return false" >'+
      ' Admin</a>';
  arow=arow+'<td style="background-color:#cecdce">'+a1+'</td>';

for (var m0=0;m0<addrList.length;m0++) {

  m=addrList[m0];
  if (typeof(messageData['ID'])!=='undefined')  aid=messageData['ID'][m];

  aname='To_'+m;
  if (typeof(messageData['NAME'])!=='undefined')  aname=messageData['NAME'][m];
  anickname=aname;
  if (typeof(messageData['NICKNAME'])!=='undefined')  anickname=messageData['NICKNAME'][m];

  if (aname==myname) {
     anameuse='<span title="Yourself!" style="color:#bababa">'+aname+'</span>';
     a1='<a title="[yourself]" href="#" ' +
        'onClick="composeClientMessage(\''+aid+'\',\''+aname+'\',\''+asubject+'\');return false" >'+
        '  '+anameuse+' </a>';
  } else {
     anameuse=aname
     a1='<a title="[ '+anickname+' ]" href="#" ' +
        'onClick="composeClientMessage(\''+aid+'\',\''+aname+'\',\''+asubject+'\');return false" >'+
        ' '+anameuse+'</a>';
  }
  arow=arow+'<td>'+a1+'</td>';

  ith++;
  if (ith==8) {         // 8 addresses per row
     arow=arow+'</tr> ';
     atable=atable+arow ;
     arow='<tr>';
     ith=0;
     arow=0;
  }
}
if (ith!==0) {
  atable=atable+arow+'</tr>';
}
atable=atable+'</table></div>';      

atable=atable+' <div style="margin:8px 5% 8px 5%;background-color:#bdbeba"> <em>or, enter a name here... '+
         '<input type="text" value="" size="25" title="Enter NAME to send a message to" '+
         ' onChange="byName_send(this.value)" /> and then <u>click here</u> </div> ';


priorContent_moveBox(8);
writeContent_moveBox(8,atable);
open_moveBox(8);  

return 1;
}

//==== do the work 
// resolve name to address, and composeClientMessage
function byName_send(uname) {
var aname,m,igot=-1;
var aid2,iok,m0,addrList,myname;

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled

uname=strip(uname).toUpperCase();

for (var m=0;m<messageData['ID'].length;m++) {
  aname='To_'+m;
  if (typeof(messageData['NAME'])!=='undefined')  aname=messageData['NAME'][m];
  aname=strip(aname).toUpperCase();
  if (aname==uname) {
        igot=m;
        break;
  }
}
aid=messageData['ID'][igot];

if (igot<0) {
  alert('Error: no participant with name of: '+uname);
  return 0;
}

// allowed to send?
myname= strip(clientMessageOptions.myname).toUpperCase() ;
iok=0;
if (clientMessageOptions.send=='IF') {
   addrList=addressListMake() ;
   for (var m0=0;m0<addrList.length;m0++) {
      m=addrList[m0];
      if (typeof(messageData['ID'])=='undefined')  continue ;
      aid2=messageData['ID'][m];
      if (aid2==aid) {
         iok=1;
         break;
     }
   }
   if (iok==0) {
        alert('Sorry, you are not able to send messages to: '+uname);
        return 1;
   }
}
composeClientMessage(aid,uname,'');

return 0;
} 

//==== do the work 
function transmitClientMessage(ido,aid,aname) {

 var alist,m,i,a1,jid,toGet,stuff,amess,asubject,topost ;
 var myname,myid;

if (messageData.on!==1) return 0 ;                                    // if set to 0, messages are disabled

if (clientMessageOptions.send=='NO' ) return 0 ;

 if (ido==1) { 
   stuff=''; 
   adate=new Date();

   asubject=document.message1.messageSubjectArea.value;
   asubject=escape(asubject);
   amess=document.message1.messageTextArea.value;

   amess=escape(amess);

   myname= clientMessageOptions.myname ;
   myid=clientMessageOptions.myid

   topost=messageSendPost+'session='+sessName+'&clientList='+aid+'&fromId='+myid+'&fromName='+myname+'&time='+adate.getTime();
   topost=topost+'&urgent=0&subject='+asubject+'&message='+amess;
//   alert('toposos='+topost);
   Post_toServer(postMessageURL,topost,'sendMessageResult','post a message of length '+amess.length,'transmitClientMessage',2);

   return 1 ;

  }
  
  writeStatus('Message sent to: '+sendMessageResult);
  writeAlert('Message sent to: '+sendMessageResult,2); 

  close_moveBox(8);

return 1;

}


//======
// extract the "ith" value of the "varnname" messageData variable.
// if i not specified, use messageData['$index']
// the "no 2nd argument" version supports use of messageVar in a SENDIF= equation
// in message_options
function  messageVar(avar,i,defval) {

if (arguments.length<3) defval=0;

if (arguments.length<1) {
  if (typeof(messageData['ID'])=='undefined') {
      if (arguments.length<3)  alert('Error in messageVar:  no ID variable');
      return defval ;
  }
  return messageData['ID'].length;         
}

if (arguments.length<2) {
   if (typeof(messageData['$index'])=='undefined') {
        alert('Error in messageVar:  messageData[\'$index\'] is not defined ');
        return 0;
   }
   i=messageData['$index']  ;
}

avar=strip(avar).toUpperCase();
if (typeof(messageData[avar])=='undefined') {
   if (arguments.length<3)  alert('Error in messageVar:  no such message variable: '+ avar);
   return defval ;
}

if (isNaN(i)) {
   if (arguments.length<3)    alert('Error in messageVar: bad index='+i);
   return defval;
}

if (typeof(messageData[avar][i])=='undefined') {
   if (arguments.length<3)    alert('Error in messageVar: non-existent index='+i);
   return defval;
}

return messageData[avar][i] ;
}

//====
// this may be called by doMath function
function  MESSAGEVAR(avar,i,defval) {
var aa ;
if (arguments.length<1) { aa=messageVar() }
else if (arguments.length<2) { aa=messageVar(avar); }
else if (arguments.length<3) { aa=messageVar(avar,i); }
else if (arguments.length<4) {  aa=messageVar(avar,i,defval); }

return aa ;
}


//==========
// return list of addresess that match a SENDIF
function addressListMake() {

var aa,i,asend;

aa=[];

asend=clientMessageOptions.send ;
asend=strip(asend).toUpperCase();

if (asend!=='IF'  ) {
   for (var i=0;i<messageData['ID'].length;i++)  {
        aa=aa.concat(i);
    }
   return aa;
}
  
daif=clientMessageOptions.sendIf ;

if (daif=='' || daif=='*') {
   for (var i=0;i<messageData['ID'].length;i++)  {
        aa=aa.concat(i);
    }
   return aa;
}

for (var i=0;i<messageData['ID'].length;i++) {
  messageData['$index']=i ;
  useif=E2_doMath(daif); 
  if (useif) aa=aa.concat(i); 
}


return aa;
  
}

