/*
 * Copyright 2008 Ricky Kong
 * MYJS Javascript toolkits
 */
/*|---------------------------------------------------------------------------*/
//Basic functions and variables for the JAVASCRIPT toolkit 
var UA = navigator.userAgent;
window.$MYJS={
	version: '0.0.1',
	isDebug: true,
	debug : function(info){
		if(this.isDebug) alert(info);
	},
	nil:function(){}
};
var Browser = {
	Opera:!!window.opera,
	IE:UA.indexOf("MSIE")>=0,
	IE6:UA.indexOf("MSIE 6")>=0,
	IE7:UA.indexOf("MSIE 7")>=0,
	Safari:UA.indexOf("Safari/")>=0,
	FF:UA.indexOf("Firefox")>=0,
	FF1:UA.indexOf("Firefox/1")>=0,
	FF2:UA.indexOf("Firefox/2")>=0,
	FF3:UA.indexOf("Firefox/3")>=0,
	Gecko:UA.indexOf("Gecko/")>=0
};
function newClass(){
	return function(){if(this.init) this.init.apply(this,arguments);}
}
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * Objects is a class defined many common functions on Object.
 * Author:Kong Kaiping [Ricky Kong]
 * 
 * Note:$object is a instance of Objects for user easy usage.mostly user has no
 * need to new a Objects but use the object.
 * 
 * Usage:
 **/
var Objects=newClass();
Objects.prototype={
	freeId:1,

	getID : function(obj){
		return  this.getAttr(obj,'/uniqueId/')||
 			this.setAttr(obj,'/uniqueId/',this.freeId++);
	},

	getAttr : function(obj, name, dft){
		var n = '_MYJS_' + name;
		return  n in obj ? obj[n] :
			dft === undefined ? dft :
 			this.setAttr(
				obj,
				name,
				typeof(def) == 'function' ? dft() : dft
			);
	},

	setAttr : function(obj, name, val){
		return obj['_MYJS_'+name]=val;
	},

	delAttr : function(obj, name){
		try{ delete obj['_MYJS_'+name]}catch(e){this.setAttr(obj, name, undefined);};
	},

	copyAttr : function(tObj, fObj, pick){
		for(var v in fObj)
			if(!pick || pick(v, tObj, fObj))
				tObj[v] = fObj[v];
	},

	toStr : function(obj){
		var atts=[], type=typeof(obj);
		if(obj==null || type!='object')
			return type == 'string' ? '"' + obj + '"' : '' + obj;
		else
			for(var n in obj){
				try{
					atts.push(n + ':' + obj[n]);
				}catch(e){$MYJS.debug(e);}
			}
		return '{' + atts.join(',') + '}';
	}
};
var $object = new Objects();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
var Strings = newClass();
Strings.prototype={
	trim : function(str){
		return str.replace(/^\s*|\s*$/g,'');
	},

	isEndWith : function(str, endStr){
		var p = str.length - endStr.length;
		return str.indexOf(endStr, p)==p;
	}
};
$string = new Strings();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * Arrays is a class defined many common algorithms on array.
 * Author:Kong Kaiping [Ricky Kong]
 * 
 * Note:$array is a instance of Arrays for user easy usage.mostly user has no
 * need to new a Arrays but use the object.
 * 
 * Usage:
 **/
var Arrays = newClass();
Arrays.prototype={
	each : function(arr, handle, verify){
		for(var i=0, len = arr.length; i < len; i++)
			if((!verify || verify(arr[i])) && handle)
				handle(arr[i], i);
	},

	isArray : function(v){
		return v.constructor==window.Array || v.constructor==[].constructor;
	},

	copy : function(set, deep){
		var arr = [], len = set.length;

		for(var i = 0; i < len; i++)
			arr[i] = (this.isArray(set[i]) && deep) ? this.copy(set[i], true) : set[i];

		return arr;
	},

	ensureArray : function(arr){
		return this.isArray(arr)||(arr!=window&&!arr.tagName&&typeof(arr.length)=='number'&&arr.iNotArray!=true)?arr:[arr];
	},

	compare : function(o1, o2){
		return o1<o2?-1:o1==o2?0:1;
	},

	equal : function(o1, o2){
		return o1===o2;
	},

	index : function(arr, e, eq){
		if(!eq)eq=this.equal;
		for(var i=0, len=arr.length; i<len; i++)
			if(eq(arr[i], e))	return i;
		return -1;
	},

	lastIndex : function(arr, e, eq){
		if(!eq)eq=this.equal;
		for(var i=arr.length; --i>=0; )
			if(eq(arr[i], e))	return i;
		return -1;
	},

	indexAll : function(arr, e, eq){
		var ret=[];
		eq=eq?eq:this.equal;
		this.each(arr,function(e1,i){if(eq(e1,e))ret.push(i);});
		return ret;
	},

	del : function(arr, e, eq){
		eq=eq?eq:this.equal;
		this.each(arr,function(e1,i){if(eq(e1,e))arr.splice(i,1);});
		return arr;
	},

	bisearch : function(arr, e, compare){
		if(!compare) compare=this.compare;
		for(var i=0,j=arr.length,p,comp;i<=j;){
			p = Math.floor((i+j)/2);
			comp = compare(e, arr[p]);
			if(comp<0)
				j=p-1;
			else if(comp>0)
				i=p+1;
			else
				return p;
		}
		return -(i+1);
	}
};
var $array = new Arrays();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * Timer is a timing controller class for schedule, it is usefull when you want
 * to do a series of thing step by step by time
 * Author:Kong Kaiping [Ricky Kong]
 * 
 * Note:
 * 
 * Usage:
 *	1. create a timer
 *		var timer = new timer([function(){}],[gap<count by microsecond>],[timers],[att]);
 *	2. start a timer
 *		timer.start();
 *	3. stop a timer
 *		timer.stop();
 *	4. pause a timer
 *		timer.pause();
 *	5. resume a timer
 *		timer.resume();
 *	6. restart a timer
 *		timer.restart();
 **/
