function API_SignOut()
  {
  var input = createInputXML("main", "API_SignOut");
  var output = getOutput(input);
  return output;
  }
  
/******************************************************************************/
/**                                                                          **/
/** API_AddRecord() - accepts a database id, an associative array of         **/
/**                   the field-value pairs for a new record, and optional   **/
/**                   variables.                                             **/
/**                                                                          **/
/******************************************************************************/
function API_AddRecord(dbid, fvlist, fform)
  {
  var input = createInputXML(dbid, "API_AddRecord");
  var children = parseFVList(input, fvlist);

  // if present, fform allows HTML forms to set the database's 
  // checkbox fields
  if (fform)
    {
    children.push(makeKid(input, "fform", fform));
    }
    
  // append the children to the qdbapi element
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);
  // send the output to the PHP form which returns the API response,
  // then return the output to the calling function.
  var output = getOutput(input);
  return output;
  }
  
  
function API_Authenticate(username, password, hours)
  {
  var input = createInputXML("main", "API_Authenticate");
  var children = new Array();
  
  children.push(makeKid(input, "username", username));
  children.push(makeKid(input, "password", password));
  if (hours)
    {
    children.push(makeKid(input, "hours", hours));
    }
    
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);

  var output = getOutput(input);
  return output;
  }
  
  
function API_CloneDatabase(dbid, newdbname, newdbdesc, keepData)
  {
  var input = createInputXML(dbid, "API_CloneDatabase");
  var children = new Array();
  
  children.push(makeKid(input, "newdbname", newdbname));
  if (newdbdesc)
    {
    children.push(makeKid(input, "newdbdesc", newdbdesc));
    }
  if (keepData == 1)
    {
    children.push(makeKid(input, "keepData", 1));
    }
  
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);

  var output = getOutput(input);
  return output;
  }
  
  
function API_CreateDatabase(dbname, dbdesc)
  {
  var input = createInputXML("main", "API_CreateDatabase");
  var children = new Array();
  
  children.push(makeKid(input, "dbname", dbname));
  if (dbdesc)
    {
    children.push(makeKid(input, "dbdesc", dbdesc));
    }
  
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);

  var output = getOutput(input);
  return output;
  }
  
  
function API_DeleteDatabase(dbid)
  {
  var input = createInputXML(dbid, "API_DeleteDatabase");
  var output = getOutput(input);
  return output;  
  }
  
  
function API_DeleteRecord(dbid, rid)
  {
  var input = createInputXML(dbid, "API_DeleteRecord");
  var children = new Array(makeKid(input, "rid", rid));

  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);

  var output = getOutput(input);
  return output;
  }
    
function API_DoQuery(dbid, query, clist, slist, fmt, options)
  {
  var input = createInputXML(dbid, "API_DoQuery");  
  var children = new Array();

  if (query)
    {
    // if the value of query is not a number, it must be either 
    // a query name or an expression in query language        
    if (isNaN(parseInt(query, 10)))
      {

      // if the value of query begins and ends with curly braces,
      // it must be written in query language.                 
      if (query.match(/^\{.*\}$/))
        {
        children.push(makeKid(input, "query", query));
        }
      // If the value of query is not a number and not query
      // language, it must be a query name
      else
        {
        // children.push(makeKid(input, "query", query));
        }
      }
    // If the value of query is a number, it must be a query id
    else
      {
      children.push(makeKid(input, "qid", query));
      } 
    }
  // clist is the list of columns to be returned in the query response,
  // represented as the columns' field ids separated by periods
  if (clist)
    {
    children.push(makeKid(input, "clist", clist));
    }
  // slist is the list of columns by which the query response will be
  // sorted, represented as the columns' field ids separated by periods.
  if (slist)
    {
    children.push(makeKid(input, "slist", slist));
    }
  // fmt can be one of two values: flat or structured.  If no value is 
  // specified, the default format is flat.
  if (fmt == ("flat" || "structured"))
    {
    children.push(makeKid(input, "fmt", fmt));
    }
  // since virtually everything I do with the API involves structured
  // XML, I make this the default format unless flat is explicitly 
  // requested by the calling function
  else
    {
    children.push(makeKid(input, "fmt", "structured"));
    }
  if (options)
    {
    children.push(makeKid(input, "options", options));
    }    
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);

  appendChildren(qdbapi, children);
  var output = getOutput(input);  
  return output;
  }   


function API_EditRecord(dbid, rid, fvlist, fform, update_id)
  {
  var input = createInputXML(dbid, "API_EditRecord");
  var children = new Array();
  var fvArray = parseFVList(input, fvlist);
  
  children.push(makeKid(input, "rid", rid));
  
  children = children.concat(fvArray);

  // if present, fform allows HTML forms to set the database's 
  // checkbox fields
  if (fform)
    {
    children.push(makeKid(input, "fform", fform));
    }

  if (update_id)
    {
    children.push(makeKid(input, "update_id", update_id));
    }

  // append the children to the qdbapi element
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);
  
  // send the output to the PHP form which returns the API response,
  // then return the output to the calling function.
  var output = getOutput(input);
  return output;
  }  
  
  
function API_GetSchema(dbid)
	{
  var input = createInputXML(dbid, "API_GetSchema");  
  var output = getOutput(input);  
  return output;
	}
  
  
function API_GrantedDBs(withembeddedtables, excludeparents, adminOnly)
  {
  var input = createInputXML("main", "API_GrantedDBs");  
  var children = new Array();
  
  if (withembeddedtables == 0)
    {
    children.push(makeKid(input, "withembeddedtables", 0));
    }
  if (excludeparents == 1)
    {
    children.push(makeKid(input, "excludeparents", 1));
    }
  if (adminOnly)
    {
    children.push(makeKid(input, "adminOnly", 1));
    }
  
  var qdbapi = input.getElementsByTagName("qdbapi").item(0);
  appendChildren(qdbapi, children);
  
  var output = getOutput(input);  
  return output;
  }
  
