//======================================================
//                           
//                           
//                           
//------------------------------------------------------
//   $Author: mbe $
// $Revision: 1.9 $
//     $Date: 2007/09/10 09:37:27 $
//------------------------------------------------------
//
// Commercial in Confidence
//                   
// Copyright © 2004 by GateHouse A/S Denmark
// All Rights Reserved.          
// http://www.gatehouse.dk  mailto:gh@gatehouse.dk
//======================================================

// To use the AJAX component new an instance of AJAXRequest(...)
// The following parameters are necessary:
//
// _enMethod:           Method must be either EN_POST or EN_GET
// _szHandlerUrl:       Name of the server side handler that will be called for handling data
// _szData:             The Data that will be send to the server handler. Separate with '&' (e.g. 'x=5&y=test')
// _CallbackFunction:   If a special javascript callback function must be used parse it here.
//                      If not set a standard callback function is just called making an eval on the result (Optional)
// _fAsync:             Set if the server calls must be synchronous (Optional)
//
// If it is desired to see the communication log create a div with id 'ajax_log_dest' on the page. E.g. the following:
// <div id="ajax_log_dest" style="position:relative;left:0px;top:0px;width:400px;height:300px;border:1px solid #000000;
//    overflow:auto;"></div>
//
// Example on use:
// <... onclick="new AJAXRequest(EN_POST,'handler.php','szTarget=dest_div');">
//
// On the server the file handler.php is called. This could look like seen below:
//
//   // Extract all post variable names and value
//   $POST_PREFIX = "post";
//   extract($_POST, EXTR_PREFIX_ALL, $POST_PREFIX);
//   echo "var clTarget = document.getElementById('".$post_szTarget."');";
//   echo "clTarget.innerHTML = 'Hello world';";
//
// -------------------------------------------------------
// AJAX CLASS PARAMETERS:
// 
//    AJAXRequest(_enMethod, _szHandlerUrl, _szData, _CallbackFunction, _fAsync)
//
// GLOBAL METHODS:
//
//    // Examines the state and returns true if response is ready
//    bool ResultReady( _clAJAX ) // _clAJAX is the response object returned from the server
//
//    // Return the result as JSON format
//    JSON GetResultAsJson( _clAJAX ) 
//
//    // Return the result as a string
//    string GetResult( _clAJAX )
//
//    String StringEncode(_szString) 
//    String StringDecode(_szString) 
// -------------------------------------------------------
// JSON code:
//
//      array.toJSONString()
//      boolean.toJSONString()
//      date.toJSONString()
//      number.toJSONString()
//      object.toJSONString()
//      string.toJSONString()
//          These method produces a JSON text from a JavaScript value.
//          It must not contain any cyclical references. Illegal values
//          will be excluded.
//
//          The default conversion for dates is to an ISO string. You can
//          add a toJSONString method to any date object to get a different
//          representation.
//
//      string.parseJSON()
//          This method parses a JSON text to produce an object or
//          array. It can throw a SyntaxError exception.
//
// -------------------------------------------------------



var g_fDoAjaxLog = false;
var g_clLogDest;
var g_szActiveXVersion = "";

// Methods
var EN_POST = 'POST';
var EN_GET  = 'GET';