var Timer = newClass();
Timer.prototype={
	/*
	 *  task:function or exec String you want the timer to call
	 *  gap:the default space of time between tow calls
	 *  times:how manay times you want the timer to exec.if times==null, it
	 *  will never stop,i.e. now the timer is a cron.
	 *  att:att(attachment) is parameter you want the timer help you to pass to
	 *  task function.
	 */
	init : function(task, gap, times, att){
		var id='timer'+$object.getID(this),I=this,count=0,stop=true,pause=false,timeout;
		$MYJS[id]=function(){
			var nav = stop?true:(pause?false:typeof(task||'')=='string'?eval(task):task(count++,{attach:att}));
			if(!(stop=(nav<0)||!(times==null||times>count)))
				timeout = setTimeout('$MYJS.'+id+'();',typeof(nav)=='number'?nav:gap||1000);
		};
		I.setTask=function(t, arg){task=t;att=arg?arg:att;return I;};
		I.setGap=function(g){gap=g;return I;};
		I.setTimes=function(ts){times=ts;return I;};
		I.stop=function(){
			clearTimeout(timeout);
			stop=true;
		};
		I.resume=function(){pause=false;};
		I.pause=function(){pause=true;};
		I.restart=function(){I.stop();I.start();};
		I.start=function(){
			if(stop){
				count=0;
				timeout = setTimeout('$MYJS.'+id+'();',0);
				stop=false;
			}
		};
	}
};
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * Events is event process relative class, it contains some common functions,
 * especially setting and removing event handler of a node
 * Author:Kong Kaiping [Ricky Kong]
 * 
 * Note:$event is a instance of Events, usually we only need to use this object,
 * it is enough for our work,so you need not to reinstance another Events object
 * 
 * 
 * Usage:
 *	1. set event handle function to a target
 *		$event.setEvent(target,'eventName',function (event,obj){
 *			do what you want to do,when the 'eventName'
 *			event is triggered
 *		});
 *		this function will clear the prior settd event handles.
 *	2. add event handle function to the target
 *		$event.addEvent(target,'eventName',function (event,obj){
 *			do what you want to do,when the 'eventName'
 *			event is triggered
 *		});
 *		this function will not remove the prior added eventHandle,it
 *		is useful when you want to add more than one event handle, e.g.
 *		you don't sure whether the others has set eventHandle to a
 *		elemment,and you don't want to cover it.
 *	
 *	2. delete a event hadle from the target
 *		$event.delEvent(target,'eventName',eventHandle);
 *		this function will remove the specified evehtHandle from the
 *		target
 *	3. remove all event handles from the target
 *		$event.clearEvent(target,'eventName');
 **/
