// lib_ajax.js - Javascript library for AJAX methods and structures

// Requires: lib_core.js
if (!LIB_CORE_LOADED) {
  // Here is where you _could_ do something to alert the user to a problem, but we don't want to do that.
}

/**********************************************************************************************************************
 EXAMPLE USAGE:

  // Define this first, outside of any custom functions.
  var myAjaxObject = new ajaxObj("myAjaxObject"); // Create new AJAX object (and give it a unique name)

  // Optional override attributes (affects behavior at runtime)
  myAjaxObject.debug = true;            // Turn on debugging mode (verbose output while running).
  myAjaxObject.silent = true;           // Keep the output quiet (no messages, no wait icons).
  myAjaxObject.waitMsg = "Loading";     // What to display in the wait dialog box (default shown).
  myAjaxObject.waitFor = 20;            // How long should we make the user wait? (default shown).
  myAjaxObject.methodType = "post";     // Should we POST or GET the data via AJAX? (default shown).

  // Optional functional attributes (affects followup logic).
  myAjaxObject.waitFrom = document.fieldname; // Show the wait message in relation to what form field?
  myAjaxObject.targetFunc = "myFunction";     // Function to be run AFTER data is returned (just the name).
  myAjaxObject.targetParams = "'Text Param', globalJSvar";    // Parameters to be passed to that function.

  // Required values/methods for running the AJAX code.
  var myParams = "urlparam=12345&ts=" + new Date().getTime(); // Parameters to be passed to the server page.
  myAjaxObject.sendRequest("ajax_handler.p", myParams);       // Post the data to the page using the given parameters.
**********************************************************************************************************************/

