/*==========
author:	吴颖晖 Freddie
copyright:	Copyright (c) 1998-2009 Flying @ Flying's World
version:	5.4b
# Requires jQuery
==========*/
F={
	assert:function(test,msg){
		if(test)return true;
		return F.error(msg||"Assertion error");
	},
	error:function(msg,nothrow){
		var note=window.console?null:"\n\n"+
			"Please help report this error to Flying with following information:\n"+
			"\t+ Browser name and version\n"+
			"\t+ Operating system name and version\n"+
			"\t+ Any specificial configuration in your system\n"+
			"\nThank you!";
		if(note)alert(msg+note);else console.log("%s",msg);
		if((!nothrow)&&("function"==typeof window.Error))throw new Error(msg);
		return false;
	},
	quote:function(str){
		return str.replace(new RegExp("(\\"+".[]?*+{}|^$()\\".split('').join("|\\")+")","g"),"\\$1");
	}
};
// prerequisite detection
if("function"!=typeof window.jQuery)F.error("Required: jQuery");

/*< URI parsing >*/
//adapted from parseUri 1.2 (http://blog.stevenlevithan.com/archives/parseuri)
F.URI=function(uri,strict){
	var self=this,
		regex=strict
			?/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/
			:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,
		fields=["URI","PROTOCOL","AUTHORITY","USER_INFO","USER","PASSWORD","HOST","PORT","RELATIVE","PATH","DIRECTORY","FILE","QUERY","ANCHOR"];
	//parse whole URI
	var match=regex.exec(uri||location.href);
	if(!match)return F.error("Invalid URI: "+uri);
	$.each(fields,function(i,key){self[key]=match[i]||"";});
	//parse query string
	self.PARAMS={};
	self.QUERY.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(_,key,val){
		if(key)self.PARAMS[decodeURIComponent(key)]=decodeURIComponent(val);
	});
	//< update query string >
	self.updQuery=function(special){
		special=special||'';
		var self=this;
		//construct the query
		var q=$.param(self.PARAMS);
		//put it into original object
		if(self.QUERY.length){
			self.URI=self.URI.replace(self.QUERY,q);
			self.RELATIVE=self.RELATIVE.replace(self.QUERY,q);
		}else{
			self.URI+='?'+q;
			self.RELATIVE+='?'+q;
		}
		self.QUERY=q;
		return q;
	};
	//< convert back to URL >
	self.toString=function(special){
		this.updQuery(special);
		return this.URI;
	};
};

/*< HTTP cookie >*/
F.cookie=function(key,val,opt){
if("undefined"==typeof val){
//get cookie
	if(!document.cookie)return null;
	var cookies={};
	$.each(document.cookie.split(';'),function(){
		var mat=/^([^=]+)=(.*)$/.exec($.trim(this));
		if(mat)cookies[decodeURIComponent(mat[1])]=decodeURIComponent(mat[2]);
	});
	return cookies[key];
}else{
//set cookie
	opt=opt||{};
	if(null===val){
		val="";
		opt.expires=-1;
	}
	//handle expiry
	if(opt.expires){
		var d=new Date();
		if("number"==typeof opt.expires){
			d.setTime(d.getTime()+(opt.expires*(60*1000)));	//in minutes
		}else if(opt.expires.toUTCString){
			d=options.expires;
		}
		opt.expires=d.toUTCString();
	}
	//assemble parts together
	function pair(key,val){return key+'='+val;};
	var parts=[pair(encodeURIComponent(key),encodeURIComponent(val))];
	$.each(["expires","path","domain"],function(){
		if(opt[this])parts.push(pair(this,opt[this]));
	});
	if(opt.secure)parts.push("secure");
	document.cookie=parts.join("; ");
}
};

