/**
 * What I need:
 * 
 * x ObjectOriented framework
 * x Browser detection
 * x ElementPositioning
 * x CrossBrowser events
 * x DOMReady event
 *
 * size - < 20K
 */
 
 /**
  * static class with some useful methods
  * "nest" for all other classes
  */
function z(s){ //
  if (typeof(s)==='string'){
    return z.id(s);
  }
  else {
    return z.get(s);
  }
}
  

/**
 * returns Element if element is exists
 */
z.id = function(id){
     return z.Element.id(id);
};
   
z.get = function(el){
     return z.Element.get(el);
};
   
/**
 * copy all properties except prototype
 */
z.copy = function(dst,src){
      for(key in src){
        if (key!=='prototype'){
          dst[key] = src[key];
        }
      }
};

z.each = function(obj,fn){
        for(key in obj){
            if (obj.constructor.prototype[key]!==obj[key]){
                if (fn(key,obj[key])===false) break;
            }
        }
};
   
z.request = function(){
        try {
            return new ActiveXObject('MSXML2.XMLHTTP');
        }
        catch(e){};
        try {
            return new XMLHttpRequest();
        }
        catch(e){}
};
      
z.loadJS = function(url){
        r = z.request();
        r.open('get',url,false);
        r.send(null);
        if (window.execScript){
            window.execScript(r.responseText);
        } else {
            var script = document.createElement('script');
            script.setAttribute('type', 'text/javascript');
            script.text = r.responseText;
            var head = document.getElementsByTagName('head')[0];
            head.appendChild(script);
            head.removeChild(script);
        }
}; 
/**
 * experimental
 */    
z.bind = function(fn,obj){
      return function(){
        return fn.apply(obj,arguments);
      }
}      

 
 
 /**
  * inhetitance in JavaScript
  */
z.Class = function(){};
z.base = null;
z.Class.extend = function(obj){
    var this1 = this;
    var obj1 = function(){
        if (obj.construct!==undefined){
            obj.construct.apply(this,arguments);
        }
        else {
            if (this1.prototype.construct!==undefined){
                this1.prototype.construct.apply(this,arguments);
            }
        }
    }

    z.copy(obj1.prototype,this.prototype);
    z.copy(obj1.prototype,obj);
    z.copy(obj1,this);

    var old_constr = this1.prototype.construct;
    obj1.prototype.base = function(){
        if (this1.prototype.base){
            this.base = this1.prototype.base;
        }
        old_constr.apply(this,arguments);    
    };
    return obj1;
}

z.Class.implement = function(obj){
    z.copy(this.prototype,obj);
}   
 
 
/**
 * cross browser manitulations with element properties, events, styles
 */
