/**
 * 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
  */
  
 /**
	 * static class with some useful methods "nest" for all other classes
	 */
  
  
  
function z(s,defaults){ //
  if (typeof(s)==='string'){
    return z.id(s,defaults);
  }
  else {
    return z.get(s,defaults);
  }
}
  

/**
 * returns Element if element is exists
 */
z.id = function(id,defaults){
     return z.Element.id(id,defaults);
};
   
z.get = function(el,defaults){
     return z.Element.get(el,defaults);
};
   
z.create = function(name,atr,stl,children){
  	 return z.Element.create(name,atr,stl,children);
};

/**
 * copy all properties except prototype
 */
z.copy = function(dst,src){
      for(key in src){
        if (key!=='prototype'){
          dst[key] = src[key];
        }
      }
};

z.ccopy = function(dst,src){
    for(key in src){
      if (key!=='prototype'){
       	  if ((typeof(dst[key])=='function') && typeof(src[key])=='function'){
       		 var back = dst[key];
       		 dst[key] = src[key]
       		 dst[key].base = back;        
          }
          else {
        	 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.Class.prototype.base = function(){
	return arguments.callee.caller.base.apply(this,arguments);
}
z.Class.extend = function(obj){
	var this1 = this;
    var obj1 = function(){
    	var c = obj.construct || this1.prototype.construct || z.Class;
    	c.apply(this,arguments);
    }
    z.copy(obj1.prototype,this.prototype); // copy inherited properties
    z.ccopy(obj1.prototype,obj); // add new properties
    z.ccopy(obj1,this); // copy static properties
    return obj1;
}

z.Class.implement = function(obj){
    z.ccopy(this.prototype,obj);
}

z.Class.stat = function(obj){
	z.ccopy(this,obj);
}   
 
 
/**
 * cross browser manitulations with element properties, events, styles
 */
z.Element = z.Class.extend({
    dom:null,
    events:null,
    construct:function(el,defaults){
        el[this.constructor.zname] = this;
        z.copy(this, defaults);
        this.events = {};
        this.dom = el;        
        // this.css = this.s = this.style; //TODO move to design time
        // this.p = this.prop;
    },
       
   /**
	 * 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.dom.addEventListener){
                	this.dom.addEventListener(eventname, function(e){t1.callEvent(eventname,e)},false);
            	}
            	else {
                	this.dom.attachEvent('on'+eventname, function(e){t1.callEvent(eventname,e)});
            	}
        	};
        	this.events[eventname].push(func);
        	return this;
    },         
   
    callEvent:function(eventname,e){
        var hooks;
        e = e?z.Event.get(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]);
     	    }
        }
    },    
    
    fireEvent:function(eventname,e){
    	e = e || new z.Event();
    	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 ((key_name=='opacity')){
            if (z.Browser.Engine.trident){
                this.dom.style.filter = "alpha(opacity:"+(key_value*100)+")";
            }
            else {
                this.dom.style[key_name] = key_value;
            }
        }
        else if((key_name=='float') || (key_name=='cssFloat') || (key_name=='styleFloat')){
            if (z.Browser.Engine.trident){
                this.dom.style.styleFloat = key_value;
            }
            else {
                this.dom.style.cssFloat = key_value;
            }
        }
        else {
            this.dom.style[key_name] = key_value;
        }
    },
   /*
	 * returns style TODO make it crossbrowser too?
	 */
    getStyle:function(key_name){
        return this.dom.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.dom[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.dom['text'] || this.dom['innerText']); 
        return this.dom[key_name];
    },    
    // credits of prototype lib
    hasClass:function(className){
        return z.Element.hasClass(this.dom.className,className)
    },
    addClass:function(className){
        if (!this.hasClass(className)){
            this.dom.className = this.dom.className+' '+className;
        }
    },
    removeClass:function(className){
        this.dom.className = this.dom.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
    },
        
    putBefore:function(el){
        if (el.dom) el = el.dom // it's z.Element
        el.parentNode.insertBefore(this.dom,el);
    },
            
    putAfter:function(el){
        if (el.dom) el = el.dom // it's z.Element
        if (el.nextSibling){
            el.parentNode.insertBefore(this.dom,el.nextSibling);
        }
        else {
            el.parentNode.appendChild(this.dom);
        }
    },
    putInto:function(el){
        if (el.dom) el = el.dom // it's z.Element
        el.appendChild(this.dom);
    },
    // the same as ordinal appand child but argument can be also an array
    appendChild:function(el){
        if (el.dom) el = el.dom // 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.dom.appendChild(el);
    },
    remove:function(el){
        if (el) {
            if (el.dom)  el = el.dom // it's z.Element
        }
        else el = this.dom;        
        el.parentNode.removeChild(el);
    },
    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.dom),left:getPOL(this.dom),width:this.dom.offsetWidth,height:this.dom.offsetHeight};
    }    
});

z.Element.stat({
	zname:'_z',
    shortEvents:function(names){
        var events = {}
        z.each(names,
	    function(key,event){
	    	events[event] = new Function("fn","remove","return this.on('"+[event]+"',fn,remove)");
	    }
        )
        this.implement(events);
    },
    
	get:function (e,defaults){
 	    if (e[this.zname]!==undefined) return e[this.zname];
        return new this(e,defaults);
    },

    id:function(id,defaults){
        var e = document.getElementById(id);
        if (e){
            return this.get(e,defaults);
        }
        return null;
    },

    hasClass:function(elementClassName,className){
        return (elementClassName.length > 0 && (elementClassName == className ||
            new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    },

    create:function(name,props,styles,children,defaults){
        var el = new this(document.createElement(name),defaults);
	    if (props) el.prop(props);
        if (styles) el.style(styles);
        if (children) el.appendChild(children)
        return el;
    },
    
    synonyms:function(name,names){
    	for(var i=0;i<names.length;i++){
    		this.prototype[names[i]] = this.prototype[name];
    	}
    }
});


z.Element.shortEvents(['click','mousedown','mouseup','mousemove','mouseover','mouseout','focus','blur','change','keypress','keydown','keyup']);
z.Element.synonyms('prop',['p']);
z.Element.synonyms('style',['s','css']);
 
/**
 * XHTML Selector
 */
 
(function(){
	function hasClass(className,classNames){
	    if (typeof(classNames.push)=='function'){
		    for(var i=0;i<classNames.length;i++) if (hasClass(className,classNames[i])) return true;
		    return false;
	    }
	    else {
	    	return z.Element.hasClass(className,classNames);
	    }
	}
	
	function isTagName(tagName,tagNames){
		if (typeof(tagNames.push)=='function'){
		    for(var i=0;i<tagNames.length;i++) if (isTagName(tagName,tagNames[i])) return true;
		    return false;
	    }
	    else { 
	    	return (tagName.toUpperCase() == tagNames.toUpperCase());
	    }
	}
	
	function hasProperty(el,key,value){
		if (typeof(value.push)=='function'){
			for(var i=0;i<value.length;i++) if (hasProperty(el,key,value[i])) return true;
		    return false;
		}
		else {
			if (!el[key]) return false;
			if (el[key]!==value) return false;
			return true;
		}
	}
	
   /**
	 * if element is complies with search it get applied to res
	 * 
	 */
    function appendResult(search,el,res,constr,defaults){// private function
        var fail = false;
        z.each(search, function(key){
            if (key=='depth') return;
            if (key=='_t' || key=='tagName' || key=='tag') { // tag name is
																// case
																// insensitive
                if (!isTagName(el.tagName,search[key])){
                  fail = true;
                  return false;
                }
                return;
            }
            if (key=='_c' || key=='class'){
                if (!hasClass(el.className,search[key])){
                  fail = true;
                  return false;
                }
                return;
            }
            if (!hasProperty(el,key,search[key])){
            	fail = true; return false;
            }
        });
        if (fail) return;
        res.elements.push(constr.get(el,defaults));
        
    };
    
    function selectElement(el, search, res, constr, defaults){
    	constr = constr || z.Element;
    	defaults = defaults || {};
        var tagname = search['tagName'] || search['_t'] || search['tag'];
        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 num = 0;
        	var tn = tagname;
        	// alert(typeof(tagname.push));
        	while(((typeof(tagname.push)=='function') && (tn=tagname[num++])) || tn){
        		var els = el.getElementsByTagName(tn);
        		tn = false; // to stop cycle
        		var els_l = els.length;        
        		for(var i=0;i<els_l;i++){
        			appendResult(search,els[i],res,constr,defaults);     
        		}
        	}
        }
        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,constr,defaults);
                if (depth === -1) selectElement(child, search, res, constr);
                else if (depth > 1) {search[depth] = depth--; selectElement(child, search, res, constr,defaults)} 
            }
        }
    };
    
    z.Element.implement({
        select:function(search,result,constr,defaults){
            var res = result || (new z.CompoundElement());
            selectElement(this.dom,search,res,constr,defaults);
            return res;
        },
        children:function(search,result,constr,defaults){
            search['depth'] = 1;
            return this.select(search,result,constr,defaults); 
        }    
    });
    
    z.Element.select = function(search,defaults){
    	return z.document.select(search,null,this,defaults); //
    }
    
})();
 
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, cons, defaults){
        var result = result || new z.CompoundElement();
        var i,len = this.elements.length;
        for(i=0;i<len;i++){
            this.elements[i].select(search, result, cons, defaults);
        }
        return result;
    },
   /**
	 * selects among all children
	 */
    children:function(search, result, cons, defaults){
        search['depth'] = 1;
        return this.select(search,result1,cons, defaults); 
    },    
   /**
	 * 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];
    } 
}); 
/**
 * 
 */ 
