/***********************************************************************************************************************
| object/Session.js
***********************************************************************************************************************/
var _intRequestIdSequence =1;  //unique ID's for requests
var _arrRequestsWaiting =new Array;  //Requests waiting to be processed


var _log =new Array();
var _intLogCount =0;


function processRequestsWaiting() {
   var strRequestId,undefined;
   var boolTimeoutRunning;

   for(strRequestId in _arrRequestsWaiting) {
      if(_arrRequestsWaiting[strRequestId] && !_arrRequestsWaiting[strRequestId].objSession.boolLoginInProgress) {
         if(!_arrRequestsWaiting[strRequestId].dequeue) { _arrRequestsWaiting[strRequestId].makeRequest(); }
         delete _arrRequestsWaiting[strRequestId];
      }
      else if(!boolTimeoutRunning) {
         boolTimeoutRunning =true;
         setTimeout("processRequestsWaiting()", 300);
      }
   }
}


/***********************************************************************************************************************
| CONSTRUCTOR - checks whether passed controls are of valid types, and errors out if not.
| strUser     : user ID.
| strPassword : user password.
| intRequestId : deprecated.
\**********************************************************************************************************************/
function Session(strCmd, strUser, strPassword, ip, strAgent, intSessionId, intRequestId, strAuto, strArgs) {
   var txt;

   this.className ="Session";
   if(!intSessionId) { intSessionId =0; }  //intSessionId is the filePro record#
   this.intSessionId =intSessionId;
   this.strAuto =0;

   this.boolLoginInProgress =false;
   this.intLoginFailureCount =0;
   this.strValidateUserCmd ="Field_cmd=rreport ajax -fp validateUser -n -m hh -y none -sr ";
   if(!strAuto) { strAuto ="automatic"; }
   if(!strArgs) { strArgs =""; }

   this.view =[];
   this.objPostData =new PostData("/usr/local/apache/htdocs/", "ajax", strCmd, "none", intSessionId, strUser, strPassword, strAgent, ip, strArgs);
   this.setStrCmd(strCmd);
   this.objValidateUserPostData  =new PostData("/usr/local/apache/htdocs/", "ajax", "validateUser", "none", intSessionId, strUser, strPassword, strAgent, ip, ("strUserAgent=" +navigator.userAgent +"&strAppData=" +navigator.appVersion +" " +navigator.vendor +"&intAvailHeight=" +parent.screen.availHeight +"&intAvailWidth=" +parent.screen.availWidth));
   this.objValidateUserPostData.intSessionId =Math.floor((Math.random( ) *1200) +1);
}





/***********************************************************************************************************************
| getView
| Retrieve preprepared static recordset.
| fcnHandleRpcResult: results handler
| strViewName: filename of view to fetch
| return: boolSuccess, was the information transmitted?
| return RPC: RPC, request result
\**********************************************************************************************************************/
//TODO: move view fetch into seperate class like Request & Login, to avoid mutliple requests overwriting each other
Session.prototype.getView =function(fcnHandleRpcResult,strViewName) {
   var boolSuccess =true;
   var postData ="";
   var xmlhView;

   if(this.view[strViewName]) {
      fcnHandleRpcResult(this.view[strViewName]);
   }
   else {
      xmlhView =new XMLHTTP();
      xmlhView.open("GET","http://65.110.86.247/ajax/js/prefetch/" +strViewName);
      xmlhView.onload =this.getViewRPC(fcnHandleRpcResult,strViewName,this.view);
      xmlhView.send();
   }



   return boolSuccess;
};

Session.prototype.getViewRPC =function(fcnHandleRpcResult,strViewName,rsSessionView) {
   //rsView: view recordset object literal (passed as an argument to fcnHandleRpcResult)
   return function() {
      var rsView;     //alert("executeCmd RESPONSE:\n" +this.responseText);
      //WARNING: views are assumed to be well-formed, and are not JSON parsed.
      try { eval("rsView =" +this.responseText); }
      catch(error) { rsView =null; }
      if(rsView) { rsSessionView[strViewName] =rsView; }  //TODO:better error handling

      try { fcnHandleRpcResult(rsView); }
      catch (e) {
         if(e.lineNumber) { throw("Error handling a request to the database:\nSession.js:getViewRPC\n" +e.message +"\n" +e.fileName +" line " +e.lineNumber); }
         else { throw("Error handling a request to the database:\nSession.js:getViewRPC\n" +e.message); } ;
      }
      finally {
         this.onload  =null;
      }
   };
};





