/* Copyright (c) 2006-2008 Ido Kanner under the Modified MIT license
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software (the "Software"), to deal in the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge,  publish, distribute,
 * sub-license, and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 
 * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */

/* TODO: creat error messages as constants */

// print more info if we are in debug mode ...
var ajax_DEBUG = false;

// The type of connection
var ajax_async  = true;  // non blocking <- return right away <- The event handler will not work !!! TODO: make an event on such case
var ajax_sync   = false; // blocking <- wait until the connection was made, and then return

//types of header for connection. Some AJAX implementation might support only GET and POST.
var ajax_HEADER_head   = "HEAD";
var ajax_HEADER_put    = "PUT";
var ajax_HEADER_delete = "DELETE";
var ajax_HEADER_get    = "GET";
var ajax_HEADER_post   = "POST";

// the XMLHttpRequest readyStatus values
// the string values
var ajax_readyState = [
                 /* 0 */ "not initialized",  
                 /* 1 */ "connection etablished", 
                 /* 2 */ "request received",
                 /* 3 */ "answer in process", 
                 /* 4 */ "finished"
                        ];

// the number values
var ajax_readyStatus_NotInitialized        = 0;
var ajax_readyStatus_ConnectionEstablished = 1;
var ajax_readyStatus_RequestReceived       = 2;
var ajax_readyStatus_AnswerInProcess       = 3;
var ajax_readyStatus_Finished              = 4;

// Possible return value from my web site. If your content needs more, you can add the values here.
var ajax_HTTP_OK                  = 200;
var ajax_HTTP_NoContent           = 204;
var ajax_HTTP_MovedPermanently    = 301;
var ajax_HTTP_TemporaryRedirect   = 307;
var ajax_HTTP_BadRequest          = 400;
var ajax_HTTP_Unauthorized        = 401;
var ajax_HTTP_PaymentRequired     = 402;
var ajax_HTTP_Forbidden           = 403;
var ajax_HTTP_PageNotFound        = 404;
var ajax_HTTP_MethodNotAllowed    = 405;
var ajax_HTTP_NotAcceptable       = 406;
var ajax_HTTP_RequestTimeout      = 407;
var ajax_HTTP_InternalServerError = 500;

var ajax_HTTP_type_HTTP           = 'http';
var ajax_HTTP_type_HTTPS          = 'https';

// our constructor
function ikajax(address)
{
  // Make sure we have the right value for our address
  if (address == null || typeof address != "string") 
    address = "/";
    
  this.version = "0.1"; // The version of my library
  
  this.obj               = null;                // our XMLHttpRequest object
  this.address           = address;             // the address of our website.
  this.page              = "";                  // the page we wish to use (including the path of how to get it)
  this.params            = new Array;           // a hash of param name as key and value is the value of the param. 
                                                // if this array is empty, then no parameters are given
  this.head              = ajax_HEADER_get;     // our HTTP header
  this.method            = ajax_async;          // what is our method of connection ?
  this.contentType       = "text/html";         // what is our mime type for the document we are expecting ?
  this.sendContentType   = "text/html";         // what is our mime type for the content we are sending ?
  this.returnContentXML  = null;                // the xml content we should have on success 
  this.returnContentText = "";                  // the text content we should have on success
  this.httpType          = ajax_HTTP_type_HTTP; // The default HTTP type, either HTTP or HTTPS
}

// our debug function is also part of our library.
ikajax.prototype.printDebug = function(message)
{
  if (ajax_DEBUG)
  {
    //alert ('AJAX: '+message);
    Log.add ("AJAX: " + message);
    Log.displayLog();
  } // if (ajax_DEBUG)
} // ikajax.prototype.printDebug = function(message)

// if this will be initilized, every exception with capture will 
// arrive to this function instead of reasing them.
ikajax.prototype.onError = null;

// Callback event when status changes.
// The first parameter should be the readyStatus, and the second parameter should be HTTP status
ikajax.prototype.onStatus = null;

// This event when set, will be triggered when we updated the content.
ikajax.prototype.onContentChanged = null;

// This function add the name and value to the params hash
ikajax.prototype.addParam = function(name, value) 
{
  this.params[name] = value;
} // ikajax.prototype.addParam = function(name, value) 