var Events = newClass();
Events.prototype={
	storage: {},

	getQueue : function(node, eventName){
		var I = this,
		store = I.storage,
		key = $object.getID(node),
		tmp = store[key] ? store[key] : store[key]={},
		q = tmp[eventName] ? tmp[eventName] : tmp[eventName]=[];

		if(!q.func){
			if(node[eventName])
				q.push(node[eventName]);
			node[eventName]= q.func = function(e){
				e = e || window.event;
				var goon = true;
				for(var i = 0,len = q.length; i < len; i++){
					try{goon = (goon && (q[i](node,e)!==false));}catch(ex){
						$MYJS.debug($object.toStr(ex));
					}
				}
				return goon
			}
			q.isNew = true;
		}else
			delete q.isNew;
		return q;
	},

	setEvent : function(goal, eventName, func){
		var I = this,ret=[];
		$array.each($array.ensureArray(goal), function(node){
			if(node){
				var q = I.getQueue(node,eventName);
				q[0]=func;
				q.length=1;
				ret.push(0);
			}else
				ret.push(null);
		});
		return ret.length==1 ? ret[0] : ret;
	},

	addEvent : function(goal, eventName, func){
		var I = this, ret = [];
		$array.each($array.ensureArray(goal), function(node){
			if(node){
				var q = I.getQueue(node, eventName);
				ret.push(q.push(func)-1);
			}else
				ret.push(null);
		});
		return ret.length==1 ? ret[0] : ret;
	},

	unshiftEvent : function(goal, eventName, func){
		var I = this, ret = [];
		$array.each($array.ensureArray(goal), function(node){
			if(node){
				var q = I.getQueue(node, eventName);
				q.unshift(func);
				ret.push(0);
			}else
				ret.push(null);
		});
		return ret.length==1 ? ret[0] : ret;
	},

	clearEvent : function(goal, eventName){
		var I = this;
		$array.each($array.ensureArray(goal), function(node){
			if(node){
				var q = I.getQueue(node, eventName);
				q.length=0;
			}
		});
	},

	delEvent : function(goal, eventName, func){
		var I = this;
		$array.each($array.ensureArray(goal), function(node){
			if(node){
				var q = I.getQueue(node, eventName);
				if(typeof(func)=='number')
					q.splice(func, 1);
				else
					I.arrays.del(q, func);
			}
		});
	}
}
var $event = new Events();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * ReqPool is a pool for XMLHttpRequest, it help user to manage XMLHttpRequest
 * Author:Kong Kaiping [Ricky Kong]
 * 
 * Note:$reqPool is a instance for user easy usage.mostly user has no need to 
 * new a pool but use the object.
 * 
 * Usage:
 *	1. get XMLHttpRequest
 *		var xhr = $reqPool.get();
 *	2. get back XMLHttpRequest
 *		$reqPool.getBack(xhr);
 **/
var	ReqPool = newClass();
ReqPool.prototype = {
	init : function(){
		this.pool = [];
	},

	getBack : function(xhr){
		xhr.abort();
		this.pool.push(xhr);
	},

	get : function(){
		var micXhrV = ["Msxml2.XMLHTTP","Microsoft.XMLHTTP"];//micsoft XMLHttpRequest Verstion
		var xhr = this.pool.pop();//XMLHttpRequest
		if(xhr == null){
			if (window.ActiveXObject)
				for(var v in micXhrV)try{xhr=new ActiveXObject(micXhrV[v]);break;}catch(e){}
			else
				xhr=new XMLHttpRequest();
		}
		return xhr;
	}
};
var $reqPool = new ReqPool();

/**
 * HttpRequest is a wrap for XMLHttpRequest to simplify ajax function
 * Author: Kong Kaiping [Ricky Kong]
 * 
 * Note:Ajax is an alias of HttpRequest and $ajax is an Ajax instance 
 * for esay usage(user has no need to reinstance a Ajax)
 * 
 * usage:
 *	1.the most common usage:
 *		$ajax.setEar(function(xhr){
 *			//do what you wanna do
 *		}).get(url,[parameter],[isSynchronous]);
 *	2.setup callback function:
 *		HttpRequest support add,delete,set callback function,
 *		relative method are addEar,setEar,delEar,clearEar
 *			addEar:increase a callback function to ajax
 *			setEar:set a callback function and remove old ones
 *			delEar:remove a callback function
 *			clearEar:remove all callback functions
 *	3.abort ajax
 *		user abort method to do this. this method need a request id as parameter.
 *		the request id can gotten from the return of post and get method
 *		eg.
 *			var info = $ajax.get(url);
 *			$ajax.abort(info.id);
 *	4.set error process function
 *		$ajax.setOnError(function(){});
 **/