/***********************************************************************************************************************
| getViewForUser
| Retrieve preprepared static recordset.
| fcnHandleRpcResult: results handler
| strViewName: filename of view to fetch
| strUser: requesting user
| strPassword: requesting user's password
| return: boolSuccess, was the information transmitted?
| return RPC: RPC, request result
\**********************************************************************************************************************/
Session.prototype.getViewForUser =function(strViewName,fcnHandleRpcResult,strUser,strPassword) {
   //TODO: fetch restriced views.  have filepro method copy view into apache dir as the 'output' for a fpCGI call
};





/***********************************************************************************************************************
| executeCmd
| Run remote procedure & return result to passed-in handler
| fcnHandleRpcResult: results handler.
\**********************************************************************************************************************/
Session.prototype.executeCmd =function(fcnHandleRpcResult,boolDeprecated,that) {
   var undefined, objRequest;

   if(this.valid() && !this.boolLoginInProgress) {  //_log[_log.length]=_intLogCount++ +". valid & login not in progress, executing " +this.objPostData.getStrPostData();
      objRequest =new Request(this,fcnHandleRpcResult,this.objPostData.getClone());
      objRequest.objPostData.intSessionId =this.intSessionId;
      objRequest.makeRequest();
   }
   else if(this.boolLoginInProgress) {  //_log[_log.length]=_intLogCount++ +". login in progress, waiting to execute " +this.objPostData.getStrPostData();
      objRequest =new Request(this,fcnHandleRpcResult,this.objPostData.getClone());
      _arrRequestsWaiting["id" +objRequest.objPostData.intRequestId] =objRequest;
      processRequestsWaiting();
   }
   else {
      if(this.objPostData.strUser && this.objPostData.strPassword) {  //_log[_log.length]=_intLogCount++ +". not valid & login not in progress, logging in & then executing " +this.objPostData.getStrPostData();
         this.boolLoginInProgress =true;
         _arrRequestsWaiting[_arrRequestsWaiting.length] =new Request(this,fcnHandleRpcResult,this.objPostData.getClone());
         objRequest =new Login(this,fcnHandleRpcResult,this.objValidateUserPostData);
         objRequest.makeRequest();
      }
      else {
         fcnHandleRpcResult([{exitCode: 1, exitDesc: "Username/Password required."}]);
      }
   }
};






/***********************************************************************************************************************
| requestCancel
| fcnHandleRpcResult: results handler
| intRequestIds: single or array of queued requests to cancel.
\**********************************************************************************************************************/
Session.prototype.requestCancel =function(fcnHandleRpcResult, intRequestIds) {
   //TODO:test to make sure this sucker works...too tired today...
   var i, intRequestId, arrDequeues =new Array(), objPostData;
   if(this.valid) {
      if(typeof fcnHandleRpcResult != "function") { fcnHandleRpcResult =null; }

      if(intRequestIds && intRequestIds[0]) {
         for(i in intRequestIds) {
            if(_arrRequestsWaiting[intRequestIds[i]]) { _arrRequestsWaiting[intRequestIds[i]].dequeue =true; }  //processWaitingRequests should delete things from the queue
            else { arrDequeues[arrDequeues.length] ="dequeue=" +intRequestIds[i]; }   //operation is already on server, try to abort
         }
         if(arrDequeues.length > 0) {
            objPostData =new PostData("/usr/local/apache/htdocs/", "ajax", "requestCancel", "none", this.intSessionId, this.strUser, this.strPassword, this.strAgent, this.ip, arrDequeues.join("&"));
            new Request(this,fcnHandleRpcResult,objPostData);    //alert("validateUser POST:\n" +this.getStrRequestCancelPostData(intRequestIds));
         }
      }
   }
};