(function(){
    var events = {
		on:function(eventname,fn,remove){
			this.each(function(el){
				el.on(eventname,fn,remove);
			})
			return this;
		},		
		addClass:function(className){
		    this.each(function(el){
                el.addClass(className);
            })
		},
	    removeClass:function(className){
            this.each(function(el){
                el.removeClass(className);
            })
        }		        
    }
	z.each(['click','mousedown','mouseup','mousemove','mouseover','mouseout','focus','blur','change','keypress','keydown','keyup'],
	    function(key,event){
	    	events[event] = new Function("fn","remove","return this.on('"+[event]+"',fn,remove)");
	    }
	)
	z.CompoundElement.implement(events);
})();


 
/**
 * cross browser event object
 */

z.Event = z.Class.extend({
    dom:null,
    owner:null,
    construct:function(e){
		with (this){
			this.dom = e;
			if (!e) return;
			e._z = this;
			this.shift = e.shiftKey;
			this.ctrl  = e.ctrlKey;
			this.alt   = e.altKey;
			this.meta  = e.metaKey;
			this.type  = e.type;
        
			var doc = document;
			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.documentElement : doc.body;
			this.page = {};
			this.page.x = dom.pageX || dom.clientX + doc.scrollLeft;
			this.page.y = dom.pageY || dom.clientY + doc.scrollTop;
        
			this.client = {};
			this.client.x = dom.layerX || dom.offsetX;
			this.client.y = dom.layerY || dom.offsetY;
        
			this.code = dom.which || dom.keyCode; 
			if (code > 111 && code < 124) this.key = 'f' + (code-111);
			this.key = this.key || keyCodes[code] || String.fromCharCode(code).toLowerCase();
		}
    },
   
    
    keyCodes:{
        '13':'enter',
        '38':'up',
        '40':'down',
        '37':'left',
        '39':'right',
        '27':'esc',
        '32':'space',
        '8':'backspace',
        '9':'tab',
        '46':'delete'
    },
    

   /**
	 * stops booble
	 */
    stop:function(){
        if (this.dom.stopPropagation){
            this.dom.stopPropagation();
        }
        else {
            this.dom.cancelBubble = true;// ie
        }
    },
   /**
	 * cancels default behaviour
	 */
    cancel:function(){
        if (this.dom.stopPropagation){
            this.dom.preventDefault();
        }
        else {
            this.dom.returnValue = false;// ie
        }
    },
    halt:function(){
        this.stop();
        this.cancel();
    }
});