var	HttpRequest = newClass();
HttpRequest.prototype = {
	init : function(){
		this.q = [[],[],[],[],[],[],[],[],[],[],[]];
		this.latestEarID = -1;
		this.onServerError=function(xhr, e){
			if(xhr.status < 100 || xhr.status > 500)
				alert("Network disconnected, please check it.");
			else
				alert("Server error: " + xhr.status);
			return false;
		};
	},

	getQId : function(state){
		return (typeof(state)=='number'?state:4)/0.5;
	},

	xhrs:{count:0},

	setOnServerError : function(f){
		this.onServerError = f;
		return this;
	},

	abort : function(id){
		var xhr =  this.xhrs[id];
		if(xhr){
			delete this.xhrs[id];
			$reqPool.getBack(xhr);
		}
	},

	addEar : function(f, s){
		var ears = this.q[this.getQId(s)];
		this.latestEarID = ears.push(f)-1;
		return this;
	},

	setEar : function(f, s){
		var ears = this.q[this.getQId(s)];
		ears[this.latestEarID = (ears.length = 1) - 1] = f;
		return this;
	},

	delEar : function(earID, s){
		var ears = this.q[this.getQId(s)];
		ears.splice(earID, 1);
		return this;
	},

	clearEar : function(s){this.q[this.getQId(s)].length=0;},

	post : function(url, args, syn){
		return this.send(url, "POST", args, syn);
	},

	get : function(url, args, syn){
		return this.send(url, "GET", args, syn);
	},

	callback : function(toCall,xhr){
		if(typeof toCall =='function')
			toCall(xhr);
		else if(toCall.object && toCall.method)
			toCall.method.apply(toCall.object,[xhr]);
	},

	send : function(url, method, args, syn){
		if(typeof(args) == 'boolean'){
			var tmp = args;//this can allow args and syn to exchange the order.
			args = syn;
			syn = tmp;
		}

		var q = $array.copy(this.q, true),
		onError = this.onServerError,
		callback = this.callback,
		isPost = method=='POST',
		getQId = this.getQId,
		xhrs = this.xhrs,
		count = xhrs.count++,
		xhr = $reqPool.get(),
		lastState = 0,
		text;

		var dispatch = function (){
			var state = xhr.readyState,
			middleQ = q[getQId(state-0.5)],
			curQ = q[getQId(state)],
			globalQ = q[getQId(5)],
			goon = true,
			i;

			try{
				if(state > lastState)
					for(i = 0; i < middleQ.length; i++) callback(middleQ[i], xhr);
				if(state==4 && xhr.status!=200)
					goon = onError(xhr);
				if(goon){
					for(i = 0; i < curQ.length; i++)  callback(curQ[i], xhr);
					for(i = 0; i < globalQ.length; i++) callback(globalQ[i], xhr);
				}
			}catch(e){$MYJS.debug(e);}

			if(state == 4){
				text = xhr.responseText;
				delete xhrs[count];
				$reqPool.getBack(xhr);
			}

			lastState = state;
		}

		if(!isPost&&args) url += (url.indexOf('?')<0 ? '?' : '&') + args;
		xhrs[count] = xhr;

		xhr.onreadystatechange = dispatch;
		xhr.open(method, url, !syn);
		if(isPost){
			xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			xhr.setRequestHeader("Content-Length", args ? args.length : 0);
		}
		try{xhr.send(args);}catch(e){
			$reqPool.getBack(xhr);
			$MYJS.debug(e);
		}
		/* Force Gecko core browser to handle ready state 4 for synchronous requests */
		if(syn && Browser.Gecko)	dispatch();
		return {text: text, id: count};
	}
};
var Ajax = HttpRequest;
var $ajax = new Ajax();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
var Forms = newClass();
Forms.prototype={
	init:function(){
		this.ajax = new Ajax();
		this.onBeforeSubmit = this.onSubmit = $MYJS.nil;
	},
	getIframe:function(){
		var ret = this.iframe || (this.iframe = $uiBase.genNode('<iframe onload="if(this.onloadCallback) this.onloadCallback();" name="fileUploadIframe" style="display:none;"></iframe>'));
		document.body.appendChild(ret);
		return ret;
	},
	getKNs : function(n){
		var ret=[];
		if(n.tagName=='SELECT' && n.multiple==true)
			$array.each(n.options, function(o){
				if(o.selected)
					ret.push({key: n.name, value: o.value});
			});
		else	if(n.name)
			ret.push({key: n.name, value: n.value});
		return ret;
	},

	encodeURI : function(goal, getKN){
		var coll = [],
		encode = window.encodeURIComponent,
		tar = goal.tagName == 'FORM' ? goal.elements :
			goal.tagName ? [target] :
			goal;

		if(!getKN) getKN = this.getKNs;
		$array.each(tar, function(e){
			var kns = getKN(e);
			kns = $array.isArray(kns) ? kns : [kns];
			$array.each(kns, function(e2){
				coll.push(encode(e2.key) + '=' + encode(e2.value));
			});
		});

		return coll.join('&');
	},

	submit : function(form, callBack){
		var method = form.method=='get' ? 'get' : 'post',I = this;
		I.onBeforeSubmit();
		if(form.enctype=='multipart/form-data'){
			this.submitByIframe(form, callBack);
		}else{
			this.ajax.setEar(function(xhr){
				I.onSubmit();
				if(callBack)	callBack(xhr);
			});
			this.ajax[method](form.action, this.encodeURI(form));
		}
	},
	
	submitByIframe : function(form, callBack){
		var iframe = this.getIframe(), I = this;
		iframe.onloadCallback = function(){
			I.onSubmit();
			if(callBack)	callBack({responseText:$string.trim(iframe.contentWindow.document.body.innerHTML)});
		}
		form.target = iframe.name;
		form.submit();
	}
}
var $form = new Forms();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * CssPatherParser is a class for parse CSS String to Obejct modle, DomFinder
 * can use the Object modle to query node. So the class will not be used by the
 * application user.
 * Author: Kong Kaiping(Ricky Kong)
 * 
 **/