/***********************************************************************************************************************
| valid
| return: boolValid, is the session object OK for transmission?
\**********************************************************************************************************************/
Session.prototype.valid =function() {
   var boolValid =false;

   if(this.intSessionId && this.intSessionId !== 0) {
      boolValid =true;
   }

   return boolValid;
};






/***********************************************************************************************************************
| MUTATORS
\**********************************************************************************************************************/
Session.prototype.setStrArgs      =function(strArgs)      { this.objPostData.strArgs =strArgs; };
Session.prototype.setStrUser      =function(strUser)      { this.objPostData.strUser =strUser; };
Session.prototype.setStrPassword  =function(strPassword)  { this.objPostData.strPassword =strPassword; };
Session.prototype.setStrAgent     =function(strAgent)     { this.objPostData.strAgent =strAgent; };
//deprecated: Session.prototype.setIntRequestId =function(intRequestId) { this.objPostData.intRequestIdServer =intRequestId; };
Session.prototype.setStrAuto      =function(strAuto)      { this.objPostData.strAuto =strAuto; }
Session.prototype.setStrCmd       =function(strCmd) {
                                      strCmd ? this.objPostData.strCmd =strCmd : strCmd =this.objPostData.strCmd;
                                      switch(strCmd) {
                                         case "update":
                                            this.objPostData.strAuto ="automaticU";
                                            break;
                                         case "select":
                                            this.objPostData.strAuto ="automaticS";
                                            break;
                                         case "insert":
                                            this.objPostData.strAuto ="automaticI";
                                            break;
                                         case "delete":
                                            this.objPostData.strAuto ="automaticD";
                                            break;
                                         default:
                                            if(!this.objPostData.strAuto || this.objPostData.strAuto == "") { this.objPostData.strAuto ="automatic"; }
                                            break;
                                      }
                                      //this.strPostData.strCmd ="Field_cmd=rreport ajax -fp " +strCmd +" -n -u -m hh -y " +this.strAuto +" -sr " +this.intRecordNumber;
                                   };
Session.prototype.setIntSessionId =function(intSessionId) { this.intSessionId =intSessionId; };





/***********************************************************************************************************************
| toString
| Debugging tool; shows all members of an object and of any child objects that share this toString function.
| boolShowChildren: show members of child objects? (default=false)
| strLabel: the label to show for the object in the output.
| intDepth: the depth of indentation for the tree (for recurvsive calls only)
\**********************************************************************************************************************/
Session.prototype.toString =function(boolShowChildren, boolShowFunctions, strLabel, intDepth) {
   var i, j, strFieldName;
   var strOutput ="";
   var strTypeIndent ="-";
   var strMemberIndent ="---";

   var strArrayMembers ="";

   var arrUndefined =new Array();
   var arrPrimitives =new Array();
   var arrObjects =new Array();
   var arrArrays =new Array();
   var arrFunctions =new Array();

   if(!intDepth) { intDepth =0; }
   if(!strLabel) { strLabel ="this"; }

   for(i =0; i < intDepth-1; i++) {
      strTypeIndent +="---";
      strMemberIndent +="------";
   }

   if(intDepth > 0) {
      strTypeIndent +="--|";
      strMemberIndent +="|----";
   }

   for(i in this) {
      if(typeof this[i] == "function") { arrFunctions[arrFunctions.length] =i +"( )"; }
      else if(typeof this[i] == "object") {
         if(this[i] instanceof Array) {
            j =0;
            strArrayMembers ="";
            if(this[i].length > 0 && this[i][0]) { j =this[i].length; }
            else { strArrayMembers =", hashed"; for(strFieldName in this[i]) { j++; strArrayMembers +=", " +strFieldName} }
            if(j == 0) { strArrayMembers =""; }
            arrArrays[arrArrays.length] =i +" [" +j +strArrayMembers +"]";
         }
         else {
            if(this[i] && this[i].className) { arrObjects[arrObjects.length] =i +" ......class: " +this[i].className; }
            else { arrObjects[arrObjects.length] =i +" ......object"; }
            if(this[i] && boolShowChildren) { arrObjects[arrObjects.length -1] +="<br>\n" +this[i].toString(boolShowChildren, boolShowFunctions, i, intDepth +1); }
         }
      }
      else if(typeof this[i] == "undefined") { arrUndefined[arrUndefined.length] =i; }
      else { arrPrimitives[arrPrimitives.length] =i +" = " +this[i] + " ......type: " +(typeof this[i]); }
   }

   if(!arrUndefined[0]) { arrUndefined[0] ="___"; }
   else { arrUndefined.sort(); }
   if(!arrPrimitives[0]) { arrPrimitives[0] ="___"; }
   else { arrPrimitives.sort(); }
   if(!arrObjects[0]) { arrObjects[0] ="___"; }
   else { arrObjects.sort(); }
   if(!arrArrays[0]) { arrArrays[0] ="___"; }
   else { arrArrays.sort(); }
   if(!arrFunctions[0]) { arrFunctions[0] ="___"; }
   else { arrFunctions.sort(); }

   strOutput ="|" +strTypeIndent +strLabel +".Undefined<br>\n|" +strMemberIndent +" " +arrUndefined.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Primitives<br>\n|" +strMemberIndent +" " +arrPrimitives.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Objects<br>\n|" +strMemberIndent +" " +arrObjects.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Arrays<br>\n|" +strMemberIndent +" " +arrArrays.join("<br>\n|" +strMemberIndent +" ");
   if(boolShowFunctions) { strOutput +="<br>\n|" +strTypeIndent +strLabel +".Functions<br>\n|" +strMemberIndent +" " +arrFunctions.join("<br>\n|" +strMemberIndent +" "); }

   return strOutput;
}




