/*
Copyright (c) 2005 JSON.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, 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 Software shall be used for Good, not Evil.

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.
*/

Array.prototype.______array = '______array';

var JSON = {
    org: 'http://www.JSON.org',
    copyright: '(c)2005 JSON.org',
    license: 'http://www.crockford.com/JSON/license.html',

    stringify: function (arg) {
        var c, i, l, s = '', v;

        switch (typeof arg) {
        case 'object':
            if (arg) {
                if (arg.______array == '______array') {
                    for (i = 0; i < arg.length; ++i) {
                        v = this.stringify(arg[i]);
                        if (s) {
                            s += ',';
                        }
                        s += v;
                    }
                    return '[' + s + ']';
                } else if (typeof arg.toString != 'undefined') {
                    for (i in arg) {
                        v = arg[i];
                        if (typeof v != 'undefined' && typeof v != 'function') {
                            v = this.stringify(v);
                            if (s) {
                                s += ',';
                            }
                            s += this.stringify(i) + ':' + v;
                        }
                    }
                    return '{' + s + '}';
                }
            }
            return 'null';
        case 'number':
            return isFinite(arg) ? String(arg) : 'null';
        case 'string':
            l = arg.length;
            s = '"';
            for (i = 0; i < l; i += 1) {
                c = arg.charAt(i);
                if (c >= ' ') {
                    if (c == '\\' || c == '"') {
                        s += '\\';
                    }
                    s += c;
                } else {
                    switch (c) {
                        case '\b':
                            s += '\\b';
                            break;
                        case '\f':
                            s += '\\f';
                            break;
                        case '\n':
                            s += '\\n';
                            break;
                        case '\r':
                            s += '\\r';
                            break;
                        case '\t':
                            s += '\\t';
                            break;
                        default:
                            c = c.charCodeAt();
                            s += '\\u00' + Math.floor(c / 16).toString(16) +
                                (c % 16).toString(16);
                    }
                }
            }
            return s + '"';
        case 'boolean':
            return String(arg);
        default:
            return 'null';
        }
    },
    parse: function (text) {
        var at = 0;
        var ch = ' ';

        function error(m) {
            throw {
                name: 'JSONError',
                message: m,
                at: at - 1,
                text: text
            };
        }

        function next() {
            ch = text.charAt(at);
            at += 1;
            return ch;
        }

        function white() {
            while (ch != '' && ch <= ' ') {
                next();
            }
        }

        function str() {
            var i, s = '', t, u;

            if (ch == '"') {
outer:          while (next()) {
                    if (ch == '"') {
                        next();
                        return s;
                    } else if (ch == '\\') {
                        switch (next()) {
                        case 'b':
                            s += '\b';
                            break;
                        case 'f':
                            s += '\f';
                            break;
                        case 'n':
                            s += '\n';
                            break;
                        case 'r':
                            s += '\r';
                            break;
                        case 't':
                            s += '\t';
                            break;
                        case 'u':
                            u = 0;
                            for (i = 0; i < 4; i += 1) {
                                t = parseInt(next(), 16);
                                if (!isFinite(t)) {
                                    break outer;
                                }
                                u = u * 16 + t;
                            }
                            s += String.fromCharCode(u);
                            break;
                        default:
                            s += ch;
                        }
                    } else {
                        s += ch;
                    }
                }
            }
            error("Bad string");
        }

        function arr() {
            var a = [];

            if (ch == '[') {
                next();
                white();
                if (ch == ']') {
                    next();
                    return a;
                }
                while (ch) {
                    a.push(val());
                    white();
                    if (ch == ']') {
                        next();
                        return a;
                    } else if (ch != ',') {
                        break;
                    }
                    next();
                    white();
                }
            }
            error("Bad array");
        }

        function obj() {
            var k, o = {};

            if (ch == '{') {
                next();
                white();
                if (ch == '}') {
                    next();
                    return o;
                }
                while (ch) {
                    k = str();
                    white();
                    if (ch != ':') {
                        break;
                    }
                    next();
                    o[k] = val();
                    white();
                    if (ch == '}') {
                        next();
                        return o;
                    } else if (ch != ',') {
                        break;
                    }
                    next();
                    white();
                }
            }
            error("Bad object");
        }

        function num() {
            var n = '', v;
            if (ch == '-') {
                n = '-';
                next();
            }
            while (ch >= '0' && ch <= '9') {
                n += ch;
                next();
            }
            if (ch == '.') {
                n += '.';
                while (next() && ch >= '0' && ch <= '9') {
                    n += ch;
                }
            }
            if (ch == 'e' || ch == 'E') {
                n += 'e';
                next();
                if (ch == '-' || ch == '+') {
                    n += ch;
                    next();
                }
                while (ch >= '0' && ch <= '9') {
                    n += ch;
                    next();
                }
            }
            v = +n;
            if (!isFinite(v)) {
                error("Bad number");
            } else {
                return v;
            }
        }

        function word() {
            switch (ch) {
                case 't':
                    if (next() == 'r' && next() == 'u' && next() == 'e') {
                        next();
                        return true;
                    }
                    break;
                case 'f':
                    if (next() == 'a' && next() == 'l' && next() == 's' &&
                            next() == 'e') {
                        next();
                        return false;
                    }
                    break;
                case 'n':
                    if (next() == 'u' && next() == 'l' && next() == 'l') {
                        next();
                        return null;
                    }
                    break;
            }
            error("Syntax error");
        }

        function val() {
            white();
            switch (ch) {
                case '{':
                    return obj();
                case '[':
                    return arr();
                case '"':
                    return str();
                case '-':
                    return num();
                default:
                    return ch >= '0' && ch <= '9' ? num() : word();
            }
        }

        return val();
    }
};