/*< system language detection >*/
F.Lang={
	get:function(cookie_name){
		//try current URI
		var lang=new F.URI().PARAMS.lang;
		if(lang&&F.Lang.iso639[lang])return lang;
		//try cookie
		lang=F.cookie(cookie_name||"lang");
		if(lang&&F.Lang.iso639[lang])return lang;
		//try browser settings
		lang=$.grep([
				navigator.language,			//Netscape
				navigator.userLanguage,		//IE (OS regional settings)
				navigator.browserLanguage,	//IE (browser localized language)
				navigator.systemLanguage	//IE (OS localized language)
			],function(x){return x;});
		if(lang.length>0){
			//translate 1st match into ISO 639 format
			var locale=lang[0].replace(/_/g,'-');
			lang=null;
			$.each(F.Lang.iso639,function(lid,re){
				if(lang)return;
				if(re.test(locale))lang=lid;
			});
			if(lang)return lang;
		}
		//fall back to page default
		return F.Lang.def();
	},
	def:function(){
		lang=$("html").attr("xml:lang")||$("html").attr("lang");
		F.assert(lang&&F.Lang.iso639[lang],"html[xml:lang] doesn't exist or is invalid");
		return lang;
	},
	iso639:{	//order: from most specific to more generic
		"en":/^en(-.*)?/,
		"zh-Hant":/^zh-(TW|HK|MO)/,
		"zh-Hans":/^zh(-(CN|SG|.*))?/
	}
};
$.extend($.fn,{lang:function(id){
	return id?this.attr({"xml:lang":id,lang:id}):(this.attr("xml:lang")||this.attr("lang"));
}});

/*< Base64 codec >*/
F.Base64={
	enc:function(str){
		str=F.UTF8.enc(str);
		var b64=[],c=[],e=[];
		for(var i=0,l=str.length;i<l;){
			for(var j=0;j<3;++j)c[j]=str.charCodeAt(i++);
			e[0]=c[0]>>2;
			e[1]=((c[0]&0x03)<<4)|(c[1]>>4);
			e[2]=((c[1]&0x0F)<<2)|(c[2]>>6);
			e[3]=c[2]&0x3F;
			if(isNaN(c[1]))e[2]=e[3]=0x40;
			else if(isNaN(c[2]))e[3]=0x40;
			b64.push($.map(e,function(i){return F.Base64.code.charAt(i);}).join(''));
		}
		return b64.join('');
	},
	dec:function(b64){
		b64=b64.replace(new RegExp("[^"+F.quote(F.Base64.code)+"]","g"),"");	//remove all invalid characters
		var str=[],c=[],e=[];
		for(var i=0,l=b64.length;i<l;){
			for(var j=0;j<4;++j)e[j]=F.Base64.code.indexOf(b64.charAt(i++));
			c[0]=(e[0]<<2)|(e[1]>>4);
			c[1]=((e[1]&0x0F)<<4)|(e[2]>>2);
			c[2]=((e[2]&0x03)<<6)|e[3];
			str.push(String.fromCharCode(c[0]));
			if(0x40!=e[2])str.push(String.fromCharCode(c[1]));
			if(0x40!=e[3])str.push(String.fromCharCode(c[2]));
		}
		return F.UTF8.dec(str.join(''));
	},
	code:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
};
F.assert(F.Base64.code.length==(0x40+1),"Invalid Base64 code table");

/*< UTF-8 codec >*/
F.UTF8={
	enc:function(str){
		str=str.replace(/\r\n/g,"\n");
		var utf=[];
		for(var i=0,l=str.length;i<l;++i){
			var c=str.charCodeAt(i);
			if(c<=0x00007F)
				utf.push(String.fromCharCode(c));
			else if(c<=0x0007FF)
				utf.push(String.fromCharCode((c>>6)|0xC0),String.fromCharCode((c&0x3F)|0x80));
			else if(c<=0x00FFFF)
				utf.push(String.fromCharCode((c>>12)|0xE0),String.fromCharCode(((c>>6)&0x3F)|0x80),String.fromCharCode((c&0x3F)|0x80));
			else if(c<=0x10FFFF)
				utf.push(String.fromCharCode((c>>18)|0xF0),String.fromCharCode(((c>>12)&0x3F)|0x80),String.fromCharCode(((c>>6)&0x3F)|0x80),String.fromCharCode((c&0x3F)|0x80));
			else
				utf.push("\xEF\xBF\xBD");	//invalid Unicode 5.0 codepoint
		}
		return utf.join('');
	},
	dec:function(utf){
		var str=[],c=[];
		for(var i=0,l=utf.length;i<l;){
			c[0]=utf.charCodeAt(i++);
			if(c[0]<=0x7F){
				str.push(String.fromCharCode(c[0]));
			}else if(c[0]<0xC2){
				str+='\uFFFD';	//invalid UTF-8 code string
			}else{
			c[1]=utf.charCodeAt(i++);
			if(c[0]<0xE0){
				str.push(String.fromCharCode(((c[0]&0x1F)<<6)|(c[1]&0x3F)));
			}else{
			c[2]=utf.charCodeAt(i++);
			if(c[0]<0xF0){
				str.push(String.fromCharCode(((c[0]&0x0F)<<12)|((c[1]&0x3F)<<6)|(c[2]&0x3F)));
			}else{
			c[3]=utf.charCodeAt(i++);
			if(c[0]<0xF4){
				str.push(String.fromCharCode(((c[0]&0x07)<<18)|((c[1]&0x3F)<<12)|((c[2]&0x3F)<<6)|(c[3]&0x3F)));
			}else{
				str.push('\uFFFD');	//invalid UTF-8 code string
			}}}}
		}
		return str.join('');
	}
};