function ajaxObj(myName) {
  // There are basically 2 objects in use here: "this" and "thisObject".
  // NOTE: "this" is the true reference to the ajaxObj instance itself.
  // "thisObject" is a global copy that can be shared beteween the functions.
  // The original object accepts the overrides and special attribute settings,
  // and gets copied to "thisObject" just before each new request is prepared.
  var thisObject;

  // Use only one function to display an error message to the user.
  this.handleError = function(message) {
    showErrMsg(message); // Use our custom error display methods.
  }

  // Use only one function to display a debug message to the user.
  this.handleDebug = function(message) {
    var thisDebugBox = document.getElementById("debugBox");
    if (thisDebugBox) {
      thisDebugBox.innerHTML = thisDebugBox.innerHTML + "<br/>" + message;
      thisDebugBox.style.display = "block";
    }
  }

  // Reset the vars that are used to hold the returned results.
  this.reset = function() {
    if (this.debug) { this.handleDebug("reset " + this.myName); }
    this.requestTimer = null; // Handle to the timer waiting on a server response (time until abort).
    this.waitTimer = null;    // Handle to the timer that drives the alert box.
    this.isWaiting = false;   // Flag that designates if we're still waiting for a response or not.
    this.textresult = "";     // Plain text version of the server's response to the request (always returned).
    this.xmlresults = null;   // XML DOM structure of the server's response to the request (only if text/xml returned).
    thisObject = this;        // Copy the object back to the "global" variable.
  }

  this.abortRequest = function(silent) {
    if (thisObject.debug) { thisObject.handleDebug("abortRequest " + thisObject.myName); }
    // Stop all user notification/waiting, kill the request, and alert the user to the error.
    thisObject.aborted = true;
    if (thisObject.isWaiting && (!thisObject.silent && !silent)) { // Only display this if we were previously waiting on a request.
      thisObject.handleError("AJAX server request was aborted due to timeout (object in use: " + thisObject.myName + ").");
    }
    thisObject.stopAjaxWait();
    thisObject.isWaiting = false;
    if (thisObject.handle) { thisObject.handle.abort(); }
    if (thisObject.debug) { thisObject.handleDebug("Prior AJAX requests have been aborted for " + thisObject.myName + "."); }
  }

  this.stopAjaxWait = function() {
    if (thisObject.debug) { thisObject.handleDebug("stopAjaxWait " + thisObject.myName); }
    var thisWaitBox = document.getElementById("waitBox");
    if (thisWaitBox) {
      thisWaitBox.innerHTML = "";
      thisWaitBox.style.display = "none";
    }
    clearTimeout(thisObject.waitTimer);
    return true;
  }

  this.startAjaxWait = function(timeLimit,myMessage) {
    if (thisObject.debug) { thisObject.handleDebug("startAjaxWait " + thisObject.myName + " - Time: " + thisObject.waitFor + "sec."); }
    var newTop;
    var newLeft;
    if (thisObject.waitFrom && thisObject.waitFrom != null) {
      // Use field as point of reference
      newTop = (findPosY(this.waitFrom) - 50) + "px";
      newLeft = (findPosX(this.waitFrom) - 20) + "px";
    } else {
      // Use mouse position
      newTop = yMousePos + 100 + "px";
      newLeft = "40%";
    }

    var thisWaitBox = document.getElementById("waitBox");
    if (thisWaitBox) {
      thisWaitBox.innerHTML = '<span style="text-align:center;width:100%;font-weight:bold;">' + myMessage + '...&nbsp;<img src="/images/site/spinner.gif" border="0"></span>';
      thisWaitBox.style.display = "block";
      thisWaitBox.style.top = newTop;
      thisWaitBox.style.left = newLeft;
      timeLimit = timeLimit * 1000; // Change from seconds to miliseconds
      thisObject.waitTimer = setTimeout(thisObject.stopAjaxWait, timeLimit);
    }
    return true;
  }

  this.createRequest = function() {
    if (this.debug) { this.handleDebug("createRequest " + this.myName); }
    if (thisObject.handle) { thisObject.abortRequest(0); } // Kill any prior running requests
    this.reset(); // Reset the return result vars.
    try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
    try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
    try { return new XMLHttpRequest(); } catch(e) {}
    this.handleError("Sorry, AJAX (XMLHttpRequest) is not supported by your browser.");
    if (thisObject.debug) { thisObject.handleDebug("Unable to create XMLHttpRequest object handle."); }
    return false;
  }

  this.sendRequest = function(handlerURL,handlerParams) {
    if (thisObject.debug) { this.handleDebug("<hr><br/>sendRequest " + thisObject.myName); }
    this.handle = this.createRequest(); // Create a new request object.
    if (!this.handle) {
      // If creating the handle failed, then just output this now.
      this.handleError("This browser cannot create an XMLHttpRequest instance.");
      return false;
    }
    thisObject.aborted = false; // Reset the abort status on the object.
    if (!thisObject.silent) {  // Only do this if we're not in a "silent" mode.
      this.handleError(""); // This assumes there were past messages, so we should clear them out.
    }
    if (thisObject.debug) { thisObject.handleDebug("Sending '" + thisObject.methodType + "' request to '" + handlerURL + "?" + handlerParams + "'"); }
    if (thisObject.handle.overrideMimeType) {
      // If the browser supports it, make sure we treat everything as text/xml
      thisObject.handle.overrideMimeType("text/xml");
    }
    // Set this before calling obj.open()
    thisObject.handle.onreadystatechange = this.handleResponse;
    thisObject.handle.open(this.methodType, handlerURL + "?" + handlerParams, true);
    // Note: Mozilla breaks if obj.send() is called without a parameter or null
    // Firefox/Opera require the parameters be sent here, if using POST
    thisObject.handle.send(handlerParams);
    // Timeout if no response in X seconds.
    thisObject.requestTimer = setTimeout(this.abortRequest, (thisObject.waitFor * 1000));
    return true;
  }

  this.handleResponse = function() {
    if (thisObject.debug) { thisObject.handleDebug("handleResponse " + thisObject.myName + " - Aborted: " + thisObject.aborted); }
    if (thisObject.aborted) { return false; } // Leave immediately if the request was aborted.
    // This handles any state changes in our XMLHttpRequest object.
    if (thisObject.handle.readyState == 4) {
      // Stop waiting, because we have a response.
      thisObject.stopAjaxWait();
      if (thisObject.debug) { thisObject.handleDebug("Last Status:<br/>" + thisObject.handle.status + " - " + thisObject.handle.statusText + "\n" + thisObject.handle.getAllResponseHeaders()); }
      if (thisObject.handle.status == 200) {
        // Everything is good, so move on from here.
        thisObject.getResults();
        if (thisObject.targetFunc != "") { // Only run this if a function name was actually given.
          if (thisObject.debug) {
            thisObject.handleDebug("Preparing to run custom function:<br/>" + thisObject.targetFunc + "(" + thisObject.targetParams + ");");
          }
          // Call this function (w/ params) once we get a successful response.
          eval(thisObject.targetFunc + "(" + thisObject.targetParams + ");");
        }
      } else {
        if (!thisObject.silent) { // Only do this if we're not in a "silent" mode.
          // Response was bad, so we have nothing to work with--just alert the user to the error.
          thisObject.handleError("Sorry, the server was unable to return the expected AJAX results.");
        }
        // Quietly finish killing the current request.
        thisObject.abortRequest(1);
      } // Not status 200
    } else if (!thisObject.isWaiting) {
      // If state is 0, 1, 2, or 3, then start waiting; if already waiting, then just keep waiting.
      if (thisObject.waitMsg != "" && !thisObject.silent) {
        thisObject.startAjaxWait(thisObject.waitFor, thisObject.waitMsg);
      }
      thisObject.isWaiting = true;
    }
  }

  this.getResults = function() {
    if (thisObject.debug) { thisObject.handleDebug("getResults " + thisObject.myName); }
    if (thisObject.aborted) { return false; } // Leave immediately if the request was aborted.
    // Stop all user notification/waiting, and store the text and xml results for later.
    thisObject.stopAjaxWait();
    thisObject.isWaiting = false;
    clearTimeout(thisObject.requestTimer);
    this.textresult = thisObject.handle.responseText; // Send the results directly to the original object.
    if (thisObject.debug && thisObject.textresult != "") {
      thisObject.handleDebug("Response Text: " + thisObject.textresult);
    }
    this.xmlresults = thisObject.handle.responseXML;  // Send the results directly to the original object.
  }

  // Assign the attributes to the object. Most of these can be changed.
  this.debug = false;       // Show debug messages during the request/response processes.
  this.silent = false;      // Should we stay silent while we process this AJAX request?
  this.waitMsg = "Loading"; // Message to be displayed to the user while the request runs.
  this.waitFor = 20;        // How long should we make the user wait? (Default is 20 seconds).
  this.waitFrom = null;     // Show the wait message in relation to what form field?
  this.methodType = "post"; // Should we POST or GET the data via AJAX? (Default is POST).
  this.targetFunc = "";     // Function to be run immediately after the response has been returned.
  this.targetParams = "";   // Parameters to be passed to the function above (targetFunc).
  // Control vars (do not access/change these directly).
  this.myName = myName;     // Name of the object, for self-reference and debugging.
  this.requestTimer = null; // Handle to the timer waiting on a server response (time until abort).
  this.waitTimer = null;    // Handle to the timer that drives the alert box.
  this.isWaiting = false;   // Flag that designates if we're still waiting for a response or not.
  this.aborted = false;     // Reset the abort status on the object.
  this.handle = null;       // Set the initial handle status to null.
  thisObject = this; // Make the object aware of itself, global to all methods.
}