z.Element = z.Class.extend({
    origin:null,
    events:{},
    construct:function(origin){
        origin._x = this;
        this.events = {};
        this.origin = origin;
        this.shortEvents(['click','mousedown','mouseup','mousemove','mouseover','mouseout','focus','blur','change','keypress','keydown','keyup']);
        this.css = this.style;
        this.p = this.prop;
    },
       
    shortEvents:function(names){
        var i = 0;
        for(i=0;i<names.length;i++){
            this[names[i]] = new Function("fn","remove","this.on('"+[names[i]]+"',fn,remove)");
        }
    },
       
   /**
    * add event listener
    */   
    on:function(eventname,func,remove){
        if (remove) return this.removeEvent(eventname,func);
        if (!this.events[eventname]) {	
     	    this.events[eventname] = [];
     	    var t1 = this;
     	    if (this.origin.addEventListener){
                this.origin.addEventListener(eventname, function(e){t1.fireEvent(eventname,e)},false);
            }
            else {
                this.origin.attachEvent('on'+eventname, function(e){t1.fireEvent(eventname,e)});
            }
        };
        this.events[eventname].push(func);
    },
   
    fireEvent:function(eventname,e){
        var hooks;
        if (e){
            e = z.Event.get(e);
        }
        else {
            e = z.Event.get(window.event);
        }
        e.owner = this;
        if (hooks = this.events[eventname]) {
     	    for(var i=0;i<hooks.length;i++){
     		    hooks[i].apply(this,[e]);
     	    }
        }
    },
   
  /**
   * removes event
   */
    removeEvent:function(eventname,func){
        if (this.events[eventname]){
            var found=false,i,len = this.events[eventname].length;
            for (i=0;i<len;i++){                
                if (this.events[eventname][i]==func){
                    found = i;                    
                    break;
                }
            }
            if (found!==false) {
                this.events[eventname].splice(found,1);
            }
        }
    },
   /**
    * sets or gets style
    * to make all similar functions to be built similar way?
    */
    style:function(key_name,key_value){
        if(arguments.length==2){
            try {
                this.setStyle(key_name,key_value);
            }
            catch(e){
                alert("wrong property:"+key_name+":"+key_value);
            }
        }
        else {
            if (typeof(key_name)=='object'){
                var this1 = this;
                z.each(key_name,function(key){
                    this1.style(key,key_name[key]);
                });
            } 
            else {
                return this.getStyle(key_name);
            }
        }
    },    
    setStyle:function(key_name,key_value){
        if (z.Browser.trident && (key_name=='opacity')){
            this.origin.style.filter = "alpha(opacity:"+(key_value*100)+")";
        }
        else if (key_name=='float'){
            if (!z.Browser.trident){
                this.origin.style['cssFloat'] = key_value;
            }
            else {
                this.origin.style['styleFloat'] = key_value;
            }
        }
        else {
            this.origin.style[key_name] = key_value;
        }
    },
   /*
    * returns style
    * TODO make it crossbrowser too?
    */
    getStyle:function(key_name){
        return this.origin.style[key_name];
    },
    
   /**
    * sets or retrieve property
    *
    */
    prop:function(key_name,key_value){
        if(arguments.length==2){
            this.setProp(key_name,key_value);
                
        }
        else {
            if (typeof(key_name)=='object'){
                var this1 = this;
                z.each(key_name,function(key){
                    this1.prop(key,key_name[key]);
                });
            } 
            else {
                return this.getProp(key_name);
            }
        }    
    },

   /**
    * sets property
    */
    setProp:function(key_name,key_value){
        try {
            this.origin[key_name] = key_value;        
        }
        catch(e){
            alert("wrong property:"+key_name+":"+key_value);
        }
    },
   /*
    * returns property
    * TODO make it crossbrowser too?
    */
    getProp:function(key_name){
        if (key_name=='text') return (this.origin['text'] || this.origin['innerText']); 
        return this.origin[key_name];
    },    
    //credits of prototype lib
    hasClass:function(className){
        return z.Element.hasClass(this.origin.className,className)
    },
    addClass:function(className){
        if (!this.hasClass(className)){
            this.origin.className = this.origin.className+' '+className;
        }
    },
    removeClass:function(className){
        this.origin.className = this.origin.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
    },
        
    putBefore:function(el){
        if (el.origin) el = el.origin //it's z.Element
        el.parentNode.insertBefore(this.origin,el);
    },
            
    putAfter:function(el){
        if (el.origin) el = el.origin //it's z.Element
        if (el.nextSibling){
            el.parentNode.insertBefore(this.origin,el.nextSibling);
        }
        else {
            el.parentNode.appendChild(this.origin);
        }
    },
    putInto:function(el){
        if (el.origin) el = el.origin //it's z.Element
        el.appendChild(this.origin);
    },
    //the same as ordinal appand child but argument can be also an array
    appendChild:function(el){
        if (el.origin) el = el.origin //it's z.Element
        if (el.constructor === Array) {
          var i,len = el.length;
          for(i=0;i<len;i++){
            this.appendChild(el[i]);
          }
          return;
        }
        this.origin.appendChild(el);
    },
    remove:function(el){
        if (el) {
            if (el.origin)  el = el.origin //it's z.Element
        }
        else el = this.origin;        
        el.parentNode.removeChild(el);
    }
});
 
z.Element.get = function (e){
 	if (e._x!==undefined) return e._x;
    //return new z.Element(e);
    return new this(e); //I cant believe its working!
};

z.Element.id = function(id){
    var e = document.getElementById(id);
    if (e){
       return this.get(e);
    }
    return null;
}