/*< FX >*/
F.FX={
	table_cross_hover:function(tbl,cls,vcls){
		setTimeout(function(){F.FX.$table_xy(tbl);},300);
		F.FX.$table_x_hover(tbl,cls,vcls);
	},
	scroll_to:function(o){
		var x=0,y=0,o=$(o).get(0);
		while(o!=null){
			x+=o.offsetLeft;
			y+=o.offsetTop;
			o=o.offsetParent;
		}
		window.scrollTo(x,y);
	},
	$table_xy:function(tbl) {
		var cells=[],rb=0;
		$(tbl.tBodies).each(function(){
			var rs=this.rows;
			$(rs).each(function(ri){
				//initialization
				cells[rb+ri]=[];
			}).each(function(ri){
				var cs=rs[ri].cells,cb=0;
				$(cs).each(function(ci){
					//find the correct cell coordinates
					while(cells[rb][cb])++cb;
					$(cs[ci]).data({row:rb,col:cb});
					//handle row/colmun spans
					var rspan=cs[ci].rowSpan||1,cspan=cs[ci].colSpan||1;
					for(var rj=0;rj<rspan;++rj){
						for(var cj=0;cj<cspan;++cj){
							F.assert(!cells[rb+rj][cb+cj],"Invalid table layout @ ("+(rb+rj)+','+(cb+cj)+')');
							cells[rb+rj][cb+cj]=cs[ci];
						}
					}
				});
				//validation
				F.assert(cells[rb].length==cells[0].length,
					"Incomplete/excess table row @ ("+rb+','+cells[rb].length+"): "+$(cells[rb]).parent().html());
				for(;cb<cells[0].length;++cb){
					F.assert(cells[rb][cb],"Missing table cell @ ("+rb+","+cb+")");
					var o=$(cells[rb][cb]);
					F.assert((o.data("row")<=rb)&&(o.data("col")<=cb),
						"Invalid table row/column @ ("+rb+','+cb+')');
				}
				++rb;
			});
		});
		$(tbl).data({cells:cells});
	},
	$table_x_hover:function(tbl,cls,vcls){
		//table cell hover effect
		var E=function(e,act){
			//detect table structure
			var cells=$(tbl).data("cells");
			if(!cells)return true;
			var o=$(e.target);
			if(null==o.data("row"))o=$(e.relatedTarget);
			if(null==o.data("row"))return true;
			var rb=o.data("row"),cb=o.data("col");
			//handle row/column spans
			var rspan=cells[rb][cb].rowSpan||1,cspan=cells[rb][cb].colSpan||1;
			for(var ri=0;ri<rspan;++ri)for(var ci in cells[rb+ri])$(cells[rb+ri][ci])[act](cls);
			for(var ri in cells)for(var ci=0;ci<cspan;++ci)$(cells[ri][cb+ci])[act](vcls);
			e.stopPropagation();
			return false;
		};
		$(tbl.tBodies)
		.mouseover(function(e){E(e,"addClass");})
		.mouseout(function(e){E(e,"removeClass");})
		.mouseleave(function(e){
			//make sure all hover effects are cleared
			var cells=$(tbl).data("cells");
			if(!cells)return true;
			for(var ri in cells)$(cells[ri]).removeClass(cls).removeClass(vcls);
			e.stopPropagation();
			return false;
		});
	}
};