//---------- AJAXRequest() ----------//
function AJAXRequest( _enMethod, _szHandlerUrl, _szData, _CallbackFunction, _fAsync) 
{
   var self = this;

   // Check the dom to see if this is IE or not
   if (window.XMLHttpRequest) 
   {
      // Not IE
      self.clAJAX = new XMLHttpRequest();
   } 
   else if (window.ActiveXObject) 
   {
      // IE!
      // Instantiate the latest MS ActiveX Objects
      if (g_szActiveXVersion) 
      {
         self.clAJAX = new ActiveXObject(g_szActiveXVersion);
      }
      else 
      {
         // Loops through the various versions of XMLHTTP to ensure we're using the latest
         var versions = ["Msxml2.XMLHTTP.7.0", 
                        "Msxml2.XMLHTTP.6.0", 
                        "Msxml2.XMLHTTP.5.0", 
                        "Msxml2.XMLHTTP.4.0", 
                        "MSXML2.XMLHTTP.3.0", 
                        "MSXML2.XMLHTTP",
                        "Microsoft.XMLHTTP"];

         for (var i = 0; i < versions.length ; i++) 
         {
            try 
            {
               // try to create the object
               // if it doesn't work, we'll try again
               // if it does work, we'll save a reference to the proper one to speed up future instantiations
               self.clAJAX = new ActiveXObject(versions[i]);

               if (self.clAJAX) 
               {
                  g_szActiveXVersion = versions[i];
                  break;
               }
            }
            catch (objException) 
            {
                // Try next one
            };
         };
      }
   }
    
   // If no CallbackFunction is specified, then assign a default which executes the code returned by the server
   if (typeof _CallbackFunction == 'undefined' || _CallbackFunction == null) 
   {
      CallbackFunction = ResultHandler;
   }
   else
   {
      CallbackFunction = _CallbackFunction;
   }
   self.CallbackFunction = CallbackFunction;

   // Create an anonymous function to log state changes
   self.clAJAX.onreadystatechange = function( ) 
   {
      //AJAXLog("AJAXRequest Handler: State =  " + self.clAJAX.readyState);
      self.CallbackFunction(self.clAJAX);
   }

   // If no method specified, then default to POST
   if (!_enMethod) 
   {
      _enMethod = "POST";
   }

   if (typeof _fAsync == 'undefined' || _fAsync == null) 
   {
      _fAsync = true;
   }

   if ( !_szData )
   {
      _szData = "";
   }
   AJAXLog("--------------------");
   AJAXLog("AJAX Request: " + ((_fAsync) ? "Async" : "Sync") + " " + _enMethod + " URL: " + _szHandlerUrl + ", Data: " + _szData);

   // Make sure that each request has a unique URL - otherwise M$ Internet Exploder decides to cache the request(!!!)
   currentTime = new Date();
   if (_szData != "")
   {
      _szData += "&";
   }
   _szData += "uniqueTimeId="+currentTime.getTime();

   if (_enMethod == EN_POST) 
   {
      self.clAJAX.open(_enMethod, _szHandlerUrl, _fAsync);

      self.clAJAX.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset:UTF-8');
      self.clAJAX.setRequestHeader('Method', 'POST ' + _szHandlerUrl + 'HTTP/1.1');

      self.clAJAX.send(_szData);
   
   }else if (_enMethod == EN_GET)
   {
      self.clAJAX.open(_enMethod, _szHandlerUrl +  '?' + _szData, _fAsync);

      self.clAJAX.send('');
   }


   return self.clAJAX;
}

//---------- ResultHandler() ----------//
function ResultHandler( _clAJAX ) 
{
   if (_clAJAX.readyState == 4) 
   {
      try
      {
         if (_clAJAX.status == 200) 
         {
            AJAXLog('AJAXRequest is complete: ' + GetReadyStateTxt(_clAJAX.readyState) + "/" + _clAJAX.status + "/" + _clAJAX.statusText);
            if ( _clAJAX.responseText ) 
            {
               AJAXLog(_clAJAX.responseText);
               AJAXLog("--------------------");
               eval(_clAJAX.responseText);
            }
         }
      }
      catch (objException) 
      {
          // do nothing !!TBD
          // http://radio.javaranch.com/pascarello/2006/02/07/1139345471027.html
          // This happens when the page reloads, and all AJAX calls are terminated
      };
   }
}

//---------- ResultReady() ----------//
function ResultReady( _clAJAX ) 
{
   if (_clAJAX.readyState == 4) 
   {
      try
      {
         if (_clAJAX.status == 200) 
         {
            if ( _clAJAX.responseText ) 
            {
               return true;
            }
         }
      }
      catch (objException) 
      {
          // do nothing !!TBD
          // http://radio.javaranch.com/pascarello/2006/02/07/1139345471027.html
          // This happens when the page reloads, and all AJAX calls are terminated
      };
   }
   return false;
}

//---------- GetResultAsJson() ----------//
function GetResultAsJson( _clAJAX ) 
{
   AJAXLog(_clAJAX.responseText);
   return _clAJAX.responseText.parseJSON();
}

//---------- GetResult() ----------//
function GetResult( _clAJAX ) 
{
   AJAXLog(_clAJAX.responseText);
   return _clAJAX.responseText;
}

//---------- GetReadyStateTxt() ----------//
function GetReadyStateTxt(_iReadyState) 
{
   if (_iReadyState == 0)
   {
      return "Uninitialized";
   }
   else if (_iReadyState == 1)
   {
      return "Loading";
   }
   else if (_iReadyState == 2)
   {
      return "Loaded";
   }
   else if (_iReadyState == 3)
   {
      return "Interactive";
   }
   else if (_iReadyState == 4)
   {
      return "Complete";
   }
   else
   {
      return "Unknown";
   }
}