z.Event.get = function(e){
  if (e._z) return e._z;
  return new z.Event(e);
}

 
/**
 * Browser detection
 * 
 */
z.Browser = {
	Engine: {name: 'unknown', version: ''},
	Platform: {name: (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()}
};

if (window.opera) z.Browser.Engine = {name: 'presto', version: (document.getElementsByClassName) ? 950 : 925};
else if (window.ActiveXObject) z.Browser.Engine = {name: 'trident', version: (window.XMLHttpRequest) ? 5 : 4};
else if (!navigator.taintEnabled) z.Browser.Engine = {name: 'webkit', version: (!!(document.evaluate)) ? 420 : 419};
else if (document.getBoxObjectFor != null) z.Browser.Engine = {name: 'gecko', version: (document.getElementsByClassName) ? 19 : 18};
z.Browser.Engine[z.Browser.Engine.name] = z.Browser.Engine[z.Browser.Engine.name + z.Browser.Engine.version] = true;
if (window.orientation != undefined) z.Browser.Platform.name = 'ipod';
z.Browser.Platform[z.Browser.Platform.name] = true;

/**
 * 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.Engine.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.Engine.presto || z.Browser.Engine.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.Logger = {
  panel:null,
  log:function(m){
    if(!this.panel){
    	this.panel = z.Element.create('DIV',{},{'position':'absolute','background':'white','overflow':'scroll','width':'300px','height':'100px','top':'10px','left':'10px'});
    	this.panel.putInto(document.body);
    }
    this.panel.dom.innerHTML = ''+m+'<br>'+this.panel.dom.innerHTML;
  }
}

z.console = {
  log:function(msg){
    if (typeof(console)!=='undefined'){
        console.log("%s",msg);
    } 
    else {
        z.Logger.log(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.dom.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.zname = '_zf';
z.Form.shortEvents(['submit','reset']); // onsubmit event


/**
 * 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(){
        	this1.callTimeout();
        },this.delay);
    },
    callTimeout:function(){    
        this.fn();
        if (this.repeat && (this.timeout!==null)){ // if timeout==null that
													// means amimation was
													// stoppend
            this.call();
        }
        else {
            this.timeout = null;
        }
    },
    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({
    onStart:function(){},
    onStop:function(){},    
    onStep:function(){},
    
    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 = Math.ceil(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];
        this.onStep(data);
        
        if (this.step_number==0) this.onStart();                     
        if (this.step_number>=this.count_steps-1) {
            this.onStop();
            this.delayer.stop();
        }
        this.step_number++;
    }
});


z.ImageCache = {
	list:[],
	add:function(url){
	    var v = new Image();
		    v.src = url;
		this.list.push(v);
	}
}

/**
 * json.js - JSON functions
 *
 * @depends base.js
 * @example examples/core/empty
 * @author Maxim Starikov
 */
 
z.json = {
  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');
    }
  }
}