/***********************************************************************************************************************
| ACCESSORS
\**********************************************************************************************************************/
Session.prototype.getStrRequestCancelPostData =function(strArgs) {
   strPostData ="Field_ddir=" +this.objPostData.strDdr;
   strPostData +="&Field_base=" +this.objPostData.strBase;
   strPostData +="&Field_cmd=rreport ajax -fp requestCancel -n -m hh -y none -sr " +this.intSessionId;
   strPostData +="&strUser=" +this.objPostData.strUser;
   strPostData +="&strPassword=" +this.objPostData.strPassword;
   strPostData +="&strAgent=" +this.objPostData.strAgent;
   strPostData +="&intSessionId=" +this.intSessionId;
   if(strPostData) { strPostData +="&" +this.objPostData.strArgs; }

   return strPostData;  //POST-formatted metadata string
};



























/***********************************************************************************************************************
| Request - queries the server for data; if session ID is null, uses the current session ID.
| objSession: the session making the request.
| fcnHandleRpcResult: function to execute upon receipt of data.
| strPostData: command and parameters to post to server.
\**********************************************************************************************************************/
function Request(objSession,fcnHandleRpcResult,objPostData) {
   this.objSession =objSession;
   this.objPostData =objPostData;
   this.className ="Request";

   this.makeRequest =function() {
      var strPostData ="";
      var xmlhValidate =new XMLHTTP();

      //if Request was made before user authentification had finished, session ID may be blank - go fetch!
      if(!objPostData.intSessionId || objPostData.intSessionId === 0) { objPostData.intSessionId =objSession.intSessionId; }

      xmlhValidate.open("POST","http://www.kinotox.net/cgi-bin/fpcgi");
//_log[_log.length]=_intLogCount++ +". REQUEST constructor, then executing " +objPostData.getStrPostData();
      xmlhValidate.onload =function() {  //thru the magic of closure, this function can access the parameters passed to the constructor
         var RPC, response, j, intRequestId, intSessionId, intLoginFailureCount;     //alert("executeCmd RESPONSE:\n" +this.responseText);
//_log[_log.length]=_intLogCount++ +". LOGIN callback function, then executing " +objPostData.strCmd;
         try {  //Note: THIS refers to the data structure returned to the browser, NOT to the Request object!
            response =this.responseText.split("|");  //returned JSON objects are pipe-seperated

            for(j in response) {                     //assign JSON returned objects to local variables
               response[j] =JSON.parse(response[j]);
               if(response[j] && response[j].className) {
                  switch(response[j].className) {
                     case "exitCode":
                        RPC =[response[j]];          //RPC returned an exit code
                        break;
                     case "intSessionId":
                        intSessionId =response[j].intSessionId;
                        objSession.setIntSessionId(intSessionId);
                        break;
                     case "intRequestId":
                        //intRequestId =response[j].intRequestId;
                        //deprecated: objSession.setIntRequestId(intRequestId);
                        break;
                     case "intLoginFailureCount":
                        intLoginFailureCount =response[j].intLoginFailureCount
                        objSession.intLoginFailureCount =intLoginFailureCount;
                        break;
                     default:
                        break;
                  }
               }
               if(!RPC && response.length > 0) { RPC =response; }  //RPC returned data
            }

            if(!RPC) { RPC =[{exitCode: 1, exitDesc: "Unexpected response from server."}]; }
            RPC.intSessionId =intSessionId;
            RPC.intRequestId =intRequestId;
            RPC.intLoginFailureCount =intLoginFailureCount;

            if(fcnHandleRpcResult && typeof fcnHandleRpcResult == "function") { fcnHandleRpcResult(RPC); }
         }
         catch(e) {
            if(typeof fcnHandleRpcResult == "function") {
               if(e.lineNumber) { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Request(): " +e.message +", " +e.fileName +", line " +e.lineNumber}]); }
               else { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Request(): " +e.message}]); }
            }
            else {
               if(e.lineNumber) { alert("Please alert the webmaster - there was a communications error:\nSession.js:Request()\n" +e.message +"\n" +e.fileName +" line " +e.lineNumber); }
               else { alert("Please alert the webmaster - there was a problem processing your contact information:\nSession.js:Request()\n" +e.message); } ;
            }
         }
         finally {
            this.onload  =null;
         }
         //alert("validateUserRPC\nSESSIONID: " +session.intSessionId +"\n\nRESPONSE:\n"+response);
      };
      strPostData =objPostData.getStrPostData();
      if(strPostData) { xmlhValidate.send(strPostData); }    //alert("executeCmd POST:\n" +strPostData());
      else { throw new Error("Please alert the webmaster - the communications string was malformed: Session.js:Request(): " +strPostData); }
   }
};











/***********************************************************************************************************************
| Login - Creates session on server, then queries the server for data.
| objSession: the session making the request.
| fcnHandleRpcResult: function to execute upon receipt of data.
| strPostData: command and parameters to post to server.
\**********************************************************************************************************************/
function Login(objSession,fcnHandleRpcResult,objPostData) {
   this.className ="Login";
   this.makeRequest =function() {
      var objRequest;
      var xmlhValidate =new XMLHTTP();

      xmlhValidate.open("POST","http://www.kinotox.net/cgi-bin/fpcgi");       //_log[_log.length]=_intLogCount++ +". LOGIN constructor, then executing " +objPostData.getStrPostData();

      xmlhValidate.onload =function() {  //thru the magic of closure, this function can access the parameters passed to the constructor
//_log[_log.length]=_intLogCount++ +". LOGIN callback function, then executing " +objPostData.getStrPostData();
         var RPC, response, j, intRequestId, intSessionId, intLoginFailureCount;     //alert("executeCmd RESPONSE:\n" +this.responseText);
         var intExitCode =-1;
         objSession.boolLoginInProgress =false;

         try {
            response =this.responseText.split("|");  //returned JSON objects are pipe-seperated

            for(j in response) {                     //assign JSON returned objects to local variables
               response[j] =JSON.parse(response[j]);
               if(response[j]) {
                  switch(response[j].className) {
                     case "exitCode":
                        RPC =[response[j]];
                        break;
                     case "intSessionId":
                        intSessionId =response[j].intSessionId;
                        objSession.setIntSessionId(intSessionId);
                        break;
                     case "intRequestId":
                        //intRequestId =response[j].intRequestId;
                        //deprecated: objSession.setIntRequestId(intRequestId);
                        break;
                     case "intLoginFailureCount":
                        intLoginFailureCount =response[j].intLoginFailureCount
                        objSession.intLoginFailureCount =intLoginFailureCount;
                        break;
                     default:
                        break;
                  }
               }
            }

            if(!RPC || !RPC[0]) { RPC =[{exitCode: 1, exitDesc: "Unexpected response from server."}]; }
            else {
               if(fcnHandleRpcResult && typeof fcnHandleRpcResult == "function") {
                  if(RPC[0].exitCode === 0) { processRequestsWaiting(); }
                  else if(RPC[0].exitCode && RPC[0].exitCode >= 1) { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Login.makeRequest(): RPC exit code=" +RPC[0].exitCode +" exit message=" +RPC[0].exitDesc}]); }
                  else { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Login.makeRequest()"}]); }
               }
               else { throw new Error("Function handler was not a function: " +fcnHandleRpcResult); }
            }
         }
         catch(e) {
            if(typeof fcnHandleRpcResult == "function") {
               if(e.lineNumber) { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Login.makeRequest: Error=" +e.message +", " +e.fileName +", line " +e.lineNumber}]); }
               else { fcnHandleRpcResult([{exitCode: 1, exitDesc: "Unexpected response from server caused application error: Session.js:Login.makeRequest: Error=" +e.message}]); }
            }
            else {
               if(e.lineNumber) { alert("Please alert the webmaster - there was a communications error:\nSession.js:Login.makeRequest:\n Error=" +e.message +"\n" +e.fileName +" line " +e.lineNumber); }
               else { alert("Please alert the webmaster - there was a problem processing your contact information:\nSession.js:Login.makeRequest:\n Error=" +e.message); } ;
            }
         }
         finally {
            this.onload  =null;
            objSession.boolLoginInProgress =false;
         }
         //alert("validateUserRPC\nSESSIONID: " session.intSessionId +"\n\nRESPONSE:\n"+response);
      };

      xmlhValidate.send(objPostData.getStrPostData());     //alert("executeCmd POST:\n" +this.objPostData.getStrPostData());
   }
};











/***********************************************************************************************************************
| PostData - wrapper for elements comprising string to POST to server
\**********************************************************************************************************************/
function PostData(strDdr, strBase, strCmd, strAuto, intSessionId, strUser, strPassword, strAgent, ip, strArgs) {
   this.strDdr =strDdr;
   this.strBase =strBase;
   this.strCmd =strCmd;
   this.strAuto =strAuto;
   this.strUser =strUser;
   this.strPassword =strPassword;
   this.strAgent =strAgent;
   this.intRequestId =_intRequestIdSequence++;
   this.intRequestIdServer =-1;
   this.intSessionId =intSessionId;
   this.ip =ip;
   this.strArgs =strArgs;
};





/***********************************************************************************************************************
| getStrPostData - return formatted string for POSTing to server
\**********************************************************************************************************************/
PostData.prototype.getStrPostData =function(){
   var strPostData;

   strPostData ="Field_ddir=" +this.strDdr;
   strPostData +="&Field_base=" +this.strBase;
   strPostData +="&Field_cmd=rreport ajax -fp " +this.strCmd +" -n -m hh -y " +this.strAuto +" -sr " +this.intSessionId;
   strPostData +="&strUser=" +this.strUser;
   strPostData +="&strPassword=" +this.strPassword;
   strPostData +="&strAgent=" +this.strAgent;
   strPostData +="&ip=" +this.ip;
   strPostData +="&intRequestId=" +this.intRequestId;
   strPostData +="&intSessionId=" +this.intSessionId;
   if(this.strArgs) { strPostData +="&" +this.strArgs; };

   return strPostData;
}





/***********************************************************************************************************************
| getClone - get a clone of the current object, but with an unique request ID.
| returns: clone of current object.
\**********************************************************************************************************************/
PostData.prototype.getClone =function(intSessionId){
   var field, undefined;
   var clone =new PostData();

   for(field in this) {
      if(field != "intRequestId" && typeof this[field] != "function" && typeof this[field] != "object") { clone[field] =this[field]; }
   }

   return clone;
}





/***********************************************************************************************************************
| toString
| Debugging tool; shows all members of an object and of any child objects that share this toString function.
| boolShowChildren: show members of child objects? (default=false)
| strLabel: the label to show for the object in the output.
| intDepth: the depth of indentation for the tree (for recurvsive calls only)
\**********************************************************************************************************************/
PostData.prototype.toString =function(boolShowChildren, boolShowFunctions, strLabel, intDepth) {
   var i, j, strFieldName;
   var strOutput ="";
   var strTypeIndent ="-";
   var strMemberIndent ="---";

   var strArrayMembers ="";

   var arrUndefined =new Array();
   var arrPrimitives =new Array();
   var arrObjects =new Array();
   var arrArrays =new Array();
   var arrFunctions =new Array();

   if(!intDepth) { intDepth =0; }
   if(!strLabel) { strLabel ="this"; }

   for(i =0; i < intDepth-1; i++) {
      strTypeIndent +="---";
      strMemberIndent +="------";
   }

   if(intDepth > 0) {
      strTypeIndent +="--|";
      strMemberIndent +="|----";
   }

   for(i in this) {
      if(typeof this[i] == "function") { arrFunctions[arrFunctions.length] =i +"( )"; }
      else if(typeof this[i] == "object") {
         if(this[i] instanceof Array) {
            j =0;
            strArrayMembers ="";
            if(this[i].length > 0 && this[i][0]) { j =this[i].length; }
            else { strArrayMembers =", hashed"; for(strFieldName in this[i]) { j++; strArrayMembers +=", " +strFieldName} }
            if(j == 0) { strArrayMembers =""; }
            arrArrays[arrArrays.length] =i +" [" +j +strArrayMembers +"]";
         }
         else {
            if(this[i] && this[i].className) { arrObjects[arrObjects.length] =i +" ......class: " +this[i].className; }
            else { arrObjects[arrObjects.length] =i +" ......object"; }
            if(this[i] && boolShowChildren) { arrObjects[arrObjects.length -1] +="<br>\n" +this[i].toString(boolShowChildren, boolShowFunctions, i, intDepth +1); }
         }
      }
      else if(typeof this[i] == "undefined") { arrUndefined[arrUndefined.length] =i; }
      else { arrPrimitives[arrPrimitives.length] =i +" = " +this[i] + " ......type: " +(typeof this[i]); }
   }

   if(!arrUndefined[0]) { arrUndefined[0] ="___"; }
   else { arrUndefined.sort(); }
   if(!arrPrimitives[0]) { arrPrimitives[0] ="___"; }
   else { arrPrimitives.sort(); }
   if(!arrObjects[0]) { arrObjects[0] ="___"; }
   else { arrObjects.sort(); }
   if(!arrArrays[0]) { arrArrays[0] ="___"; }
   else { arrArrays.sort(); }
   if(!arrFunctions[0]) { arrFunctions[0] ="___"; }
   else { arrFunctions.sort(); }

   strOutput ="|" +strTypeIndent +strLabel +".Undefined<br>\n|" +strMemberIndent +" " +arrUndefined.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Primitives<br>\n|" +strMemberIndent +" " +arrPrimitives.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Objects<br>\n|" +strMemberIndent +" " +arrObjects.join("<br>\n|" +strMemberIndent +" ") +
              "<br>\n|" +strTypeIndent +strLabel +".Arrays<br>\n|" +strMemberIndent +" " +arrArrays.join("<br>\n|" +strMemberIndent +" ");
   if(boolShowFunctions) { strOutput +="<br>\n|" +strTypeIndent +strLabel +".Functions<br>\n|" +strMemberIndent +" " +arrFunctions.join("<br>\n|" +strMemberIndent +" "); }

   return strOutput;
}















try { checkIfLastToLoad(); } catch(error) {;}