//---------- AJAXLog() ----------//
function AJAXLog( _szText, _fClear ) 
{
   if (g_fDoAjaxLog) 
   {
      if (!g_clLogDest) 
      {
         g_clLogDest = document.getElementById("ajax_log_dest");
      }

      if (g_clLogDest) 
      {
         if (_fClear) 
         {
               g_clLogDest.innerHTML = "";
         }

         var szOldContent = g_clLogDest.innerHTML;
         g_clLogDest.innerHTML = GetTime() +" "+ _szText + ((szOldContent) ? "<br>" : "") + szOldContent;
      }
   }
}

//---------- GetTime() ----------//
function GetTime()
{
   var clDate = new Date();
   return clDate.getHours() +":"+ clDate.getMinutes() +":"+ clDate.getSeconds();
}

//---------- StringEncode() ----------//
function StringEncode(_szString) 
{
   if (encodeURIComponent) 
   {
      return encodeURIComponent(_szString);
   }

   if (escape) 
   {
      return escape(_szString);
   }
}

//---------- StringDecode() ----------//
function StringDecode(_szString)
{
    _szString = _szString.replace(/\+/g, ' ');

    if (decodeURIComponent) 
    {
        return decodeURIComponent(_szString);
    }

    if (unescape) 
    {
        return unescape(_szString);
    }

    return _szString;
}

// Downloaded from "http://www.JSON.org/json.js"
/*
    
    json.js
    2006-11-09

    This file adds these methods to JavaScript:

        array.toJSONString()
        boolean.toJSONString()
        date.toJSONString()
        number.toJSONString()
        object.toJSONString()
        string.toJSONString()
            These method produces a JSON text from a JavaScript value.
            It must not contain any cyclical references. Illegal values
            will be excluded.

            The default conversion for dates is to an ISO string. You can
            add a toJSONString method to any date object to get a different
            representation.

        string.parseJSON()
            This method parses a JSON text to produce an object or
            array. It can throw a SyntaxError exception.

    It is expected that these methods will formally become part of the
    JavaScript Programming Language in the Fourth Edition of the
    ECMAScript standard in 2007.
*/

Array.prototype.toJSONString = function () {
    var a = ['['], b, i, l = this.length, v;

    function p(s) {
        if (b) {
            a.push(',');
        }
        a.push(s);
        b = true;
    }

    for (i = 0; i < l; i += 1) {
        v = this[i];
        switch (typeof v) {
        case 'undefined':
        case 'function':
        case 'unknown':
            break;
        case 'object':
            if (v) {
                if (typeof v.toJSONString === 'function') {
                    p(v.toJSONString());
                }
            } else {
                p("null");
            }
            break;
        default:
            p(v.toJSONString());
        }
    }
    a.push(']');
    return a.join('');
};

Boolean.prototype.toJSONString = function () {
    return String(this);
};

Date.prototype.toJSONString = function () {

    function f(n) {
        return n < 10 ? '0' + n : n;
    }

    return '"' + this.getFullYear() + '-' +
            f(this.getMonth() + 1) + '-' +
            f(this.getDate()) + 'T' +
            f(this.getHours()) + ':' +
            f(this.getMinutes()) + ':' +
            f(this.getSeconds()) + '"';
};

Number.prototype.toJSONString = function () {
    return isFinite(this) ? String(this) : "null";
};

Object.prototype.toJSONString = function () {
    var a = ['{'], b, i, v;

    function p(s) {
        if (b) {
            a.push(',');
        }
        a.push(i.toJSONString(), ':', s);
        b = true;
    }

    for (i in this) {
        if (this.hasOwnProperty(i)) {
            v = this[i];
            switch (typeof v) {
            case 'undefined':
            case 'function':
            case 'unknown':
                break;
            case 'object':
                if (v) {
                    if (typeof v.toJSONString === 'function') {
                        p(v.toJSONString());
                    }
                } else {
                    p("null");
                }
                break;
            default:
                p(v.toJSONString());
            }
        }
    }
    a.push('}');
    return a.join('');
};


(function (s) {
    var m = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    };

    s.parseJSON = function () {
        try {
            if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
                    test(this)) {
                return eval('(' + this + ')');
            }
        } catch (e) {
        }
        throw new SyntaxError("parseJSON");
    };

    s.toJSONString = function () {
        if (/["\\\x00-\x1f]/.test(this)) {
            return '"' + this.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 '"' + this + '"';
    };
})(String.prototype);
 