var CssPathParser = newClass();
CssPathParser.prototype = {
	init : function(){
		function tag(curNode,attVal){
			curNode.tagName=(curNode[0]={att:'tagName',val:attVal.toUpperCase(),op:'='}).val;
		}
		function claz(curNode,attVal){
			curNode.className=(curNode[curNode.length]={att:'className',val:attVal,op:'.'}).val;
		}
		function fakeClaz(curNode,attVal){
			curNode[attVal]=curNode[curNode.length]={att:attVal,val:attVal,op:':'};
		}
		function id(curNode,attVal){
			curNode.id=(curNode[curNode.length]={att:'id',val:attVal,op:'#'}).val;
		}
		function att(curNode,attVal){
			var NV=attVal.split('=');
			if(NV.length>=2){
				var n = NV[0],len=n.length-1,c=n.charAt(len),val=NV.slice(1).join('=');
				if(c=='~'||c=='|'||c=='!')
					curNode[curNode.length]={att:n.substr(0,len),val:val,op:c+'='};
				else
					curNode[n]=(curNode[curNode.length]={att:n,val:val,op:'='}).val;
			}
		}
		this.relOp={' ':1,'>':1,'+':1,',':1};
		this.endOn = {' ':1,'#':1,'.':1,'>':1,'[':1,']':1,'+':1,':':1,',':1};
		this.procs = {'':tag,'.':claz,'#':id,'[':att,']':att,':':fakeClaz};
		this.tag=tag;
		this.ESC='/';
	},

	parse : function(cssPath){
		var chars=cssPath.split(''),
		endOn = this.endOn,
		relOp = this.relOp,
		procs = this.procs,
		tag = this.tag,
		dftEndOn=endOn,
		spEndOn={']':1},
		curNode = [],
		path = [];

		var val='',proc=tag,beESC=false;
		for(var ch; ch = chars.shift(); ){
			beESC= ch==this.ESC;
			ch = beESC ? chars.shift() : ch;
			if(!beESC && endOn[ch]){
				endOn= ch=='[' ? spEndOn : dftEndOn;
				if(ch==' ' && relOp[chars[0]]) continue;
				if(val){
					proc(curNode, val);
					val = '';
				}
				if(relOp[ch]){
					if(curNode.length){
						path.push(curNode);
						path.push(ch);
						curNode = [];
					}
					proc = tag;
				}else{
					proc = procs[ch];
				}
			}else{
				val += ch;
			}
		}
		if(val)	proc(curNode, val);
		if(curNode.length)	path.push(curNode);
		return path;
	}
};

/**
 * DomFinder is the core class for node query in the dom tree. it find the nodes
 * and return them. Application users usually will not use it, instead,they will use some
 * handy function defined below.
 * Author:Kong Kaiping(Ricky Kong)
 * 
 **/
