/********************************/
//	Dynamic Dependant Project
//	AJAX Stuff
//	Requires the foundation code
/********************************/

var ddp;
if(!ddp) ddp = {};
ddp.a = {};

/********************************/
//	DDP -> AJAX
/********************************/
(function() {
  //will hold our memorized optimizations
  ddp.a._memorize = {};
  
  //create a request manager on which to fire and attach events as well as storing the currently active requests
  /***************************************************************/
  var requestManager = new function(){
    var objThisRequestManager = this;
    //create an objects to store globally accessiable function names for "addScriptToHeader" 
    this.scriptCallbacks = {};
    //track xmlHTTPRequests in this array
    var XHRInProgress = [];
    
    //provide a method to get the xhrs in progress without allowing access to the actual array
    this.getXHRInProgress = function(obj){
      //return a copy of the actual array
      return XHRInProgress.filter(function(objRequest){ return (!obj || (objRequest.objFireEventsOn && objRequest.objFireEventsOn == obj)); });
    };
    
    //
    this.addXHRInProgress = function(objRequestToAdd){
      XHRInProgress.push(objRequestToAdd);
      if(objRequestToAdd.objFireEventsOn)
        ddp.f.fireEvent(objRequestToAdd.objFireEventsOn, 'xhradd', {"cancelable": false, "objRequest": objRequestToAdd, "XHRInProgress":objThisRequestManager.getXHRInProgress(objRequestToAdd.objFireEventsOn) });
      ddp.f.fireEvent(objThisRequestManager, 'xhradd', {"cancelable": false, "objRequest": objRequestToAdd, "XHRInProgress":objThisRequestManager.getXHRInProgress() });
    };
    
    //
    this.removeXHRInProgress = function(objRequestToRemove){
      XHRInProgress = XHRInProgress.filter(function(obj){ return (obj != objRequestToRemove); });
      if(objRequestToRemove.objFireEventsOn)
        ddp.f.fireEvent(objRequestToRemove.objFireEventsOn, 'xhrremove', {"cancelable": false, "objRequest": objRequestToRemove, "XHRInProgress":objThisRequestManager.getXHRInProgress(objRequestToRemove.objFireEventsOn) });
      ddp.f.fireEvent(objThisRequestManager, 'xhrremove', {"cancelable": false, "objRequest": objRequestToRemove, "XHRInProgress":objThisRequestManager.getXHRInProgress() });
    };

  };
  

  /***************************************************************/
  //addHeaderScript
  //a.k.a. loadJSON (using only the first argument) exported to the ddp.a namespace as loadJSON as well.
  //add a script element to the document header using the supplied url
  //----arguments----
  //-strURL:              a url which generates return data in javascript (or JSON) format
  //-fnSuccess:           optional. callback function to recieve the returned data
  //-fnFailure:           optional. callback function to be fired on load failure or timeout
  //-serverCallbackName:  optional. this is the variable name that the server accepts in the GET request so that
  //                      it can insert a user defined callback into the response (defaults to "callback") 
  //-timeoutInSeconds:    optional. In Opera/Safari, I haven't found a way to detect an error when the content in
  //                      a script tag fails to load (like a 403, 404 or bad url), so this timeout is necessary as a catch-all.
  //                      the default is 60 seconds. this may need to be set higher for very slow connections.
  //-objWindow:           optional. the window object that the new script should be added to. (default is the current)
  //
	function addHeaderScript(strURL, fnSuccess, fnFailure, serverCallbackName, timeoutInSeconds, objWindow){
    //define the timeout variable and the "request complete" flag.
    var requestTimeout = null;
    var requestComplete = false;
    
    //default to 'callback' as the name of the variable the server will look for (in the GET request) and
    //use it's content as the javascript callback function in the response. 
    serverCallbackName = serverCallbackName || 'callback';
    //if a browser window wasn't supplied, use the current one
    objWindow = (typeof objWindow == 'object') ? objWindow : window;
    
    //we only need to add a callback function, accessible through the global scope, if fnSuccess was passed 
    if(typeof fnSuccess == 'function'){
      //if the callback variable is already in the url
      if(ddp.f.getFromURL(serverCallbackName, strURL, true) != null){
        //notify the user of an error
        var strErr = 'ddp.a.addHeaderScript - The callback variable "' + serverCallbackName + '" already exists in the url';
        if(fnFailure)
          fnFailure(5, strErr, strURL);
        else
          throw(strErr + ': ' + strURL);
      }
        
      //create a half time-based, half random-number string for the function name (e.g cbf125053068355207244769124226434)
      var strFunctionName = 'cbf' + new Date().getTime() + String(Math.random()).replace('.', '');
      //if the url already has a ? then add an &; otherwise, add a ?.
      strURL += (strURL.indexOf('?')+1) ? '&' : '?';
      //add a name value pair for passing the callback function name we generated.
      strURL += serverCallbackName +'=ddp.a.requestManager.scriptCallbacks.' + strFunctionName;
      //create a global function with our function name 
      ddp.a.requestManager.scriptCallbacks[strFunctionName] = function(){
        requestComplete = true;
        cleanUp();
        //pass all arguments to our success function
        fnSuccess.apply(objWindow, arguments);
      };
    }
    
    //create a new script element
    var elNewScript = objWindow.document.createElement('script');
    
    //add event handlers to the script element's onerror, onload, and onreadystatechanged events (try to detect success or failure)
    ddp.f.addEvent(elNewScript, 'error', cleanUp);
    ddp.f.addEvent(elNewScript, 'load', function(){
      if(!(fnSuccess && ddp.a.requestManager.scriptCallbacks[strFunctionName]))
        requestComplete = true;
      cleanUp();
    });
    ddp.f.addEvent(elNewScript, 'readystatechange', function(){
      if(elNewScript && elNewScript.readyState && (elNewScript.readyState == 'loaded' || elNewScript.readyState == 'complete'))
        ddp.f.fireEvent(elNewScript, 'load');
    });
    
    //now, set the src attribute to our url with the callback added (if there was a callback function provided)
    elNewScript.setAttribute('src', strURL);
    
    //append the new script element to the <head> element
    objWindow.document.getElementsByTagName('head')[0].appendChild(elNewScript);
    
    //set a timeout for the request.
    requestTimeout = window.setTimeout(cleanUp, (timeoutInSeconds || 60) * 1000)
    
    //cleanup routine to remove the window timeout, callback function reference, 
    function cleanUp(){
      if(ddp.a.requestManager.scriptCallbacks[strFunctionName])
        //once our function has executed, remove the reference to the callback function name we generated.
        delete ddp.a.requestManager.scriptCallbacks[strFunctionName];
      //if the timeout is still active
      if(requestTimeout){
        //clear it
        window.clearTimeout(requestTimeout);
        requestTimeout = null;
      }
      if(elNewScript){
        //remove the script fom the DOM tree)
        elNewScript.parentNode.removeChild(elNewScript);
        elNewScript = null;
      }
      if(!requestComplete){
        requestComplete = true;
        //if there was a failure function specified, fire it as a 400 http error
        if(fnFailure)
          fnFailure.call(objWindow, 400, 'ddp.a.addHeaderScript - There was an error loading the script', strURL);
      }
    }
	}
  

  
  /***************************************************************/
  //asyncronous GET request
  function request(url,destination){
    //default xhr is an asyncronous GET
    return xhr(url, {
      "returnFormat": "text",
      "success": function(strData){
        ddp.f.setHTML(ddp.f.isString(destination) ? document.getElementById(destination) : destination, strData);
      },
      "failure": function(intErrorNumber, strMessage){
        alert(intErrorNumber + ': ' + strMessage + ' - ' + url);
      }
    });
  }

  
   
  /***************************************************************/
  //Uses POST and a Callback function (JSON response)
	function requestPost(url,params,callback){
    return xhr(url, {
      "method": "post",
      "returnFormat": "json",
      "data": params,
      "success": callback,
      "failure": function(intErrorNumber, strMessage){
        alert(intErrorNumber + ': ' + strMessage + ' - ' + url);
      }
    });
  }

  
  //xhr (xml http request)
  /*
  possible arguments to be included as properties of the object
  NAME             TYPE      - DESCRIPTION
  "success"        function  - a function to be executed on success
  "failure"        function  - a function to be executed on failure
  "progress"       function  - a function to be executed periodically to report on progress
  "aborted"        function  - a function to be executed when the request is aborted
  "method"         string    - the HTTP request method (GET, POST, HEAD, etc..)
  "url"            string    - the request url
  "data"           *         - data in the form of a string, an object, or a DOM element
  "returnFormat"   string    - the format of the data returned from the request. if left unset, it will be determined by the content type returned from the server
  "sync"           boolean   - performs syncronous request if set to true
  "makeUniqueURL"  boolean   - do not append "?#########" to the url for uniqueness (IE caching issue)
  e.g. xhr('/path/mypage.htm', {"success": functionReference, "data":"x=abc123", "method":"post"});
  */  
  function xhr(strURL, objParameters){
    var objRequest = new httpRequestClass();
    
    if(typeof objParameters != 'object'){
      objRequest.url = strURL;
      objRequest.sync = true;
      return objRequest.execute();
    }
    else{
      for(var key in objParameters){
        if(typeof objRequest[key] == 'undefined' || key == 'abort'){
          throw new Error('ddp.ajax.xhr - Unrecognized parameter name: "' + key + '"');
        }
        else{
          objRequest[key] = objParameters[key];
        }
      }
      if(ddp.f.isString(strURL)){
        objRequest.url = strURL;
      }
        
      //if a syncronous request
      if(objRequest.sync)
        return objRequest.execute();  //execute the request. return it's result
      //if it's asyncronous
      else
        //and result of the execute was not false, return the object, otherwise return null
        return objRequest.execute() ? objRequest : null;
    }
  }
  
  
  /***************************************************************/
  //HTTPRequest
  //performs a get or post http request
  //----arguments----
  //strMethod:        string    -'get' or 'post' or null (defaults to 'get')
  //strURL:           string    -the url (can include name/value arguments. eg. '/path/page.php?name=value&test=1') 
  //strData:          string    -request data in the form of a name/value string ('variableName=variableValue&pizza=yes')
  //                  object    -if you pass a form element in strData, the form will be parsed and stringified automatically!
  //successCallback:  function  -function to be executed when the data is returned.
  //                             data will be returned as the first argument of the function (optional)
  //failureCallback:  function  -function to be executed if the request fails. (optional) 
  //strReturnFormat:  string    -'json' to return a JSON object, 'xml' to return an xmlDocument or
  //                             'text' to return a plain text string (defaults to 'text')
  //progessCallback:  function  -function which is executed periodically during the transfer. (only supported in Mozilla/FF)
  //                             recieves three arguments (percentageComplete, bytesSoFar, bytesTotal)
  //blnSync:          boolean   -when set to true (defaults to false), the request is syncronous and the data is returned from
  //                             the actual function (false is returned on error)
  //function HTTPRequest(strMethod, strURL, strData, successCallback, failureCallback, strReturnFormat, progressCallback, blnSync, blnMakeUniqueURL, fnAborted){
  function HTTPRequest(){
    //create an object to hold out parameters to ddp.a.xhr
    var objParams = {};
    
    //the list of parameters in the order that they appear in HTTPRequest()  
    var arParameterNames = [
      'method',
      'url',
      'data',
      'success',
      'failure',
      'returnFormat',
      'progress',
      'sync',
      'makeUniqueURL',
      'aborted',
      'complete'
    ];
    
    //go through the arguments and if any are defined, add them to the parameter object
    for(var i=0, l=arParameterNames.length; i<l; i++){
      if(arguments[i])
        objParams[arParameterNames[i]] = arguments[i];
    }
    
    //execute ddp.a.xhr using our parameter object
    return xhr(null, objParams);
  }
  
  
  //httpRequestClass
  //the wrapper class used to make a request cross-browser compatible
  //basically, set all the properties you need to and then execute() the request.
  function httpRequestClass(){
    this.success = null;  //by default, don't use a success function
    this.progress = null;  //by default, don't use a progress function
    this.failure = null;  //by default, don't use a failure function
    this.complete = null; //by default, don't use a complete function
    this.aborted = null; //by default, don't use an abort handler
    
    this.abortable = true;
    this.method = 'GET'; //by default, use the 'get' method
    this.url = null;
    this.data = null;
    this.returnFormat = 'default'; //if left as 'default' the return type will try to be determined from the content-type in the response header
    this.sync = false;
    this.makeUniqueURL = true;
    this.objFireEventsOn = null; //optional object to fire the xhradd/remove events on
    var objThisRequest = this;  //keep a reference to this object
    objThisRequest.http = null;
    var strErrorURL = ''; //the URL that will be reported on errors
        
    //abort
    //abort the HTTPRequest
    this.abort = function(){
      var returnValue = false;
      //there may or may not be support for the "abort" method on the
      //xmlhttprequest object, depending on the implementation we're using
      if(objThisRequest.http && objThisRequest.abortable){
        requestManager.removeXHRInProgress(objThisRequest);
        try{
          objThisRequest.blnAborting = true;
          objThisRequest.http.abort();
          objThisRequest.blnAborting = false;
          returnValue = true;
        }
        catch(e){
          //at the very least, unhook the event handlers so that they don't fire
          objThisRequest.success = null;
          objThisRequest.failure = null;
          
          objThisRequest.blnAborting = false;
          returnValue = false;
        }
        
        if(typeof objThisRequest.aborted == 'function')
          objThisRequest.aborted(strErrorURL);
        if(typeof objThisRequest.complete == 'function')
          objThisRequest.complete(-1);
      }

      return returnValue;
    };
    
    
    //failHandler
    //Fired when there is an error with the request
    //----arguments----
    //intNumber:      integer -the error number
    //strDescription: string  -the error description
    this.failHandler = function(intNumber, strDescription){
      var returnValue = false;
      if(objThisRequest.progressInterval){
        window.clearInterval(objThisRequest.progressInterval); //clear it
        objThisRequest.progressInterval = null; //and set the reference to it to null
      }
      requestManager.removeXHRInProgress(objThisRequest);
      if(objThisRequest.failure){ //if there is a failure specified,
        returnValue = objThisRequest.failure(intNumber, strDescription, strErrorURL);  //use it
        if(typeof objThisRequest.complete == 'function')
          objThisRequest.complete(0);
      }
      else if(typeof objThisRequest.complete == 'function'){
        objThisRequest.complete(0);
      }
      else if(!objThisRequest.sync){ //if there is no handler specified then just throw the error
        throw 'ddp.a.HTTPRequestClass: ' + strDescription + ' (' + intNumber + ') "' + strErrorURL + '"';
      }
      return returnValue;//return false to a syncronous request to indicate that there was a problem
    }
    
    function createRequestObject(){
      var http = false;
      var MHROV = ddp.a._memorize.httpRequestObjectVersion;
      if(MHROV){
        if(MHROV == 'XMLHttpRequest')
          return new XMLHttpRequest();
        else
          return new ActiveXObject(MHROV);
      }
      else{
        //create the request in (Firefox/Mozilla/Opera)
        if(typeof XMLHttpRequest != 'undefined'){
          try{
            http = new XMLHttpRequest();
            //ddp.a._memorize.httpRequestObjectVersion = 'XMLHttpRequest'; //save the object version
          }
          catch(e){}
        }
        //create the request (IE)
      	else if(typeof ActiveXObject != 'undefined'){
          var XMLHTTP_IDS = [
            'Msxml2.XMLHTTP.6.0', //installed with IE7. ships with vista out-of-the-box.
            'MSXML2.XMLHTTP.4.0', //designed to support legacy applications.
            'MSXML2.XMLHTTP.3.0', //installed on all Win2k (sp4) and up Microsoft OSs. Does not support "Xml Schema" (XSD 1.0)
            'MSXML2.XMLHTTP.5.0', //installed with Office 2003 but is off by default in IE7 and causes a "gold bar" to pop up when instanciated in IE7. better than nothing.
            'MSXML2.XMLHTTP',     //really old (no support for abort() method)
            'Microsoft.XMLHTTP'   //really old (no support for abort(), among other things)
          ];
          
          for(var i=0; i<XMLHTTP_IDS.length && !http; i++){
             try{
               http = new ActiveXObject(XMLHTTP_IDS[i]);
               //ddp.a._memorize.httpRequestObjectVersion = XMLHTTP_IDS[i]; //save the object version so we can ask about it later
             }
             catch(e){}
          }
      	}
        
      }
      return http;
    }
    
    
    //execute
    //takes all the properties that have been setup and performs the request
  	this.execute = function(){
      //create a new http request object
      var http = objThisRequest.http = createRequestObject();
      
      if(!http) //if the object couldn't be created
        return objThisRequest.failHandler(1, 'XMLHTTPRequest object could not be created.');  //fire the failure handler and quit
        
      //stateChangeHandler
      //hook the onreadystatechange event of the request object with our own function (stateChangeHandler)
      //wait for the readyState to change to 4 ("loaded") and make sure the http status is 200 ("OK")
  	  var stateChangeHandler = function(){
        if(http && http.readyState == 4){  //readyState will be 4 when the document is loaded.
          //if there is a progress interval set,
          if(objThisRequest.progressInterval){
            window.clearInterval(objThisRequest.progressInterval); //clear it
            objThisRequest.progressInterval = null; //and set the reference to it to null
          }
          if(objThisRequest.blnAborting)
            return false;
      		else if(http.status == 200 || http.status == 304){
            progressHandler(http); //fire the progress handler to make sure we show a progress of 100%
            //if the return type was left as 'default', try to determine the proper return type from the response 'content-type' header
            if(objThisRequest.returnFormat=='default'){
              var strResponseType = http.getResponseHeader('Content-Type'); //get the content-type string from the header
              strResponseType = strResponseType.toLowerCase();
              //if it's a json/javascript content type,
              if(strResponseType.indexOf('application/json') == 0 || strResponseType.indexOf('application/javascript') == 0 || strResponseType.indexOf('text/json') == 0 || strResponseType.indexOf('text/javascript') == 0)
                objThisRequest.returnFormat = 'json'; //set the return type to json
              //if it's an xml content type
              else if(strResponseType.indexOf('application/xml') == 0 || strResponseType.indexOf('text/xml') == 0 || strResponseType.indexOf('application/xhtml+xml') == 0)
                objThisRequest.returnFormat = 'xml'; //set the return type as xml
              else
                objThisRequest.returnFormat = 'text'; //otherwise return plain text
            }
            
      			var result = null; //return null if something goes wrong
            if(objThisRequest.method=='HEAD'){ //if it's a head request,
              //result = http.getAllResponseHeaders();
              if(http.getAllResponseHeaders){ //make sute the requst object supports getting the whole header 
                var strTmp = ddp.f.trimWhitespace(http.getAllResponseHeaders()); //get the whole header and remove extra line feeds or spaces on either end
                if(strTmp && strTmp.length){  //if we got a proper string and the string has a length and the length is more than 0
                  result = {}; //at this point we can at least return an empty object
                  var arLines = strTmp.split('\n'); //split the string into an array of lines 
                  for(var i=0; i<arLines.length; i++){  //go through the lines of the header
                    var intPosition = arLines[i].indexOf(':'); //find the first colon
                    //now take the first part(being the item name) and set a property with the same name to the second part (the item value)
                    result[arLines[i].substring(0,intPosition).toLowerCase()] = ddp.f.trimWhitespace(arLines[i].substring(intPosition+1));
                  }
                }
              }
            }
      			else if(objThisRequest.returnFormat == 'json' && http.responseText) //If the return is in JSON format, eval the result before returning it.
     					result = parseJSON(http.responseText);  //parse the string into a json object
      			else if(objThisRequest.returnFormat == 'xml') //If the return is in XML format,
     					result = (http.responseXML.documentElement) ? http.responseXML.documentElement : http.responseXML; //return the responseXML object.
            else if(objThisRequest.returnFormat == 'text' && http.responseText) //otherwise
     					result = http.responseText; //return the plain text string
      			
            //
            requestManager.removeXHRInProgress(objThisRequest);
            
      			if(typeof objThisRequest.success == 'function')  //if a success function was specified
              objThisRequest.success(result, http.status, strErrorURL);//execute it, passing the data to it.
            if(typeof objThisRequest.complete == 'function')
              objThisRequest.complete(1); //1 for success
            objThisRequest.http = null;
            //http = null;
            objThisRequest.response = result;
            return result;
      		}
      		else //if the http status wasn't 200, then there was a problem 
      			return objThisRequest.failHandler(http.status, 'HTTP Error', strErrorURL); //fire the failure handler 
      	}
    	};
      
      //progressHandler
      //this is our progress handler, which will be fired periodicaly by the XmlHttpRequest object.
      //we, in turn, will fire the user-defined progress handler and pass it three args: percent complete, bytes recieved and total bytes.
      //----arguments----
      //obj:  object  -an object that is passed from the XmlHttpRequest()
      var progressHandler = function(obj){
        //if the progress function has not been defined, just skip all the calculating
        if(typeof objThisRequest.progress != 'function')
          return;
          
        var percentComplete = 0;
        var bytesTotal = 0;
        var bytesSoFar = 0;
        
        if(obj){
          if(typeof obj.totalSize != 'undefined'){ //older style object/properties (pre ff3)
            bytesTotal = obj.totalSize;
            bytesSoFar = obj.position;
          }
          else if(typeof obj.total != 'undefined'){ //using NN based browsers 
            bytesTotal = obj.total;
            bytesSoFar = obj.loaded;
          }
          else if(obj==http){
            try{
              bytesTotal = obj.getResponseHeader('Content-Length');
              //bytesSoFar = http.responseBody.length;
              bytesSoFar = obj.responseText.length;
            }
            catch(e){}
          }
        }
        
        //if we aren't going to end up dividing by zero...
        if(bytesTotal > 0)
          //round the percentage to two decimal places (eg. 25.66)
          percentComplete = Number(bytesSoFar / bytesTotal * 100).toFixed(2);
        //execute the user's progress function with our caclulated info
        objThisRequest.progress(percentComplete, bytesSoFar, bytesTotal);
      };
      

      //if an object is passed
      if(ddp.f.isObject(objThisRequest.data)){
        var objObject = objThisRequest.data;
        //if the object is a DOM element(probably a <form>)
        if(objObject.nodeType && objObject.nodeType==1){
          if(!ddp.forms)//throw an error if the forms module isn't included
            throw new Error('ddp.a.httpRequestClass: The ddp.forms module is required to parse a DOM element');
          //go through the element's children and parse all the name/value pairs from the inputs into a string (using encodeURIComponent())
          objThisRequest.data = ddp.forms.parseForm(objObject);
        }
        else
          objThisRequest.data = serializeObject(objObject); //turn the object into a string of URI encoded name/value pairs
      }
      
      
      //validate the request type
      objThisRequest.method = objThisRequest.method.toUpperCase();
      var arRequestTypes = ['GET','POST','HEAD','OPTIONS','PUT','DELETE','TRACE','CONNECT'];
      for(var i=0, l=arRequestTypes.length; i<=l && objThisRequest.method != arRequestTypes[i]; i++)
        if(i==l)
          throw new Error('ddp.ajax.httpRequestClass - Invalid request type: "' + objThisRequest.method + '"');
          
      //make sure a url was supplied 
  		if(!objThisRequest.url) return objThisRequest.failHandler(2, 'URL argument not supplied');
  		
      try{
        if(typeof http.onprogress != 'undefined')
          http.onprogress = progressHandler;
        else
          objThisRequest.progressInterval = window.setInterval(function(){progressHandler(http);}, 250);
        if(objThisRequest.method=='POST'){
          var strCompiledURL = objThisRequest.url;
          strErrorURL = strCompiledURL;
          if(objThisRequest.makeUniqueURL){
            strCompiledURL += (strCompiledURL.indexOf('?')+1) ? '&' : '?';
            //avoid the cache problem in IE by creating a different URL each time
            strCompiledURL += ddp.f.GUID(); //tack on a global unique identifier string
          }
          http.open('POST', strCompiledURL, !objThisRequest.sync);
          //ddp.f.addEvent(http, 'readystatechange', stateChangeHandler);
          http.onreadystatechange = stateChangeHandler;
    			http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;'); //Send the proper header information along with the request
    			//http.setRequestHeader('Content-length', objThisRequest.data.length);
    			//http.setRequestHeader('Connection', 'close'); //jim says conflicts with IE6 POST??
          requestManager.addXHRInProgress(objThisRequest);
          http.send(objThisRequest.data);  //send the post request
          
        }
        else{
          var strCompiledURL = objThisRequest.url;
          if(objThisRequest.data){    //if there's anything in data,
            strCompiledURL += (strCompiledURL.indexOf('?')+1) ? '&' : '?'; //add a "?" if there isn't one, otherwise add a "&"
            strCompiledURL += objThisRequest.data;  //add it to the url
          }
          strErrorURL = strCompiledURL;
          if(objThisRequest.makeUniqueURL){
            strCompiledURL += (strCompiledURL.indexOf('?')+1) ? '&' : '?';
            //avoid the cache problem in IE by creating a different URL each time
            strCompiledURL += ddp.f.GUID(); //tack on a global unique identifier string
          }
          //2083 characters max length of GET request url in IE6
          //if(objThisRequest.method == 'GET' && strCompiledURL.length > 2083)
          http.open(objThisRequest.method, strCompiledURL, !objThisRequest.sync); //open the request
          //ddp.f.addEvent(http, 'readystatechange', stateChangeHandler);
          http.onreadystatechange = stateChangeHandler;
          requestManager.addXHRInProgress(objThisRequest);
          http.send(null);  //and send it
        }
        return (objThisRequest.sync) ? stateChangeHandler() : true;  //return true to indicate that the request was sent
      }
  		catch(e){ //if something goes wrong
  			return objThisRequest.failHandler(-1, e.fileName + ', line ' + e.lineNumber + ', ' + e.name + ': ' + (e.description || e.message));//fire the failHandler
  		}
  	};
  }
  

  
  /***************************************************************/
  // The parseJSON method takes a string and returns
  // a JavaScript value if the string is a valid JSON text.
  //parts from www.JSON.org/json2.js (http://www.JSON.org/js.html)
  function parseJSON(text){
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;
  
    cx.lastIndex = 0;
  	
    //convert special characters to unicode escaped equivalent
    if(cx.test(text))
    	text = text.replace(cx, function(a){ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); });
  
  	if(/^[\],:{}\s]*$/.
  		test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
  		replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
  		replace(/(?:^|:|,)(?:\s*\[)+/g, ''))){
      
      try{
        //eval the string and return the results
        var objData = eval('(' + text + ')');
        
        //if the result is an object and it contains a property named "ddpSchema", then recursively
        //convert the data to date objects, decoded strings, boolean values etc.. following the schema.
        if(objData && objData.ddpSchema)
          objData = _convertData(objData, objData.ddpSchema);
        else if(ddp.f.isObject(objData)){
          for(var key in objData){
            if(objData.hasOwnProperty(key)){
              if(objData[key] && objData[key].ddpSchema){
                objData[key] = _convertData(objData[key], objData[key].ddpSchema);
              }
            }
          }
        }
        
        return objData;  
      }
      catch(objEvalError){
        var strErr = 'Error: ddp.a.parseJSON: Error evaling JSON data';
        try{ console.log(strErr); }
        catch(e){ alert(strErr); }
        throw(objEvalError);
      }
    }
  	
  	return false;
  };
  