z.Element.hasClass = function(elementClassName,className){
    return (elementClassName.length > 0 && (elementClassName == className ||
            new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
}

z.Element.create = function(name,props,styles,children){
    var el = z.Element.get(document.createElement(name));
    if (props) el.prop(props);
    if (styles) el.style(styles);
    if (children) el.appendChild(children)
    return el;
};

/**
 * 
 */
z.Element.implement({
    getCoordinates:function(){
        function getPOL(obj){
            var x;
            x = obj.offsetLeft;
            if (obj.offsetParent != null)
            x += getPOL(obj.offsetParent);
            return x;       
        };
        function getPOT(obj){
            var y;
            y = obj.offsetTop;
            if (obj.offsetParent != null)
            y += getPOT(obj.offsetParent);
            return y;
        };
        return {top:getPOT(this.origin),left:getPOL(this.origin),width:this.origin.clientWidth,height:this.origin.clientHeight};
    }
});

/**
 * XHTML Selector
 */
 
(function(){
   /**
    * if element is complies with search it get applied to res
    *
    */
    function appendResult(search,el,res){//private function
        var fail = false;
        z.each(search, function(key){
            if (key=='depth') return;
            if (key=='tag' || key=='tagName') { //tag name is case insensitive
                if (el.tagName.toUpperCase()!=(search[key]).toUpperCase()){
                  fail = true;
                  return false;
                }
                return;
            }
            if (key=='class'){
                if (!z.Element.hasClass(el.className,search['class'])){
                  fail = true;
                  return false;
                }
                return;
            }
            if (!el[key]) {fail = true; return false;};
            if (el[key]!==search[key]) {fail = true; return false;};
        });
        if (fail) return;
        res.elements.push(z.Element.get(el));
    };
    
    function selectElement(el,search,res){
        var tagname = search['tagName'] || search['tag'];// || 'div'; //shorthands
        var depth = search['depth'] || -1; //-1 recursive, 1 - only first level childs, 2 frist level and second level and so on
        
        if ((tagname) && (depth===-1)){
            var els = el.getElementsByTagName(tagname);
            var i,els_l = els.length;        
            for(i=0;i<els_l;i++){
                appendResult(search,els[i],res);     
            }
        }
        else { //all elements
            var child;
            for(child = el.firstChild;child;child = child.nextSibling){
                if (child.nodeType!=1) continue; //need only elements
                appendResult(search,child,res);
                if (depth === -1) selectElement(child, search, res);
                else if (depth > 1) {search[depth] = depth--; selectElement(child,search,res)} 
            }
        }
    };
    
    z.Element.implement({
        select:function(search,result1){
            var res = result1 || (new z.CompoundElement());
            selectElement(this.origin,search,res);
            return res;
        },
        children:function(search,result1){
            search['depth'] = 1;
            return this.select(search,result1); 
        }    
    });
})();
 
z.select = function(search){
    return z.Document.select(search);
} 
 
z.CompoundElement = z.Class.extend({
    elements:[], //list of z.Element items
    construct:function(){
        this.elements = [];
    },
   /**
    * selects among all children
    */
    select:function(search, result){
        var result = result || new z.CompoundElement();
        var i,len = this.elements.length;
        for(i=0;i<len;i++){
            this.elements[i].select(search, result);
        }
        return result;
    },
   /**
    * selects among all children
    */
    children:function(search, result){
        search['depth'] = 1;
        return this.select(search,result1); 
    },    
   /**
    * call function for each element
    */
    each:function(fn){
        //var args = args || []
        var i,len = this.elements.length;
        for(i=0;i<len;i++){
            if (fn.apply(this.elements[i],[this.elements[i]])===false) 
                break;
        }
    },
   /**
    *
    */
    get:function(key){
        return this.elements[key];
    } 
}) 
 
/**
 * cross browser event object
 */

z.Event = z.Class.extend({
    origin:null,
    owner:null,
    construct:function(e){
        this.origin = e;
        e._x = this;
     
        this.shift = e.shiftKey,
        this.ctrl  = e.ctrlKey,
        this.alt   = e.altKey,
        this.meta  = e.metaKey
    },
    
   /**
    * get relative object coordinates
    */
    offset:function(){
        return {
            x: this.origin.offsetX,
            y: this.origin.offsetY 
        };
    },
   /**
    * get Page coordinates
    */
    page:function(){
        var doc = document;
            doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.documentElement : doc.body;
        
        return {
            x: this.origin.pageX || this.origin.clientX + doc.scrollLeft,
            y: this.origin.pageY || this.origin.clientY + doc.scrollTop
        };
    },
   /**
    * get key code
    */
    keyCode:function(){
        return this.origin.which || this.origin.keyCode;
    },
    
    keyCodes:{
        '13':'enter',
        '38':'up',
        '40':'down',
        '37':'left',
        '39':'right',
        '27':'esc',
        '32':'space',
        '8':'backspace',
        '9':'tab',
        '46':'delete'
    },
    
   /**
    *
    */
    key:function(){
        var code = this.keyCode();
        var key = this.keyCodes[code];
        if (this.origin.type == 'keydown'){
                var fKey = code - 111;
                if (fKey > 0 && fKey < 13) key = 'f' + fKey;
        }
        key = key || String.fromCharCode(code).toLowerCase();
        return key;    
    },
   /**
    * stops booble
    */
    stop:function(){
        if (this.origin.stopPropagation){
            this.origin.stopPropagation();
        }
        else {
            this.origin.cancelBubble = true;//ie
        }
    },
   /**
    * cancels default behaviour
    */
    cancel:function(){
        if (this.origin.stopPropagation){
            this.origin.preventDefault();
        }
        else {
            this.origin.returnValue = false;//ie
        }
    },
    halt:function(){
        this.stop();
        this.cancel();
    }
});

z.Event.get = function(e){
  if (e._x) return e._x;
  return new z.Event(e);
}

 
/**
 * Browser detection
 *
 */
z.Browser = {	
	platform: (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase(),
	version: (navigator.userAgent.toLowerCase().match(/.+(?:rv|it|ra|ie|me)[\/: ]([\d.]+)/) || [-1,-1])[1]
};

if (window.opera) z.Browser.presto = true;  // Opera
else if (window.ActiveXObject) z.Browser.trident = true;  // EI
else if (!navigator.taintEnabled) z.Browser.webkit = true;  // Safari
else if (document.getBoxObjectFor != null) z.Browser.gecko = true; //Firefox

/**
 * domready event
 */
z.Document = {
    isReady:false,
    queue:[],    
    bind:function(){
		// Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
		// If IE is used and is not in a frame
		// Continually check to see if the document is ready
		if ( z.Browser.trident && window == top ) (function(){
			if (this.isReady) return;
			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}
			// and execute any waiting functions
			z.Document.invokeQueue();
		})();
		if ( z.Browser.presto || z.Browser.webkit ) {
			var numStyles;
			(function(){
				if (this.isReady) return;
				if ( document.readyState != "loaded" && document.readyState != "complete" ) {
					setTimeout( arguments.callee, 0 );
					return;
				}
				z.Document.invokeQueue();
			})();
			return;
		}
		if (document.addEventListener){
  			document.addEventListener( "DOMContentLoaded", function(){z.Document.invokeQueue()}, false );
		}
    },
    invokeQueue:function(){
    	this.isReady = true;
      	var i;
      	for (i=0;i<this.queue.length;i++){
        	var f = this.queue[i];
        	f();
      	}
    },
    ready:function(f){
		if (this.isReady) {
   	 		f();
    	}
    	else {
	  		this.queue.push(f);
    	}
  	}
}
z.copy(z.Document,new z.Element(document));
z.Document.bind();

/**
 * debugger
 */
z.console = {
  log:function(msg){
    if (typeof(console)!=='undefined'){
        console.log("%s",msg);
    } 
    else {
        Debugger.trace(msg);
    }
  }
}

z.log = function(m){
  z.console.log(m);
};

/**
 * inputs
 * thanks to prototype
 */
z.Form = z.Element.extend({
    getValues:function(){
        //var objForm;
        var submitDisabledElements=false;
        if(arguments.length > 1 && arguments[1]==true)
            submitDisabledElements=true;var prefix="";
        if(arguments.length > 2)
            prefix=arguments[2];
        var values = {};
            var formElements=this.origin.elements;
            for(var i=0;i < formElements.length;i++){
                if(!formElements[i].name) continue;
                if(formElements[i].name.substring(0,prefix.length)!=prefix) continue;
                if(formElements[i].type && (formElements[i].type=='radio' 
                || formElements[i].type=='checkbox') && formElements[i].checked==false) continue;
                if(formElements[i].disabled && formElements[i].disabled==true && submitDisabledElements==false) continue;
                var name=formElements[i].name;
                if(name){
                    values[name] = formElements[i].value;
                }
            }
        //}
        return values;
    }
});

z.Form.get = function (e){
    if (e._x!==undefined) return e._x;
    return new z.Form(e);
};

/**
 * Delayer function execution with delay
 * autostart defaultvalue is = true
 */

z.delay = function(fn,timeout,autostart,repeat){
    var d = new z.Delayer(fn, timeout,(autostart===false?false:true), repeat);
    return d;
};

z.Delayer = z.Class.extend({
    fn:null,
    delay:null,
    repeat:false,
    timeout:null,
    construct:function(fn, delay, autostart, repeat){
        this.fn = fn;
        this.delay = delay;
        this.repeat = repeat;
        if (autostart) this.call();
    },
    start:function(){
        this.stop();
        this.call();
    },
    call:function(){
        var this1 = this;
        this.timeout = window.setTimeout(function(){
            if (this1.repeat && (this1.timeout!==null)){ //if timeout==null that means amimation was stoppend
                this1.call();
            }
            else {
                this1.timeout = null;
            }
            this1.fn();
        },this.delay);
    },
    stop:function(){
        if (this.timeout){
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
    }
});

/**
 * Animation
 */

z.Animators = {};
 
/**
 * values should be two items array - first and last points
 */
z.Animators.Line = {
  calculate:function(values,value_name,data,duration,rate,count_steps){
    var i,v=values[0],dx = (values[1]-values[0])/(count_steps-1);
    for(i=0;i<count_steps;i++){
        data[i][value_name] = v;
        v = v+dx;
    }
  }
};

z.Animators.Ease = {
  calculate:function(values,value_name,data,duration,rate,count_steps){
  
  }
};
 
z.Animation = z.Class.extend({
    onBegin:null,
    onComplete:null,    
    onStep:null,
    
    animation:null,
    duration:300,
    rate:12, //count of frames per second
    step_number:0,
    count_steps:-1,
    //timeout:null,
    delayer:null,
    data:null, //array of animation data
    animator:z.Animators.Line, //default anumator
    construct:function(props){
      z.copy(this,props);
      this.data = [];
      this.count_steps = this.duration*this.rate/1000;
      var i;
      for(i=0;i<this.count_steps;i++){
        this.data[i] = {};
      }      
      var this1 = this;
      z.each(this.animation,function(key){
        this1.animator.calculate(this1.animation[key], key,this1.data, this1.duration, this1.rate, this1.count_steps);
      });
      this.delayer = z.delay(function(){this1.doStep()},1000/this.rate,false,true);
    },
    start:function(){
      this.stop(); 
      this.step_number = 0;
      this.delayer.start();
    },
    stop:function(){
      this.delayer.stop();
    },
    doStep:function(){
        var data = this.data[this.step_number];
        if (this.step_number==0 && this.onBegin) this.onBegin(data);       
        this.onStep(data);      
        if (this.step_number>=this.count_steps-1) {
            if (this.onComplete) this.onComplete(data);
            this.delayer.stop();
        }
        this.step_number++;
    }
});

//TODO fix language selector
/*
z.Document.ready(function(){
  z.select({'className':'dd'}).select({tag:'a'}).click(function(e){
  	showDropDown(this.p('rel'),this.origin);
  	e.halt();
  });

  
  z.select({'className':'dd'}).select({tag:'span'}).click(function(e){
    var parent = z(this.p('parentNode'));
    var a = parent.select({tag:'a'});
    
    a = a.get(0).origin;
    
  	showDropDown(a.rel,a);
  	e.halt();
  });  
  
  z.select({'className':'dd'}).click(function(){
    var a = this.origin.firstChild;
    showDropDown(a.rel,a);
  });
  
  z.Document.click(function(){
    if (showDropDown.dd){
      z(showDropDown.dd).addClass('invisible');
      showDropDown.dd = null;
    }
  });  
  
  
  //z('lang_dd').select({'tag':'a'}).click(function(e){
  //  ajaxSetLanguage(this.p('rel'));
  //});
});

*/

function showDropDown(dd_id,a){
  var d = z(dd_id).origin;
  
  if (showDropDown.dd){
    if (showDropDown.dd==d) return;
    z(showDropDown.dd).addClass('invisible');
  }
  
  showDropDown.dd = d;
  
  z(d).removeClass('invisible');
  c = z(a).getCoordinates();
  
  //alert(a.clientHeight);
  z(d).css('left',c.left+'px');
  z(d).css('top',(a.offsetHeight+c.top)+'px');
}

var Ajax = {
  server:'',
  debug:true,
  requests:{},
  requests_num:0,
  callFunction:function(){
	var r = new AjaxRequest(); //this.getTransport();
	r.callFunction(arguments);
  },
  $:function(id){
    return document.getElementById(id);
  },
  callFunctionArg:function(func_name,args){
	var r = new AjaxRequest(); //this.getTransport();
	r.callFunctionArg(func_name,args);
  },  
  toJSONString:function(value){
  		if (value==null) return 'null';
        switch (typeof value) {
        case 'object':
        	if (value.constructor === Array){
				return this.arrayToJSONString(value);
			}
			else {
				return this.objectToJSONString(value);
			}
        case 'string':
           	return this.stringToJSONString(value);
        case 'number':
           	return this.numberToJSONString(value);
        case 'boolean':
           	return this.booleanToJSONString(value);
        }  
  },
  arrayToJSONString:function(value) {
        var a = [], i,          // Loop counter.
            l = value.length,
            v;          // The value to be stringified.
        for (i = 0; i < l; i += 1) {
            v = value[i];
            a.push(this.toJSONString(v));
        }
        return '[' + a.join(',') + ']';
  },
  booleanToJSONString:function (value) {
        return String(value);
  },
  dateToJSONString:function (value) {
        function f(n) {
            return n < 10 ? '0' + n : n;
        }
        return '"' + value.getUTCFullYear() + '-' +
                f(value.getUTCMonth() + 1) + '-' +
                f(value.getUTCDate()) + 'T' +
                f(value.getUTCHours()) + ':' +
                f(value.getUTCMinutes()) + ':' +
                f(value.getUTCSeconds()) + 'Z"';
  },
  numberToJSONString:function (value) {
        return isFinite(value) ? String(value) : 'null';
  },  
  objectToJSONString:function (value) {
        var a = [],     // The array holding the partial texts.
            k,          // The current key.
            v;          // The current value.
        if (value==null) return 'null';
        for (k in value) {
            if (typeof k === 'string' && Object.prototype.hasOwnProperty.apply(value, [k])) {
                v = value[k];
                switch (typeof v) {
                case 'object':
                	if (v.constructor === Array){
						a.push(this.stringToJSONString(k) + ':' + this.arrayToJSONString(v));
					}
					else {
						a.push(this.stringToJSONString(k) + ':' + this.objectToJSONString(v));
					}
				break;
                case 'string':
                	a.push(this.stringToJSONString(k) + ':' + this.stringToJSONString(v));
                break;
                case 'number':
                	a.push(this.stringToJSONString(k) + ':' + this.numberToJSONString(v));
                break;
                case 'boolean':
                    a.push(this.stringToJSONString(k) + ':' + this.booleanToJSONString(v));
                break;
                }
            }
        }
        return '{' + a.join(',') + '}';
  },  
  stringToJSONString:function (value) {
        var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };
        if (/["\\\x00-\x1f]/.test(value)) {
                return '"' + value.replace(/[\x00-\x1f\\"]/g, function (a) {
                    var c = m[a];
                    if (c) {
                        return c;
                    }
                    c = a.charCodeAt();
                    return '\\u00' +
                        Math.floor(c / 16).toString(16) +
                        (c % 16).toString(16);
                }) + '"';
            }
            return '"' + value + '"';
  },
  parseJSON:function (value,filter) {
    var j;
    function walk(k, v) {
                var i;
                if (v && typeof v === 'object') {
                    for (i in v) {
                        if (Object.prototype.hasOwnProperty.apply(v, [i])) {
                            v[i] = walk(i, v[i]);
                        }
                    }
                }
                return filter(k, v);
    }
    if (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(value.
                    replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) {
                j = eval('(' + value + ')');
                return typeof filter === 'function' ? walk('', j) : j;
    }
    else {
    	if (this.debug){
    		alert(value);
    	}
    	throw new SyntaxError('parseJSON');
    }
  },
  renderResponses:function(responses){
  	for(var i=0;i<responses.length;i++){
  		var response = responses[i];
  		this.renderResponse(response);
  	}
  },
  renderResponse:function(response){
    //alert(this.toJSONString(response));
    if (response.c=='cm'){ //call method
      var params = [];
      for(var i=0;i<response.a.length;i++){
        params[i] = "response.a["+i+"]";
      }
      var s = response.o+"."+response.m+"("+params.join(",")+")";
      eval(s);
    }
    if (response.c=='sc'){ //script call
      var params = [];
      for(var i=0;i<response.a.length;i++){
        params[i] = "response.a["+i+"]";
      }
      var s = response.f+"("+params.join(",")+")";
      eval(s);
    }    
    else if (response.c=='a'){ //call method
      alert(response.m);
    }
	else if (response.c=='e'){ //eval script
      eval(response.s);
    }
	else if (response.c=='s'){ //aSsign
      var obj = this.$(response.e);
      if (obj) obj[response.p] = response.v;
    }         
  },
  getTransport:function(){
  		try {
			return new ActiveXObject("Microsoft.XMLHTTP");
		} 
		catch (e) {}
		try {
			return new XMLHttpRequest();
		} 
		catch (e) {}
		
  },
  getFormValues:function (frm){
	var objForm;
	var submitDisabledElements=false;
	if(arguments.length > 1 && arguments[1]==true)
		submitDisabledElements=true;var prefix="";
	if(arguments.length > 2)
		prefix=arguments[2];
	if(typeof(frm)=="string") objForm=$(frm);
	else objForm=frm;
	var values = {};
	if(objForm && objForm.tagName.toUpperCase()=='FORM'){
		var formElements=objForm.elements;
		for(var i=0;i < formElements.length;i++){
			if(!formElements[i].name) continue;
			if(formElements[i].name.substring(0,prefix.length)!=prefix) continue;
			if(formElements[i].type && (formElements[i].type=='radio' 
			|| formElements[i].type=='checkbox') && formElements[i].checked==false) continue;
			if(formElements[i].disabled && formElements[i].disabled==true && submitDisabledElements==false) continue;
			var name=formElements[i].name;
			if(name){
				values[name] = formElements[i].value;
			}
		}
	}
	return values;
  }
  
}


function AjaxRequest(){
	this.xmlhttp = Ajax.getTransport();
}
AjaxRequest.prototype.callFunction = function(args){
	var func = args[0];
	var el_argx = [];
	for(var i=1;i<args.length;i++){
	  el_argx.push(args[i]); 
	}
	var request = {'f':func,'a':el_argx};
	var json_request = Ajax.toJSONString(request);
	this.xmlhttp.open('post',Ajax.server,true);
	this.xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	this.xmlhttp.send('r='+encodeURIComponent(json_request));
	var this1 = this;
	this.xmlhttp.onreadystatechange = function(){
	  if (this1.xmlhttp.readyState==4){
	  	var responses = Ajax.parseJSON(this1.xmlhttp.responseText);
	  	Ajax.renderResponses(responses);
	  }
	}
}
AjaxRequest.prototype.callFunctionArg = function(func_name,args){
	var el_argx = [];
	for(var i=0;i<args.length;i++){
	  el_argx.push(args[i]); 
	}
	var request = {'f':func_name,'a':el_argx};
	var json_request = Ajax.toJSONString(request);
	this.xmlhttp.open('post',Ajax.server,true);
	this.xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	this.xmlhttp.send('r='+encodeURIComponent(json_request));
	var this1 = this;
	this.xmlhttp.onreadystatechange = function(){
	  if (this1.xmlhttp.readyState==4){
	  	var responses = Ajax.parseJSON(this1.xmlhttp.responseText);
	  	Ajax.renderResponses(responses);
	  }
	}
}