var DomFinder = newClass();
DomFinder.prototype = {
	init : function(){
		this.limit=false;
		this.funcs=[byId,byTag,byName,byPath];
		function byId(id,top){
			var ret = document.getElementById(id);
			return ret?(top&&top!=document&&!isTop(top,ret)?[]:[ret]):[];
		}
		function byTag(tagName,top){
			return (top?top:document).getElementsByTagName(tagName);
		}
		function byName(nam,top){
			var all=document.getElementsByName(nam);
			return top&&top!=document?delNotChild(all,top):all;
		}
		function byPath(path,top,isTight){
			var ret=[];
			path.unshift(',');
			for(var end = path.length-1, i = end, cur, i1 = -1, ns; (cur = path[i]); i--){
				if(cur==','){
					var leaf = path[end],
					pivot = i1==-1 ? null : ns[0],
					sub = path.slice(i + 1, i1 + 1);

					if(i1 == -1 || i1 == i+1 || isFitPath(sub, pivot, isTight ? top : null)){
						if(i1 == end)
							ret.push(pivot);
						else{
							var a = leaf.name || leaf.tagName || '*',
							p = (!top || isTop(top, pivot)) ? pivot : top,
							nSib = p ? p.nextSibling : null,
							can = leaf.name ? byName(a, p) : byTag(a, p);

							sub = path.slice(i1 == -1 ? i + 1 : i1, end + 1);
							if(nSib && sub[1] == '+' && isFitPath(sub, nSib, pivot))
								ret.push(nSib);

							for(var j = 0, len = can.length ; j < len ; j++){
								if(isFitPath(sub, can[j], pivot))
									ret.push(can[j]);
							}
						}
					}
					end=i-1;
					i1=-1;
				}else if(typeof(cur)=='object'&&i1<0){
					if((ns=byId(cur.id,isTight?top:null)).length==1
					||(ns=byName(cur.name,isTight?top:null)).length==1
					||(ns=byTag(cur.tagName,isTight?top:null)).length==1) i1=i;
				}
			}
			return ret;
		}
		function delNotChild(nodes,node){
			for(var i=0, ret = [], len=nodes.length; i<len; i++)
				if(isTop(node,nodes[i]))
					ret.push(nodes[i]);
			return ret;
		}
		function isTop(topNode,node){
			if(node)do{node=node.parentNode;}while(node&&node!=topNode);
			return node!=null;
		}
		function isFitNode(node,atts){
			out:for(var i=0, att; (att=atts[i]); i++){
				var val = att.val,name=att.att,attVal=node[name];
				attVal=attVal==null?null:attVal+'';
				switch(att.op){
					case  '#':
					case  '=': if(attVal!=val&&(val!='*'||name!='tagNane')) break out; break;
					case  '.': if((' '+attVal+' ').indexOf(' '+val+' ')<0) break out; break;
					case '~=': if(attVal==null||attVal.indexOf(val)<0) break out; break;
					case '|=': if(attVal==null||attVal.indexOf(val)!=0) break out;break;
					case '!=': if(attVal==val) break out;
				}
			}
			return att==null;
		}
		function isFitPath(path,node,topNode){
			if(!topNode)	topNode=document;
			for(var i=path.length, att, isFit=true, div='+'; (att=path[--i]); ){
				if(typeof(att) == 'string'){
					switch(div = att){
						case '>': 
						case ' ': node=node.parentNode;continue;
						case '+': node=node.previousSibling;continue;
						default : return false;
					}
				}else{
					while(node && !(isFit=(isFitNode(node,att)))){
						node=(div==' '&&node!=topNode)?node.parentNode:null;
					}
					if(!isFit||topNode==node)
						break;
				}
			}
			return i<0||(i==0 && topNode==node);
		}
	},

	cpp: new CssPathParser(),

	reg:/^#(\w*)$|^([\w\*]*)$|^\[name=(\w*)\]$/,

	css : function(exp,top){
		var $1,$2,$3;
		if(this.reg.test(exp))
			$1=RegExp.$1,$2=RegExp.$2,$3=RegExp.$3;
		return this.funcs[$1?0:$2?1:$3?2:3]($1||$2||$3||this.cpp.parse(exp),top);
	}
};

var DF = new DomFinder();

/*
 * Handy functions form dom nodes query
 * Author:Kong Kaiping(Ricky Kong)
 * 
 * Usage:
 *	1. Query by ID, return a single node
 *		var node = $("id",[parentNode]);
 *	2. Query by tagName, return a set of nodes
 *		var nodes = $tag("tagName",[parentNode]);
 *	3. Query by className,return a set of nodes
 *		var nodes = $claz("className",[parentNode]);
 *	4. Query by name,return a set of nodes
 *		var nodes = $name("name",[parentNode]);
 *	5. Query by CSS path String,return a set of nodes
 *		var nodes = $css("cssPath",[parentNode]);	
 */
var $ = function(id, top){
	return top ? DF.funcs[0](id,top)[0] : document.getElementById(id);
};
var $css = function(exp, top){
	return DF.css(exp, top);
};
var $tag = function(tag, top){
	return (top? top : document).getElementsByTagName(tag);
};
var $claz = function(claz, top){
	var ctx = top ? top : document;
	return ctx.getElementsByClassName ? ctx.getElementsByClassName(claz) : DF.css('.'+claz, top);
};
var $name = function(name, top){
	return top ? DF.funcs[2](name, top) : document.getElementsByName(name);
};
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
/**
 * UIBase is class that defines basic and common function on UI,such as location
 * ,set style,create nodes etc.
 * Author:Kong Kaiping(Ricky Kong)
 * 
 * Node:$uiBase is instance of UIBase, use can use this object instead of new a
 * UIBase object.
 * 
 * Usage:
 *	1.get location of a node
 *		$uiBase.getPos(node);
 *	2.create nodes by HTML/tagName
 *		$uiBase.genNode(html/tagName);//return a single node
 *		$uiBase.genNodes(html/tagName);//return a set of nodes
 *	3.set Style for specified node
 *		$uiBase.setCSS(node/nodes,cssString);
 *		$uiBase.setStyle(node/nodes,styleAttrs);//styleAttrs is a object
 *		that contains style attributes you want to set.e.g.{color:red}
 **/