function detectAjax() {
  // Quietly attempt to create an XMLHTTP object, and just pass it back.
  // NOTICE: Do not attempt to use this object! This is for statistics purposes only!
  try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
  try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
  try { return new XMLHttpRequest(); } catch(e) {}
  return false;
}

// Begin dynamic search & display functions
var targetSearchField;
var targetValueField;
var searchTimeout;
function prepareSearchBox(targetSearchName,targetValueName) {
  // To use a single field for searching and storing the value, pass "" as parameter #2.
  if (targetSearchName) {
    // If we can directly address the given name as an object, use it.
    targetSearchField = targetSearchName;
  } else if (targetSearchName != "") {
    // Otherwise, we were given a field name, which we need to find first.
    targetSearchField = document.getElementById(targetSearchName);
  }
  if (targetValueName) {
    // If we can directly address the given name as an object, use it.
    targetValueField = targetValueName;
  } else if (targetValueName != "") {
    // Otherwise, we were given a field name, which we need to find first.
    targetValueField = document.getElementById(targetValueName);
  }
  var thisSearchBox = document.getElementById("dynSearchBox");
  if (targetSearchField && thisSearchBox) {
    var newTop = findPosY(targetSearchField) + 17;
    var newLeft = findPosX(targetSearchField) + 1;
    thisSearchBox.innerHTML = "";
    thisSearchBox.style.top = newTop + "px";
    thisSearchBox.style.left = newLeft + "px";
    thisSearchBox.style.display = "none";
  }
}

