/**
* Trida pro Ajax komunikaci se serverem. Komunikace v teto tride je provadena vzdy asynchronne.
*
* @param object callback Parametrem je objekt obsahujici dve metody: onSuccess a onError. Temto dvoum metodam je predan vysledek pozadavku.
*
* @example

    var callback = {
      onSuccess: function(data) {
        alert(data);
      },
      onError: function() {
        alert('Doslo k chybe!');
      }
    };
    var a = new Ajax(callback);
    a.connect("seznam-stranek?data");

* nebo

    var a = new Ajax({ onSuccess: function(data) { alert(data); } });
    a.connect("seznam-stranek?data");

*/
var Ajax = function(callback) {
  var request = null;
  var callback = (typeof callback == "undefined") ? false : callback;

  /**
  * Privatni metoda pro inicializaci ajax objektu
  *
  */
  var _init = function() {
    if (request != null) {
      alert("Previous Ajax request isn't finished!");
      return false;
    }
    // -- inicializace privatni instance pro ajax request
    if (typeof XMLHttpRequest == "undefined") {
       request = new ActiveXObject(navigator.userAgent.indexOf("MSIE 5") >= 0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
    }
    else {
      request = new XMLHttpRequest();
    }
    return true;
  };

  /**
  * Kontrola vysledku odeslaneho pozadavku
  *
  */
  var _httpSuccess = function() {
    try {
      // -- pokud neni vracen zaden stav a jedna se pozadavek na lokalni soubor, tak je to OK
      return !request.status && location.protocol == "file:" ||
        // -- status v rozmezi 200 - 299 je take OK
        (request.status >= 200 && request.status < 300) ||
        // -- status 304 oznacuje nezmenenou stranku, take OK
        request.status == 304 ||
        // -- osetreni pro Safari prohlizec - ten vraci prazdny status pokud se stranka nezmanila, take OK
        navigator.userAgent.indexOf("Safari") >= 0 && typeof request.status == "undefined";
    }
    catch(e) { };

    return false;
  };

  /**
  * Funkce vypreparuje data s odpovedi serveru na pozadavek. Data vrati ve spravnem formatu (XML nebo text),
  * pripadne pokud byl posla java skript, tak jej provede.
  *
  * @param typ string Parametr definuje typ ocekavane odpovedi (XML, text, html, script). Pokud neni zadan, tak se jej funkce pokusi zjistit sama z hlavicky odpovedi
  */
  var _getHttpData = function(typ) {
    // -- nacteni hlavicky prijate v odpovedi ze serveru
    var ct = request.getResponseHeader("Content-Type");
    // -- pokud neni nastaven vychozi typ, tak se provede kontrola jestli byla vracena XML data
    var data = !typ && ct && ct.indexOf("xml") >= 0;
    // -- pokud bylo poslano XML tak se nacte, jinak se nacte jako text
    data = typ == "xml" || data ? request.responseXML : request.responseText;
    // -- pokud byl prijat java skript, tak se provede
    if (typ == "script") {
      eval.call(window,data);
    }
    // -- pokud jde o JSON objekt
    else if (typ == 'json') {
      data = eval('(' + data + ')');
    }

    return data;
  };

  /**
  * Kontrola stavu odeslaneho pozadavku
  *
  */
  var _onReadyStateChange = function() {
    if (request.readyState == 4) {
      if (_httpSuccess()) {
        if (callback !== false && typeof callback.onSuccess != "undefined") callback.onSuccess(_getHttpData());
      }
      else {
        if (callback !== false && typeof callback.onError != "undefined") callback.onError();
        else alert("Error on Ajax request!");
      }
      request = null;
    }
  };

  /**
  * Otevreni spojeni na server
  *
  * @param method string Nastavuje typ pozadavku - GET/POST
  * @param method string Url adresa serveru kam se posila pozadavek
  */
  var _open = function(method, url) {
    request.open(method,url,true);
    // -- nastaveni metody pro kontrolu stavu pozadavku
    request.onreadystatechange = _onReadyStateChange;
    // -- nastaveni spravne hlavicky v pripade POST pozadavku
    if (method.toLowerCase() == "post") {
      request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    }
    // -- osetreni pro Mozilla prohlizece - zabezpecuje poslani spravne delky dat
    if (request.overrideMimeType) {
      request.setRequestHeader("Connection","close");
    }
  };

  /**
  * Odesle pozadavek na server
  *
  * @param data array/form Parametr muze obsahovat pole ve form klic=hodnota nebo HTML element typu FORM
  */
  var _send = function(data) {
    if (typeof data == "undefined" || data == null) request.send(null);
    else request.send(_serializeData(data));
  };

  /**
  *  Serializuje data do parametru vhodneho pro odeslani na server
  *
  * @param data array/form Parametr muze obsahovat pole ve form klic=hodnota nebo HTML element typu FORM
  */
  var _serializeData = function(data) {
    var s = [];
    if (data.constructor == Array) {
      for (var j in data) {
        s.push(j + "=" + encodeURIComponent(data[j]));
      }
    }
    else {
      for (var i=0; i<data.length; i++) {
        if (data[i].type == "radio" && data[i].checked) s.push(data[i].name + "=" + encodeURIComponent(data[i].value));
        else if (data[i].type == "radio" && !data[i].checked) continue;
        else if (data[i].type == "checkbox" && !data[i].checked) continue;
        else s.push(data[i].name + "=" + encodeURIComponent(data[i].value));
      }
    }
    return s.join("&");
  };

  /**
  * Odeslani jednoducheho pozadavku na server metodou GET
  *
  * @param string url Adresa kam se pozadavek posila - muze obsahovat i GET parametry
  */
  this.connect = function(url) {
    if (_init()) {
      _open("GET",url,true);
      _send(null);
    }
  };

  /**
  * Odeslani POST pozadavku na server. Data jsou ziskany z formulare
  *
  * @param HTMLElement form Formular ve kterm jsou udaje pro odeslani
  */
  this.connectForm = function(form,url) {
    if (_init()) {
      _open("POST",url,true);
      _send(form);
    }
  };

  /**
  * Odeslani POST pozadavku na server. Data jsou ziskany z predaneho pole
  *
  * @param array data Pole hodnot ve forme "klic=hodnota"
  */
  this.connectData = function(data,url) {
    if (_init()) {
      _open("POST",url,true);
      _send(data);
    }
  };

}