var	UIBase = newClass();
UIBase.prototype = {
	init : function(){
		$object.copyAttr(this,{
			lifeDoor: document.createElement('DIV'),
			tagReg: /^\w+$/,
			trReg:/^<tr(.|\s)*\/tr>$/i,
			tdReg:/^<td(.|\s)*\/td>$/i,
			optgroupReg:/^<optgroup(.|\s)*\/optgroup>$/i,
			optionReg:/^<option(.|\s)*\/option>$/i
		});
		this.lifeDoor.style.display='none';
	},

	getPos : function(node, offsetParent){
		var left = 0, top = 0, n = node;
		while(n){
			var op = n.offsetParent,deltaX=0,deltaY=0;
			if(op){
				if(op.style.borderLeftWidth)
					deltaX += parseInt(op.style.borderLeftWidth) * (Browser.Gecko ? 2 : Browser.Safari || Browser.IE ? 1 : 0);
				if(op.style.borderTopWidth)
					deltaY += parseInt(op.style.borderTopWidth) * (Browser.Gecko ? 2 : Browser.Safari || Browser.IE ? 1 : 0);
			}
			left += n.offsetLeft + deltaX;
			top += n.offsetTop + deltaY;
			n = (offsetParent==op)?null:op;
		}
		return {top: top,
			left: left,
			offsetWidth: node.offsetWidth,
			offsetHeight: node.offsetHeight,
			width: node.offsetWidth || parseInt(node.style.width),
			height: node.offsetHeight || parseInt(node.style.height),
			offsetTop: node.offsetTop || parseInt(node.style.top),
			offsetLeft: node.offsetLeft || parseInt(node.style.left),
			display: node.style.display,
			position: node.style.position};
	},
	
	getEventXY : function(evt){
		var body = document.body,xy={};
		$object.copyAttr(xy,evt,function(n){
			return n=='pageX'||n=='clientX'||n=='screenX'||n=='layerX'||
				n=='pageY'||n=='clientY'||n=='screenY'||n=='layerY';
		});
		if(Browser.IE) xy.pageX = evt.clientX+body.scrollLeft+body.parentNode.scrollLeft;
		if(Browser.IE) xy.pageY = evt.clientY+body.scrollTop+body.parentNode.scrollTop;
		return xy;
	},

	genNode : function(html){
		return this.genNodes(html)[0];
	},

	genNodes : function(html){
		var d = this.lifeDoor,
		ret = [],
		htm = $string.trim(html);

		if(this.tagReg.test(htm)){
			ret.push(document.createElement(htm));
		}else{
				if(this.tdReg.test(htm)){
					d.innerHTML = '<table><tbody><tr>'+htm+'</tr></tbody><table>';
					ret = d.firstChild.firstChild.firstChild.childNodes;
				}else if(this.trReg.test(htm)){
					d.innerHTML = '<table><tbody>'+htm+'</tbody><table>';
					ret = d.firstChild.firstChild.childNodes;
				}else if(this.optionReg.test(htm)||this.optgroupReg.test(htm)){
					d.innerHTML = '<select>'+htm+'<select>';
					ret = d.firstChild.childNodes;
                                }else{
					d.innerHTML = htm;
					ret = d.childNodes;
				}
                                ret = $array.copy(ret);
		}
		return ret;
	},

	toJSName : function(cssName){
		return cssName.replace(/-\w/g,
			function($0){return $0.toUpperCase().charAt(1);}
		);
	},

	setCSS : function(node, cssStr){
		var convert = this.toJSName,
		setStyle = this.setStyle,
		as = cssStr.split(';'), 
		style = {};

		$array.each(as,function(a){
			var p = a.split(':');
			if(p.length == 2)
				style[convert(p[0].replace(/\s+/g,''))] = p[1].replace(/^\s+|\s+$/g,'');
		});
		$array.each($array.ensureArray(node),function(n){
			try{setStyle(n,style);}catch(e){$MYJS.debug(e)}
		});
	},

	setStyle : function(node, style){
		$array.each($array.ensureArray(node),function(n){
			for(var v in style){
				if(v == 'opacity' && Browser.IE){
					n.style.filter = 'alpha(opacity=' + (parseFloat(style[v]) * 100) + ')';
					if(!n.currentStyle || !n.currentStyle.hasLayout) n.style.zoom=1;
				}
				else
					n.style[v] = style[v];
			}
		});
	},

	getViewPort : function(){
		return document.compatMode=='BackCompat'||Browser.Opera?document.body:document.body.parentNode;
	},

	addClass : function(node,toAdd){
		$array.each($array.ensureArray(node),function(n){
			var classNames = n.className;
			classNames.replace(new RegExp('\\s+'+toAdd+'\\s+|\\s+'+toAdd+'$|^'+toAdd+'\\s+','g'),' ');
			n.className += ' ' + toAdd;
		});
	},

	delClass : function(node,toDel){
		$array.each($array.ensureArray(node),function(n){
			var classNames = n.className;
			n.className = classNames.replace(new RegExp('\\s+'+toDel+'\\s+|\\s+'+toDel+'$|^'+toDel+'\\s+','g'),' ');
		});
	},
	
	getCenterXY:function(outerPos, innerPos){
		return {x : (outerPos.width - innerPos.width) / 2,
				y : (outerPos.height - innerPos.height) / 2};
	}
}
var $uiBase = new UIBase();
/*-------------------------------------------------------------------------->>*/