// we are initilize our 
ikajax.prototype.initObj = function () 
{
  // do we have a native ajax support ?
  if(window.XMLHttpRequest) 
  {
    try 
    {
      // lets use the native support.
      this.obj = new XMLHttpRequest(); //lets have it ?
      if (ajax_DEBUG)
        this.printDebug ('We have native object.');
    } // try
    catch (e)
    { // we were unable to use the native support altough the object does exists ...
      this.obj = null;
      this.printDebug("initObj[this.obj = new XMLHttpRequest()] Exception: "+ e); // on debug mode we will see the exception
      if (this.onError == null)
        throw(e); // we throwing the exception, because here we do not have anything to do with it ...
      else
        this.onError(e); // here is the exception, see what you can do with it
    } // catch (2)
  } // if(window.XMLHttpRequest) 
  else if (window.ActiveXObject) // No ? are we using IE ?
  {
    try 
    { 
      // we are looking for active-x that if it does exists, we will have the object we need
      this.obj = new ActiveXObject("Msxml2.XMLHTTP"); 
      this.printDebug ('We have the Msxml2.XMLHTTP object.');
    } // try
    catch (e)
    {
      try
      {
        // we try another active-x that if it does exists, will return our object...
        this.obj = new ActiveXObject("Microsoft.XMLHTTP");
        this.printDebug ('We have the Microsoft.XMLHTTP object.');
      } // try
      catch (e)
      {
        // we where unable to locate the component for ajax... or something bad have happened.
        this.obj = null;
        this.printDebug("initObj[this.obj = new ActiveXObject(\"Microsoft.XMLHTTP\");] Exception: " +e);
        if (this.onError == null)
          throw(e);
        else
          this.onError(e);
      } // catch (e)
    } // catch
  } // else if (window.ActiveXObject)
  else
  {
    // No AJAX support, or your web browser does it differently then the normal known ways ...
    this.obj = null;
    this.printDebug("Could not find support for AJAX.");
    if (this.onError == null)
      throw("Could not find support for AJAX.");
    else
      this.onError("Could not find support for AJAX.");
  } // else
  
} // ikajax.prototype.initObj = function () 


// This function get executed when onreadystatechange callback is used
// This function also call the user callback in case he or she made one
ikajax.prototype.onConnection = function() 
{
  this.printDebug("Entered to onConnection");
  
  try
  {
    // do we have a callback function for statuses ?
    if (this.onStatus != null)
     if (this.obj.readyState && this.obj.status)
       this.onStatus(this.obj.readyState, this.obj.status); //so lets give the statuses to the user callback
  
    // let us know while debugging what is the stage we are at.
    var readyState = ""; 
    try
    {
      readyState = this.obj.readyState;
    } //try
    catch (e)
    {
       readyState = "not present";
    } //catch (e)
    
    var status_     = "";
    try
    {
      status_ = this.obj.status; 
    } //try
    catch (e)
    {
      status_ = "not present";
    } // catch(e)
    this.printDebug("Ready State: [" + readyState  + " = "+ ajax_readyState[readyState] +
                                 "] \| Status: [" + status_ + "]");
    
    if (readyState == 4) // did the operation finished ?
    {
      if (status_ == ajax_HTTP_OK) // did the server gave us what we need ?
      {
        try 
        {
          this.printDebug("Going to set up the responseXml");
          if (this.obj.responseXML != null) // lets give xml object back to the user, if that possible
          {
            this.returnContentXML = this.obj.responseXML; // we are trying to return xml
          }
          else
            this.returnContentXML = null; // we can not give xml to the user.
        } // try
        catch (e)
        { // an exception was raised, lets make the xml object null, and give the user the error message
          this.returnContentXML = null;
          this.printDebug("Tried to assign the xml object\n" + e);
          if (this.onError == null)
            throw(e);
          else
            this.onError(e);
        } // catch (e)
      
        try 
        {
          this.returnContentText = this.obj.responseText; // lets give the user a text result.
        } // try
        catch (e)
        { // an exception was raied
          this.printDebug("Tried to assign the text \n" +e);
          this.returnContentText = "";
          if (this.onError == null)
            throw(e);
          else
            this.onError(e);
        } // catch (e)
      } // if (this.obj.status == ajax_HTTP_OK)
      else
      { // the status from the server is no OK (200)
        this.printDebug("Unable to retrive the content.\n readyState : "+ readyState + " status_ : " + status_);
        if (this.onError == null)
          throw("Unable to retrive the content.");
        else
          this.onError("Unable to retrive the content.");
      } // else
    } // if (this.obj.readyState == 4)
  } // try
  catch (e)
  { // Something went wrong and no other try/catch block had it ...
    this.printDebug("Exception: " + e);
    if (this.onError == null)
      throw(e);
    else
      this.onError(e);
  } // catch (e)
  try
  {
    if (this.obj.responseXml != null || this.obj.responseText != "")
    { 
      if (this.onContentChanged != null)
      {
         this.onContentChanged();
      } // if (this.onContentChanged == "function")
      else
      { 
        this.printDebug("No event handler for content changes");
      } // else
    } // if (this.obj.responseXml || this.obj.responseText)
    else
    {
      this.printDebug("We do not have content at this point.");
    }
  } // try
  catch (e)
  { // Something went wrong and no other try/catch block had it ...
    this.printDebug("Exception: " + e);
    if (this.onError == null)
      throw(e);
    else
      this.onError(e);
  } // catch (e)
} // this.obj.onConnection = function()