// This gets called AFTER the AJAX routine returns a result.
function populateSearchBox(arrayData) {
  // Incomming data is expected to be a maximum of 3 arrays, in text form.
  // In Order: array_values, array_text, array_counts
  // First we eval the data to make them available, then we parse them out.
  if (arrayData != "") {
    eval(arrayData);
  } else {
    // ABORT! No data returned from AJAX request!
    return true;
  }
  var thisSearchBox = document.getElementById("dynSearchBox");

  // We should have values returned, and the count should match the # of text items.
  if (array_values.length > 0 && array_values.length == array_text.length) {
    var content = "";   // Init our final content buffer
    var maxLength = 0;  // Maximum size of text, so we can resize the box accordingly
    var newWidth = "width:98%;";
    if (array_counts.length > 0) {
      // If the count column is included, then we need to shrink the size of the text column.
      newWidth = "width:70%;";
    }
    var lastValue = "";
    var lastText = "";
    var total = 0;
    for (ix = 0; ix < array_values.length; ix++) {
      content = content + '<span style="clear:both;' + newWidth + '">';
      if (array_text[ix] != "") {
        var textLength = array_text[ix].length;
        content = content + '<span style="float:left;clear:both;">';
        content = content + '<a href="javascript:useSearchResult(\'' + array_values[ix] + '\',\'' + array_text[ix] + '\');" ';
        content = content + ' safelink="yes" class="searchlink" onmouseover="javascript:hideStatus();" ';
        content = content + 'title="' + array_text[ix] + '">' + array_text[ix] + '</a></span>';
        lastValue = array_values[ix];
        lastText = array_text[ix];
        // This finds the longest string in our results
        if (textLength > maxLength) { maxLength = textLength; }
        if (array_counts[ix] != "" && array_counts[ix] != undefined) {
          var countLength = array_counts[ix].length;
          content = content + '<span style="float:right;color:maroon;clear:both;">' + array_counts[ix] + '</span>';
          if ((textLength + countLength) > maxLength) { maxLength = textLength + countLength; }
        }
        total++;
      }
      content = content + '</span><br/>';
    }

    if (total > 0) {
      // Resize the box to fit the text it contains.
      var newWidth = (maxLength * 8); // Allow 8 pixels per character
      var newHeight = (total * 15); // Allow 15 pixels per character
      if (newWidth < 160) { newWidth = 160; }
      if (newHeight > 200) { newHeight = 200; }
      thisSearchBox.style.width = newWidth + "px";
      thisSearchBox.style.height = newHeight + "px";
      thisSearchBox.innerHTML = content;
      thisSearchBox.style.display = "block";

      // If only 1 result exists, just select it automatically
      if (total == 1) { useSearchResult(lastValue,lastText); }
    } else {
      hideSearchBox();
    }
  }
}

// This actually does something with our results, after the user selects something.
function useSearchResult(thisValue,thisText) {
  // If no value was given, just quit.
  if (thisValue == "") { return false; }

  // Re-populate the search field with the actual value chosen.
  if (targetValueField) {
    // If separate Search/Value fields are provided, update both.
    targetSearchField.value = thisText;
    targetValueField.value = thisValue;
  } else {
    // Otherwise just update the main search field with the value.
    targetSearchField.value = thisValue;
  }
  hideSearchBox();
}

// Destroys the search box on the screen once the user selects something.
function hideSearchBox() {
  var thisSearchBox = document.getElementById("dynSearchBox");
  if (thisSearchBox) {
    thisSearchBox.innerHTML = "";
    thisSearchBox.style.display = "none";
  }
}

// Output the structure for the dropdown search box.
document.write("<style type=\"text/css\">");
document.write("a.searchlink { text-decoration:none; width:98%; color:black; }");
document.write("a.searchlink:hover { background-color:#333366; color:white; }");
document.write(".dynsearchbox { position:absolute; z-index:99; width:160px; height:200px; padding:0px; display:none; border:1px solid black; background-color:#FFFFFF; color:black; padding-bottom:5px; overflow:auto; }");
document.write("</style>");
document.write("<div class=\"dynsearchbox\" id=\"dynSearchBox\" onblur=\"javascript:setTimeout(\'hideSearchBox\',\'500\');\" onfocus=\"javascript:function() { clearTimeout(searchTimeout); }\"></div>");

// Output the structure for the new AJAX "wait" message.
document.write("<style type=\"text/css\">");
document.write(".waitbox { position:absolute; left:40%; z-index:99; width:180px; height:20px; padding:10px; display:none; text-align:center; border:2px solid maroon; background-color:#FFFFFF; color:black; }");
document.write("</style>");
document.write("<div class=\"waitbox\" id=\"waitBox\"></div>");

// Output the structure for the new AJAX "debug" message.
document.write("<style type=\"text/css\">");
document.write(".debugbox { position:relative; top:0px; z-index:99; width:98%; height:120px; padding:10px; overflow:auto; display:none; text-align:left; border:1px solid black; background-color:#FFFFFF; color:black; }");
document.write("</style>");
document.write("<div class=\"debugbox\" id=\"debugBox\"><h3>AJAX Debug Console</h3></div>");

// Set a global flag that indicates this library has been loaded by the browser.
var LIB_AJAX_LOADED = true;

// End library
