


var _AjaxMessageCenter = new AjaxMessageCenter();
var _areBundlingMessagesIntoOneRequest = false;

/**
 * sendAjaxMessage()
 * 
 * Send a single ajax message to the server or append to the queue if called
 * between beginAddingAjaxMessagesToQueue()/sendQueuedAjaxMessages().
 * 
 * @param targetClass string
 * @param targetMethod string
 * @param targetMethodArguments anything (optional)
 * @param callbackFunction function (optional)
 */
function sendAjaxMessage(targetClass, targetMethod, targetMethodArguments, callbackFunction)
{
  message = new AjaxMessage(targetClass, targetMethod, targetMethodArguments, callbackFunction);
  
  _AjaxMessageCenter.appendMessageToQueue(message);
  
  if (!_areBundlingMessagesIntoOneRequest) {
    _AjaxMessageCenter.sendQueuedMessages();
  }
}

/**
 * beginAddingAjaxMessagesToQueue()
 * 
 * After this function has been called, any sendAjaxMessage() calls will be added
 * to a queue and sent when you call sendQueuedAjaxMessages(). Using the queue
 * will bundle multiple messages into a single HTTP request, which is faster and
 * more reliable.
 */
function beginAddingAjaxMessagesToQueue()
{
  if (_areBundlingMessagesIntoOneRequest) {
    alert('WARNING: Cannot nest begin/endSendingAjaxMessages().');
    return;
  }
  
  _areBundlingMessagesIntoOneRequest = true;
}

/**
 * sendQueuedAjaxMessages()
 * 
 * Send any messages in the queue
 */
function sendQueuedAjaxMessages()
{
  if (!_areBundlingMessagesIntoOneRequest) {
    alert('WARNING: endSendingAjaxMessages() called when we are not sending messages.');
    return;
  }
  
  _areBundlingMessagesIntoOneRequest = false;
  
  _AjaxMessageCenter.sendQueuedMessages();
}




function AjaxMessageCenter()
{
  this.messageQueue = new Array();
  this.busyIndicatorRetainCount = 0;
  
  this.appendMessageToQueue = function(message)
  {
    this.messageQueue.push(message);
  }
  
  this.sendQueuedMessages = function()
  {
    messageObjects = new Array();
    for (var i = 0; i < this.messageQueue.length; i++) {
      messageObjects.push(this.messageQueue[i].buildMessageObj());
    }
    
    req = this.buildXHR();
    var thisVar = this;
    var messageQueueCopy = thisVar.messageQueue;
    req.onreadystatechange = function() { thisVar.processXHRStatusChange(req, messageQueueCopy); };
    
    req.open('POST', '/include/postoffice.php');
    req.send(messageObjects.toJSONString());
    
    this.messageQueue = new Array();
    this.showBusyIndicator();
  }
  
  this.buildXHR = function()
  {
    var req = false;

    try { req = new XMLHttpRequest(); } catch(e) {} // most browsers
    if (!req) try { req = new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} // ie6
    if (!req) try { req = new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} // ie5
    if (!req) {
      alert('Error: Your browser does not support AJAX. Please upgrade your browser and try again.'); // unsupported browser
      return null;
    }

    return req;
  }
  
  this.processXHRStatusChange = function(xhrObject, messageObjects)
  {
    // make sure we're finnished loading.
    if (xhrObject.readyState < 4)
      return;
    
    // check for http error of "0" - firefox does this, we don't know why
    if (xhrObject.status == 0)
      return;
    
    // Make we didn't 404 or anything like that. NOTE: FireFox occasionally logs a wierd exception when executing 'xhrObject.status'.
    if (xhrObject.status != 200) {
      alert('Error communicating with server.' + "\n\n HTTP Error: " + xhrObject.status);
      return;
    }
      
    // Parse response.
    results = xhrObject.responseText.parseJSON();
    if (results == false) {
      alert('Error communicating with server.' + "\n\nServer response was: \n" + xhrObject.responseText);
      return;
    }
  
    // Run callbacks.
    for (var i = 0; i < messageObjects.length; i++) {
      if (messageObjects[i].callbackFunction != null)
        messageObjects[i].callbackFunction(results[i]);
    }
    
    this.hideBusyIndicator();
  }
  
  this.showBusyIndicator = function ()
  {
    this.busyIndicatorRetainCount++;
    
    busyEl = document.getElementById('ajaxmessage-busy');
    if (busyEl != null) busyEl.style.display = (this.busyIndicatorRetainCount > 0) ? 'block' : 'none';
  }
  
  this.hideBusyIndicator = function ()
  {
    this.busyIndicatorRetainCount--;
    
    busyEl = document.getElementById('ajaxmessage-busy');
    if (busyEl != null) busyEl.style.display = (this.busyIndicatorRetainCount > 0) ? 'block' : 'none';
  }
    
}

function AjaxMessage(targetClass, targetMethod, targetMethodArguments, callbackFunction)
{
  this.targetClass = targetClass;
  this.targetMethod = targetMethod;
  this.targetMethodArguments = targetMethodArguments;
  this.callbackFunction = callbackFunction;

  this.buildMessageObj = function()
  {
    var dataObj = {
      targetClass: this.targetClass,
      targetMethod: this.targetMethod
    }
    
    if (this.targetMethodArguments != null)
      dataObj.targetMethodArguments = this.targetMethodArguments;
    
    if (this.callbackFunction != null)
      dataObj.callbackFunction = this.callbackFunction.toString();
    
    return dataObj;
  }
}

/*
    json.js
    2006-10-05

    This file adds these methods to JavaScript:

        object.toJSONString()

            This method produces a JSON text from an object. The
            object must not contain any cyclical references.

        array.toJSONString()

            This method produces a JSON text from an array. The
            array must not contain any cyclical references.

        string.parseJSON()

            This method parses a JSON text to produce an object or
            array. It will return false if there is an error.

    It is expected that these methods will formally become part of the
    JavaScript Programming Language in the Fourth Edition of the
    ECMAScript standard.
*/
(function () {
    var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        s = {
            array: function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            number: function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            object: function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            string: function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        };

    Object.prototype.toJSONString = function () {
        return s.object(this);
    };

    Array.prototype.toJSONString = function () {
        return s.array(this);
    };
})();

String.prototype.parseJSON = function () {
    try {
        return (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)) &&
            eval('(' + this + ')');
    } catch (e) {
        return false;
    }
};