//open a connection 
ikajax.prototype.openConnection = function()
{
  if (this.obj == null)
    this.initObj();

  if (this.obj == null)
  {
    this.printDebug("Could not find support for AJAX.");
    if (this.onError == null)
      throw("Could not find support for AJAX.");
    else
      this.onError("Could not find support for AJAX.");

    return;
  }

  if (typeof this.obj.overrideMimeType == "function")
  {
    if (arguments.length == 1 && arguments[0].match(/^.*?\/.*?[^\/]$/))
      this.obj.overrideMimeType (arguments[0]);
    else
      this.obj.overrideMimeType(this.contentType);
  }
  
  var _this = this;
  var fncparams = function (list) 
  {
    var items = new String("");
    for (var param in list)
    {
      if (items.length == 0)
        items = "?";
      else
        items += "&";
      //encodeURIComponent
      _this.printDebug("fncparams: name =" + name + " encoded name = "+ encodeURIComponent(param) +
                       "\n param = " + list[param] + " encoded param = " + encodeURIComponent(list[param]));
      items += encodeURIComponent(param) + "=" + 
               encodeURIComponent(list[param]);
    } // for (var i=0; i<=this.params.length; i++)

    return items;
  } // params = function ()
  
  var parametersList = fncparams(this.params);
  this.printDebug("parametersList: "+ parametersList);
  
  if (! this.address.match(/^\/.*?$/) && ! this.address.match(/^\/$/))
  {
    this.printDebug("We have full address");
    if (! this.address.match(/^http\:\/\//) && ! this.address.match(/^https\:\/\//))
    {
      this.printDebug("About to add the " + this.httpType + " prefix to the address.");
      this.address = this.httpType + "://" + this.address;
    }
			
    if (! this.address.match(/\/$/))
      this.address += "/";
   } // if (! this.address.match(/^\/$/))
   else
   {
     this.printDebug("We do not have full address");
   }
  
  try 
  {
   var _this = this; 
   this.obj.onreadystatechange = function() { _this.onConnection();};
   this.printDebug("About to send message with " + this.head + " header: \n"+ this.address + this.page + parametersList);
   if (this.head != ajax_HEADER_post)
   {
     this.obj.open(this.head, this.address + this.page + parametersList, this.method);
     this.obj.setRequestHeader('Content-Type', this.sendContentType);
     this.obj.send(null);
   }
   else
   {
     this.obj.open(this.head, this.address + this.page, this.method);
     this.obj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
     parametersList = parametersList.replace(/^\?/, "");
     this.printDebug("Content-length " + parametersList.length);
     this.obj.setRequestHeader("Content-length", parametersList.length);
     this.obj.send(parametersList);
    }
    this.printDebug("Sent the message with the " + this.head + " header.");
  } // try
  catch (e)
  {
    this.printDebug("Exception: "+ e);
    if (this.onError == null)
      throw(e);
    else
      this.onError(e);
  } // catch (e)

} // ikajax.prototype.openConnection = function()