/* AJASON
 * Bringing AJAX and JSON together
 *
 * This file is part of AJASON.
 *
 * AJASON is a PHP 5 library and JavaScript client for the upcoming
 * Web technology called AJAX. AJAX permits data to be fetched
 * asynchronously without the need for reloading the Web page and
 * thus allows the development of interactive GUI-like Web applications.
 * JSON is a lightweight data interchange format which is used by AJASON
 * to exchange data between server and client.
 *
 * AJASON is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * AJASON is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Foobar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * AJASON has been developed by Sven Jacobs <sven.jacobs@web.de>.
 * For more details visit sven-jacobs.de/wiki/AJASON
 *
 * Some parts of this project are contributed by other projects, namely
 * JSON (json.org) and JSON-PHP (mike.teczno.com/json.html) which are
 * copyrighted by their respective authors.
 */

/**
 * Ajax JavaScript class
 *
 * Contains methods to create the XMLHttpRequest object and to call remote
 * functions and object methods.
 *
 * @author Sven Jacobs <sven.jacobs@web.de>
**/
function Ajax( mtd, handler, debug, displayErrors )
{
   var _method        = mtd;
   var _handler       = handler;
   var _debug         = debug;
   var _displayErrors = displayErrors;
   var _clientVersion = "0.9";
   var _async = true;

   this.setAsync = function(val){
     _async = val;
   }
   /*** Private methods ***/

   /**
    * Return instance of XMLHttpRequest class
    *
    * @return Object of XMLHttpRequest
   **/
   function getXmlHttpRequestObject()
   {
      var xmlHttpRequest = false;

      if ( window.XMLHttpRequest )
      {
         try
         {
            xmlHttpRequest = new XMLHttpRequest();
         }
         catch (e)
         {
            xmlHttpRequest = false;
         }
      }
      else if ( window.ActiveXObject )
      {
         try
         {
            xmlHttpRequest = new ActiveXObject( "Msxml2.XMLHTTP" );
         }
         catch (e)
         {
            try
            {
               xmlHttpRequest = new ActiveXObject( "Microsoft.XMLHTTP" );
            }
            catch (e)
            {
               xmlHttpRequest = false;
            }
         }
      }

      // Override mime type if browser supports it (like Mozilla)
      if ( xmlHttpRequest.overrideMimeType )
      {
         xmlHttpRequest.overrideMimeType( "text/xml" );
      }
      TZR.xmlHttpRequest=xmlHttpRequest;
      return xmlHttpRequest;
   }

   /**
    * Prepare XMLHttpRequest object
    *
    * @param[in] xmlHttpRequest XMLHttpRequest object
    * @param[in] params Parameters of function or method which has been called
    * @param[in] data Data which will be send
    * @return Final data which will be send
   **/
   function prepareXmlHttpRequest( xmlHttpRequest, params, data )
   {
      var callbackFunction = getCallbackFunction( params );
      var handler;
      var finalData;

      if ( _method == "GET" )
      {
         handler   = _handler + "?" + data;
         finalData = null;
      }
      else
      {
         handler   = _handler;
         finalData = data;
      }

      xmlHttpRequest.onreadystatechange = function()
      {
         if ( xmlHttpRequest.readyState == 4 )
         {
            try
            {
               var response = JSON.parse( xmlHttpRequest.responseText );
            }
            catch (e)
            {
               alert( "AJAX error:\nUnknown response from server!\n\n" + xmlHttpRequest.responseText );
               return;
            }

            if ( response.errorCode == 0 && callbackFunction != null )
            {
               if ( _debug )
               {
                  // Show client/server versions and whole server response before executing callback function
                  alert( "AJAX debug info:\n\nServer version: " + response.serverVersion + "\nClient version: " + _clientVersion + "\nServer response: " + xmlHttpRequest.responseText );
               }
               callbackFunction( response.response );
            }
            else if ( response.errorCode != 0 && _displayErrors )
            {
               alert( "AJAX error:\n" + response.errorMessage );
            }
         }
      }

      xmlHttpRequest.open( _method, handler, _async );

      if ( _method == "POST" )
      {
         xmlHttpRequest.setRequestHeader( "Method",        "POST " + _handler + " HTTP/1.1" );
         xmlHttpRequest.setRequestHeader( "Content-Type",  "application/x-www-form-urlencoded" );
         xmlHttpRequest.setRequestHeader( "Content-Length", finalData.length );
      }

      return finalData;
   }

   /**
    * Do params include callback function?
    *
    * @param[in] params Parameters of function or method which has been called
    * @return Returns true if params contain callback function
   **/
   function paramsHaveCallbackFunction( params )
   {
      if ( typeof( params ) == "object" && params.length > 0 )
      {
         return ( typeof( params[ params.length - 1 ] ) == "function" );
      }
      else
      {
         return false;
      }
   }

   /**
    * Return callback function from params
    *
    * @param[in] params Parameters of function or method which has been called
    * @return Callback function
   **/
   function getCallbackFunction( params )
   {
      if ( paramsHaveCallbackFunction( params ) )
      {
         return params[ params.length - 1 ];
      }
      else
      {
         return null;
      }
   }

   /**
    * Prepare parameters
    *
    * @param[in] params Parameters of function or method which has been called
    * @return Prepared parameters
   **/
   function prepareParams( params )
   {
      var preparedParams = new Array();

      if ( typeof( params ) == "object" && params.length > 0 )
      {
         var end;

         if ( paramsHaveCallbackFunction( params ) )
         {
            end = 1;
         }
         else
         {
            end = 0;
         }

         for( var i = 0; i < params.length - end ; i++ )
         {
            preparedParams.push( params[ i ] );
         }
      }

      return preparedParams;
   }

   /**
    * Prepare data
    *
    * @param[in] cls Class name or null
    * @param[in] funcOrMtd Function name (if cls == null) or method name
    * @param[in] params Parameters of function or method which has been called
    * @return Prepared data
   **/
   function prepareData( cls, funcOrMtd, params )
   {
      var data = new Object();

      data[ "clientVersion" ] = _clientVersion;
      data[ "params" ]        = prepareParams( params );

      if ( cls != null )
      {
         data[ "cls" ] = cls;
         data[ "mtd" ] = funcOrMtd;
      }
      else
      {
         data[ "func" ] = funcOrMtd;
      }

      return "ajax=" + escape( JSON.stringify( data ) );
   }

   /**
    * Finally send request
    *
    * @param[in] params Parameters of function or method which has been called
    * @param[in] data Data to be send
   **/
   function call( params, data )
   {
      var xmlHttpRequest = getXmlHttpRequestObject();
      var finalData      = prepareXmlHttpRequest( xmlHttpRequest, params, data );
      xmlHttpRequest.send( finalData );
   }

   /*** Public methods ***/

   /**
    * Call remote method
    *
    * @param[in] cls Name of remote class
    * @param[in] mtd Name of remote method
    * @param[in] params Parameters of local method which has been called
   **/
   this.callMethod = function( cls, mtd, params )
   {
      var data = prepareData( cls, mtd, params );
      call( params, data );
   }

   /**
    * Call remote function
    *
    * @param[in] func Name of remote function
    * @param[in] params Parameters of local function which has been called
   **/
   this.callFunction = function( func, params )
   {
      var data = prepareData( null, func, params );
      call( params, data );
   }
}