// Return a boolean value telling whether // the first argument is a string. 

  function _convertData(objData, objDataTypes){
    //if the object passed is an array
    //if(typeof objData == 'object' && typeof objData.constructor == 'object' && objData.constructor == Array){
    if(ddp.f.isArray(objData)){
      //iterate through it 
      for(var i=0; i<objData.length; i++)
        //and convert each index (maintaining the same level of depth in objDataTypes
        objData[i] = _convertData(objData[i], objDataTypes)
      return objData;
    }
    //if the object passed is an object but not an array
    //else if(typeof objData == 'object'){
    else if(objData && typeof objData == 'object' && !ddp.f.isString(objData)){
      //go through the properties
      for(property in objData){
        //and if the property has a corresponding property in the ddp schema
        if(objDataTypes[property])
          //then convert the property
          objData[property] = _convertData(objData[property], objDataTypes[property]);
      }
      return objData;
    }
    //if the object passed is not an object, just a primitive data type
    else{
      switch(objDataTypes){
        case 'date': //for 'date' types
          if(objData == null)
            return null;
          else if(ddp.f.isString(objData) && objData.indexOf('ddpDate') == 0){
            var arDateParts = objData.split(' ');
            arDateParts.splice(0, 1); //splice out the ddpDate marker
            for(var i=arDateParts.length; i<7; i++)
              arDateParts[i] = 0;
            return new Date(arDateParts[0], arDateParts[1], arDateParts[2], arDateParts[3], arDateParts[4], arDateParts[5], arDateParts[6])
          }
          else if(isFinite(objData)){  //if it's just a number
            var returnValue = new Date();//create a new date object
            //assume we were passed the number of milliseconds since
            //midnight january 1st 1970 and set the date using "setTime"
            returnValue.setTime(objData);
            return returnValue;
          }
          else{
            //try and return the value from the DDP date parsing method
            var d = ddp.f.parseDateString(objData);
            //return either the successfully parsed date or the string itself
            return d ? d : objData;
          } 
        case 'boolean':
          if(objData === null)
            return false;
          return (objData || (objData.toLowerCase && objData.toLowerCase() == 'true') ) ? true : false;
        case 'number':
          return (ddp.f.isString(objData)) ? Number(objData) : objData;
        case 'string':
          return (objData==null) ? '' : objData;
        default:
          return objData;
      }
    }
  }
  
  
  
  /**************************************************/
  //loadXmlDocument
  function loadXmlDocument(strURL, fnSuccess){
    var docXML = null;
    if(document.implementation && document.implementation.createDocument){
      try{
        docXML = document.implementation.createDocument('', '', null);
        ddp.f.addEvent(docXML, 'load', function(){ fnSuccess(docXML); });
        docXML.load(strURL);
        return true;
      }
      catch(e){}
    }
    else if(window.ActiveXObject){
      try{
        docXML = new ActiveXObject("Microsoft.XMLDOM");
        docXML.async = true;
    	  docXML.onreadystatechange = function(){ if(docXML.readyState == 4) fnSuccess(docXML);	};
        docXML.load(strURL);
        return true;
      }
      catch(e){}
    }
    return false;
  }
  
  
  /**************************************************/
  //submitToIframe
  //
  //
  function submitToIframe(elForm, fnResponse, strReturnType, strMethod){
    var strUniqueId = 'ddpSubmitToIframe' + String(Math.random()).replace('.','');
    //var strIframeSrc = 'about:blank'; //gets reported in IE6 as an unencrypted item under https protocol
    var strIframeSrc = 'javascript: false;';
    var elIFrame = document.createElement('iframe');
    
    strReturnType = (typeof strReturnType == 'string') ? strReturnType.toLowerCase() : 'text';
    strMethod = (typeof strMethod == 'string') ? strMethod.toLowerCase() : null;
    
    elIFrame.setAttribute('src', strIframeSrc);
    elIFrame.setAttribute('id', strUniqueId);
    elIFrame.setAttribute('NAME', strUniqueId);
    elIFrame.style.display = 'none';
    document.getElementsByTagName('body')[0].appendChild(elIFrame);
    
    //bug fix for IE (if left out IE opens a new window instead of using the new iframe)
    try{ window.frames[strUniqueId].name = strUniqueId; }
    catch(e){ }
    
    //add a handler to the iframe's load event
    ddp.f.addEvent(elIFrame, 'load', function(){
      //get the iframe's document (cross-browser wise)
      var elDocument = (elIFrame.contentDocument)?(elIFrame.contentDocument):((elIFrame.contentWindow)?(elIFrame.contentWindow.document):(self.frames[strUniqueId].document));
      
      if(elDocument.location.href != strIframeSrc){ //if the iframe's url is not the one we just gave it
        if(typeof fnResponse == 'function'){
          var returnValue;
          if(strReturnType == 'json')
            returnValue = parseJSON(elDocument.body.innerHTML);
          else if(strReturnType == 'xml')
            returnValue = elDocument;
          else
            returnValue = elDocument.body.innerHTML;
          fnResponse(returnValue); //fire the response handler
        }
        ddp.f.removeEvent(elIFrame, 'load', arguments.callee); //remove this handler
        //remove the iframe since we don't need it anymore
        //but wait 1 second so firefox will stop showing activity
        setTimeout(function(){
          elIFrame.parentNode.removeChild(elIFrame);
          if(elForm.createdBySubmitToIframe)
            elForm.parentNode.removeChild(elForm);
        }, 1000);
      }
    });
    
    if(typeof elForm != 'object' || elForm == null)
      throw('ddp.ajax.submitToIframe - elForm is not an object');
      
    if(!elForm.tagName || elForm.tagName.toLowerCase() != 'form'){
      var obj = (elForm.tagName && elForm.nodeType) ? ddp.forms.parseForm(elForm, true, true) : elForm;
      var elForm = document.createElement('form');
      elForm.createdBySubmitToIframe = true;
      elForm.style.display = 'none';
      elForm.setAttribute('method', obj['formMethod'] || 'get');

      if(obj['formAction']){
        if(elForm.getAttribute('method').toLowerCase() == 'get' && obj['formAction'].indexOf('?')+1){
          obj['formAction'].substr(obj['formAction'].indexOf('?')+1).split('&').forEach(function(a){
            var arTmp = a.split('=');
            if(arTmp[0] != '' && !obj[arTmp[0]])
              obj[arTmp[0]] = arTmp[1];
          });
          
          elForm.setAttribute('action', obj['formAction'].substr(0 ,obj['formAction'].indexOf('?')));
        }
        else
          elForm.setAttribute('action', obj['formAction']);
      }

      for(var key in obj){
        if(key != 'formAction' && key != 'formMethod'){
          var elInput = document.createElement('input');
          elInput.setAttribute('name', key);
          elInput.setAttribute('value', obj[key]);
          elForm.appendChild(elInput);
        }
      }
      document.getElementsByTagName('body')[0].appendChild(elForm);
    }
    
    elForm.setAttribute('target', strUniqueId);
    elForm.submit();
  }
  
  
  
	
  /**************************************************/
  //createAjaxIframe
  //Creates an IFRAME element for transfering data to the server and 
  //sets a timeout to remove it a specified number of seconds after
  //it is created.
  function createAjaxIframe(secondsBeforeRemoval, url){
  	var body = document.getElementsByTagName('BODY')[0];
  	var f = document.createElement('IFRAME');
  	f.src = url;
  	f.style.display='none';
  	var timeNow = new Date().getTime();
  	f.id = 'frame' + timeNow;
  	var x = setTimeout('document.getElementById(\'' + f.id + '\').parentNode.removeChild(document.getElementById(\'' + f.id + '\'));', secondsBeforeRemoval * 1000);
  	body.appendChild(f);
  }
  
  
  /**************************************************/
  //serializeObject
  //converts an object with properties to an encodeURIComponent'd name/value string
  //onlyOwnProperties is optional. if set to true, it stops serializeObject from outputing inherited properties
  function serializeObject(objObject, onlyOwnProperties){
    //array to hold the name/value pair strings
    var arNameValuePairs = [];
    //if the value was passed in as "true" then set it true, otherwise false.
    //onlyOwnProperties = (onlyOwnProperties==true) ? true : false;
    onlyOwnProperties = (onlyOwnProperties==true);
    
    //go through the object's properties, one by one
    for(var propertyName in objObject){
      //try{  console.log('name:' + propertyName + ' hop:' + objObject.hasOwnProperty(propertyName)); } catch(e){}
      //if the "onlyOwnProperties" flag is set to true, don't include inherited properties in the return string 
      //and if the browser doesn't support the "hasOwnProperty" method, just do it anyway 'cause there's no way to tell
      //if(Object.hasOwnProperty.call(value, k)) //from Jim's implementation
      if(!onlyOwnProperties || !objObject.hasOwnProperty || objObject.hasOwnProperty(propertyName)){
        //if the value is null
        if(objObject[propertyName] === null)
          //output a string with 'null' in it
          arNameValuePairs.push(encodeURIComponent(propertyName) + '=null');
        //if it's an array,
        else if(ddp.f.isArray(objObject[propertyName])){
          //go through the array elements
          for(var i=0, l=objObject[propertyName].length; i<l; i++)
            //and format the string properly by repeating the name with each value
            arNameValuePairs.push(encodeURIComponent(propertyName) + '=' + encodeURIComponent(objObject[propertyName][i]));
        }
        //if it's a regular object (and not a string)
        else if(!ddp.f.isString(objObject[propertyName]) && typeof objObject[propertyName] == 'object')
          //substitute a string containing '[Object]'
          arNameValuePairs.push(encodeURIComponent(propertyName) + '=' + encodeURIComponent('[Object]'));
        //if it's a function
        else if(typeof objObject[propertyName] == 'function')
          //output the return value of it's toString() function or maybe just '[Function]' but whatever
          arNameValuePairs.push(encodeURIComponent(propertyName) + '=' + encodeURIComponent(objObject[propertyName].toString()));
        //otherwise
        else
          //just use it's plain ol' value
          arNameValuePairs.push(encodeURIComponent(propertyName) + '=' + encodeURIComponent(objObject[propertyName]));
      }
    }
    //put the pairs together, seperated by an ampersand
    return arNameValuePairs.join('&');
  }
  

/**************************************************/
//Export the public functions to a public namespace
/**************************************************/

//Exports go here
var ddpns = ddp.a;

ddpns.requestManager = requestManager;  
ddpns.loadJSON = addHeaderScript;
ddpns.addHeaderScript = addHeaderScript;
ddpns.request = request;
ddpns.requestPost = requestPost;
ddpns.HTTPRequest = HTTPRequest;
ddpns.xhr=xhr;
ddpns.httpRequestClass = httpRequestClass;
ddpns.parseJSON = parseJSON;
ddpns.convertWithSchema = _convertData;
ddpns.loadXmlDocument = loadXmlDocument;
ddpns.submitToIframe = submitToIframe;
ddpns.createAjaxIframe = createAjaxIframe;
ddpns.serializeObject = serializeObject;

})();