function parseFVList(xml, fvlist)
  {
  var attr = "";
  var nodeArray = new Array();
  // for each element in the fvlist array (e.g., fvlist['foo'] = bar or 
  // fvlist['47'] = snafu), create child element for the XML document
  // (e.g., <field name="foo">bar</field> or <field fid="47">snafu</field>)
  for (var i in fvlist)
    {
    if (isNaN(parseInt(i, 10)))
      {
      attr = "name";    
      }
    else
      {
      attr = "fid";
      }
    nodeArray.push(makeKid(xml, "field", fvlist[i], attr, i));
    }
  return nodeArray;
  }
  
  
function createInputXML(dbid, action)
  {
  // create an XML Document object with the root "rpm"
  var xmlDoc = createXMLDoc("rpm");
  //create elements for the XML Document
  var dbidElement = makeKid(xmlDoc, "dbid", dbid);
  var actionElement = makeKid(xmlDoc, "action", action);
  var qdbapiElement = makeKid(xmlDoc, "qdbapi");

  // append each element to the root
  xmlDoc.documentElement.appendChild(dbidElement);
  xmlDoc.documentElement.appendChild(actionElement);
  xmlDoc.documentElement.appendChild(qdbapiElement);
  return xmlDoc;
  }

/******************************************************************************/
/**                                                                          **/
/** getOutput() - Accepts an XML Document to send via a POST request to the  **/
/**              a PHP form for processing.  The optional boolean textOnly,  **/
/**              when true, forces the HTTP response as plain text.          **/
/**              If textOnly is false or undefined, the response may be      **/
/**              returned as either XML or plain text, depending on the      **/
/**              response's Content-Type header                              **/
/**                                                                          **/
/******************************************************************************/
function getOutput(content, textOnly)
	{
	// Variable declarations
  var req = createXMLHttpRequest();
	var url = "api_handler.php";
	var method = "POST";
	// POST the XML input to the PHP form and return the XML output
  req.open(method, url, false);
	req.setRequestHeader("Content-Type", "text/xml");
	req.send(content);
   
  // if textOnly is true, return the HTTP response as plain text no matter what
  if (textOnly)
    {
    return req.responseText;
    }
  // otherwise, evaluate the Content-Type header of the HTTP response
  else 
    {
    var contentType = req.getResponseHeader("Content-Type");  

    // if the content type is XML, return the response as an 
    // XML document object
    if (contentType == "text/xml" || contentType == "application/xml")
      {
      return req.responseXML;
      }
    // for all other content types, return the response as plain text
    else
      {
      return req.responseText;
      }
    }
	}

  

/******************************************************************************/
/**                                                                          **/
/** makeKid() - accepts an XML Document object and four properties of a new  **/
/**             element which will be created in that document.  This        **/
/**             will take the form <tag attribute="attrValue">value</tag>    **/
/**             Note that creating an element in the XML Document object     **/
/**             does not automatically place that element in the document    **/
/**             itself; the appendChild() method must be used for that.      **/
/**                                                                          **/
/******************************************************************************/  
function makeKid(xDoc, tag, tagValue, attribute, attrValue)
  {
  var elem = xDoc.createElement(tag);
  // the value for the tag is optional
  if (tagValue)
    {
    elem.appendChild(xDoc.createTextNode(tagValue));
    }
  // the attribute its value are optional
  if (attribute)
    {
    elem.setAttribute(attribute, attrValue);
    }				
  return elem;
  }
  
/******************************************************************************/
/**                                                                          **/
/** appendChildren() - accepts a parent node and an array of nodes which     **/
/**                    will be appended to that parent.                      **/
/**                                                                          **/
/******************************************************************************/  
function appendChildren(father, kids)
  {
  for (var i = 0; i < kids.length; i++)
    {
    father.appendChild(kids[i]);
    }
  }
  

  
/******************************************************************************/
/**                                                                          **/
/** CreateXMLDoc() - Accepts a string contaning the root name and creates an **/
/**                  XML Document Object                                     **/
/**                                                                          **/
/******************************************************************************/
function createXMLDoc(root)
  {
  // try to create a Mozilla-compatible XML Document object
  try 
    {
    var xmlDoc = document.implementation.createDocument("", root, null);
    }
  // if that fails, create a MSIE-compatible XML Document object
  catch(e)
    {
    var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.appendChild(xmlDoc.createElement(root));
    }
  return xmlDoc;
  }
  
/******************************************************************************/
/**                                                                          **/
/** CreateXMLHttpRequest() - Creates and returns an XMLHttpRequest object    **/
/**                                                                          **/
/******************************************************************************/
function createXMLHttpRequest()
  {
  // Try to create Mozilla-compatible XMLHttpRequest object
	try
		{
		var requestObj = new XMLHttpRequest();
		}
	// If that fails, create MSIE-compatible XMLHttpRequest object
	catch(e)
		{
		var requestObj = new ActiveXObject("Microsoft.XMLHTTP");
		}
  return requestObj;
  }
  
/*
Functions that have not been added to this script:

API_AddField
API_ChangePermission
API_ChangeRecordOwner
API_FieldAddChoices
API_FieldRemoveChoices
API_FindDBByName
API_GenAddRecordForm
API_GenResultsTable
API_GetDBInfo
API_GetNumRecords
API_GetRecordAsHTML
API_GetRecordInfo
API_ImportFromCSV
API_PurgeRecords
API_SetFieldProperties
API_SignOut
*/