/**
 * ajax.js - ajax functions
 * 
 * @depends json.js, base.js
 */
z.Ajax = z.Class.extend({
  server:'',
  debug:true,
  requests:{},
  requests_num:0,
  callFunction:function(){
	var r = new z.AjaxRequest(this.server); // this.getTransport();
	r.callFunction(arguments);
  },
  
  callFunctionArg:function(func_name,args){
	var r = new z.AjaxRequest(this.server); // this.getTransport();
	r.callFunctionArg(func_name,args);
  },   
  initForm:function(form_id){
  	  var this1 = this; 
	  z.document.ready(function(){
		  var form = z(form_id);
		  if (form){
			  this1.setupForm(form,form_id);
		  }
	  });
  },
  setupForm:function(form,form_id,server){
	  var name = '_ajax_'+Math.ceil(Math.random()*10000);
	  // add additional field
	  var value = z.json.toJSONString({'o':form_id});
	  var input = z.Element.create('INPUT',{'type':'hidden','name':'r','value':value})
	  input.putInto(form);
	  form.dom.action = this.server;
	  form.dom.target = name;
	  // create iframe
	  if (z.Browser.trident){
		  var tagname = '<iframe name="'+name+'">';
	  }
	  else {
		  var tagname = 'IFRAME';
	  }
	  var iframe = z.Element.create('IFRAME',{'name':name},{'width':'0px','height':'0px','position':'absolute','top':'0px','left':'-1000px'});
	  iframe.putInto(document.body);	  
  	}
});

z.Ajax.stat({
  	renderResponses:function(responses){
    	for(var i=0;i<responses.length;i++){
    		var response = responses[i];
    		this.renderResponse(response);
    	}
  	},
	
	renderResponse:function(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 = document.getElementById(response.e);
			if (obj) obj[response.p] = response.v;
		}         
	}	
});

z.AjaxRequest = z.Class.extend({
	server:'',
	construct:function(server){
		this.xmlhttp = this.getTransport();
		this.server = server;
	},
	getTransport:function(){
  		try {
			return new ActiveXObject("Microsoft.XMLHTTP");
		} 
		catch (e) {}
		try {
			return new XMLHttpRequest();
		} 
		catch (e) {}		
    },

	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 = z.json.toJSONString(request);
		this.xmlhttp.open('post',this.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 = z.json.parseJSON(this1.xmlhttp.responseText);
				z.Ajax.renderResponses(responses);
			}
		}
	},
	
	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 = z.json.toJSONString(request);
		this.xmlhttp.open('post',this.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 = z.json.parseJSON(this1.xmlhttp.responseText);
				z.Ajax.renderResponses(responses);
			}
		}
	}
});