/*|---------------------------------------------------------------------------*/
var Gradual=newClass();
Gradual.prototype={
	init : function(node){
		$object.copyAttr(this,{
			node : node,
			aides : [new Gradual.Plain(node)],
			timer : this.getTimer(node)
		});
	},

	timers:{},

	getTimer : function(node){
		var id = $object.getID(node);
		return this.timers[id] || (this.timers[id] = new Timer(null,20));
	},

	up : function(){
		this.timer.setTask(this.doTask, [this.node, this.aides, 'up']).restart();
	},

	down : function(){
		this.timer.setTask(this.doTask, [this.node, this.aides, 'down']).restart();
	},

	doTask : function(i, info){
		var okCount = 0,
		as = info.attach[1],
		node = info.attach[0],
		dir = info.attach[2];

		for(var j=0; j < as.length; j++)
			okCount += as[j][dir](node,i) ? 0 : 1;

		return okCount==as.length ? -1 : 20;
	}
};
Gradual.Plain=newClass();
Gradual.Plain.prototype={
	init : function(node){
		this.display = node.style.display;
		if(this.display == 'none')
			this.display = '';
	},

	up : function(node){node.style.display = this.display; },

	down : function(node){node.style.display = 'none'; }
}
/*-------------------------------------------------------------------------->>*/
TipTool = newClass();
TipTool.prototype = {
	init : function(showTipEvent,hideTipEvent){
		this.shop = new TipShop();
		this.showTipEvent = showTipEvent || 'onmousemove';
		this.hideTipEvent = hideTipEvent || 'onmouseout';
	},

	setTip : function(goal){
		var shop = this.shop;

		if(shop.initTip)
		$array.each(goal,function(item){
			shop.initTip(item);
		});

		$event.addEvent(goal, this.showTipEvent, function(goal,e){
			shop.showTip(goal,e);
		});

		$event.addEvent(goal, this.hideTipEvent, function(goal, e){
			shop.hideTip(goal,e);
		});
	}
}

TipShop = newClass();
TipShop.prototype = {
	init : function(){
		this.tip = $uiBase.genNode('<span style="display:inline-block;zoom:1;" class="tipFrame"></span>');
		this.frame = $uiBase.genNode('<div id="tip'+this.getID()+'"></div>');
		this.tipStyle = {fontSize:'12px',padding:'2px 2px 5px',background:'#ffffe1',border:'solid 1px black'};
		$uiBase.setCSS(this.frame,'visibility:hidden;position:absolute;width:600px;');
		document.body.appendChild(this.frame);
	},

	getID : function(){
		return "tipFrame"+$object.getID(this);
	},

	getContent : function(goal){
		var head = this.getHeader(goal), body = this.getBody(goal), foot = this.getFooter();
		return (head?('<div class="tipHeader">'+head+'</div>'):'') +
			(body?('<div class="tipBody">'+body+'</div>'):'') +
			(foot?('<div class="tipFooter">'+foot+'</div>'):'');
	},

	getHeader: $MYJS.nil,
	getFooter: $MYJS.nil,

	getBody : function(goal){
		return $object.getAttr(goal, "title");
	},

	getBasePoint : function(goal, e){
		return $uiBase.getEventXY(e);
	},

	getPosition : function(goal, e){
		var xy = this.getBasePoint(goal, e),
		frame = this.frame,
		viewPort = $uiBase.getViewPort(),
		rightWidth =  viewPort.clientWidth - xy.clientX,
		bottomHeight =  viewPort.clientHeight - xy.clientY,
		position = {};

		position.left = (xy.pageX-(frame.offsetWidth-rightWidth>0?frame.offsetWidth-rightWidth:0));

		var onBottom = bottomHeight > (frame.offsetHeight + 20) || bottomHeight > xy.clientY;
		position.top = (xy.pageY + 20 - (onBottom ? 0 : frame.offsetHeight + 24));

		return position;
	},
 
	initTip : function(goal){
		$object.getAttr(goal, "title", goal.title);
		goal.title = '' ;
	},

	showTip : function(goal,e){
		var tip = this.tip,
		frame = this.frame,
		viewPort = $uiBase.getViewPort(),
		maxWidth = viewPort.clientWidth > 600 ? 600 : viewPort.clientWidth;

		$uiBase.setCSS(frame, 'left:0px;width:'+maxWidth+'px');
		tip.innerHTML = this.getContent(goal);
		frame.appendChild(tip);
		frame.style.visibility = 'visible';
		$uiBase.setStyle(tip, this.tipStyle);

		if(tip.offsetWidth < maxWidth)
			frame.style.width = (tip.offsetWidth)+'px';

		var position = this.getPosition(goal, e);
		frame.style.left = position.left +'px';
		frame.style.top = position.top +'px';
	},

	hideTip : function(){
		this.frame.style.visibility = 'hidden';
	}
}
/*|---------------------------------------------------------------------------*/
