window.GE = {};

/**
 * event函数集合
 * 
 * @class event对象
 */
function EV(){}

/**
 * 检查字符串是否是合法的格式
 *
 * @param  {string}strEmail  Email地址
 * @return {Boolean}         如果Email地址错误返回空字符串
 */
function fIsEmailAddr(sEmail){
	return /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(sEmail);
}

/**
 * 检查字符串的日期格式是否正确
 *
 * @param  {date}sDate  要验证的日期 
 * @return {Boolean}    日期格式是否合法，合法的格式为yyyy-MM-dd 
 */
function fCheckDate(sDate){
  var aStr = sDate.split("-");
  var oDate = new Date(aStr[0],aStr[1]-1,aStr[2]);
  if(oDate.getFullYear() != aStr[0] || (oDate.getMonth()+1) != parseInt(aStr[1],10) || oDate.getDate() != parseInt(aStr[2],10)){
	return false;
  }
  return true;
}

/**
 * 去掉字符串两边的空格
 *
 * @param   void   
 * @return {String} 去掉两边空格后的字符串    
 */
function fTrim(){
  return this.replace(/(^\s*)|(\s*$)/g, "");
}

/**
 * 去掉字符串左边的空格 
 *
 * @param   void   
 * @return {String} 去掉左边空格后的字符串    
 */
function fLtrim(){
  return this.replace(/(^\s*)/g, "");
}

/**
 * 去掉字符串右边的空格
 *
 * @param   void   
 * @return {String} 去掉右边空格后的字符串    
 */
function fRtrim(){
  return this.replace(/(\s*$)/g, "");
}

/**
 * 去掉字符串的所有空格
 *
 * @param   void   
 * @return {String} 去掉所有空格后的字符串    
 */
function fCleanBlank(){
  return this.replace( /\s/g, "");
}

/**
 * 第几个字符的位置
 *
 * @param  {String}sSearch  需要匹配的目的字符串  
 * @return {Number}nIndex  第几个目的字符串  
 * example 查找 "abcdeabcde" 中第二个b的位置
 */
function fPosIndexOf(sSearch, nIndex){
	var aStr = this.split(sSearch);
	if(aStr.length - 1 < nIndex){
		return -1;
	}else{
		var len = 0;
		for(var i=0; i<nIndex; i++){
			len += aStr[i].length;
			len += sSearch.length;
		}
		return len - sSearch.length;
	}
}

/**
 * 取字符串长度,包括中文字的长度，一个中文字符长度为2
 *
 * @param   void  
 * @return {Number}  字符串的长度    
 */
function fLen(){
  var nCNLenth = 0;
  var nLenth = this.length;
  for (var i=0; i<nLenth; i++){
	if(this.charCodeAt(i)>255){
	  nCNLenth += 2; 
	}else{
	  nCNLenth++;
	}
  }
  return nCNLenth;
}

/**
 * 删除标签字符串
 *
 * @param   void
 * @return {String}   删除标签后的字符串 
 */
function fStripTags(){
	return this.replace(/<\/?[^>]+>/gi, '');
};

/**
 * html编码
 *
 * @param   void
 * @return {String}   编码后的html代码
 */
function fEscapeHTML(){
	var oDiv = document.createElement('div');
	var oText = document.createTextNode(this);
	oDiv.appendChild(oText);
	return oDiv.innerHTML;
};

/**
 * html解码
 *
 * @param   void
 * @return {String}  解码后的html代码  
 */
function fUnescapeHTML(){
	var oDiv = document.createElement('div');
	oDiv.innerHTML = this.stripTags();
	return oDiv.childNodes[0].nodeValue;
};

/**
 * html编码
 */
function fEncodeHTML(){
	var s = this;
	s = s.replace(/\&/gi,"&amp;");
	s = s.replace(/</gi,"&lt;");
	s = s.replace(/>/gi,"&gt;");
	s = s.replace(/\"/gi,"&quot;");
	return s;
}

/**
 * html解码
 */
function fDecodeHTML(){
	var s = this;
	s = s.replace(/&lt;/gi,"<");
	s = s.replace(/&gt;/gi,">");
	s = s.replace(/&quot;/gi,"\"");
	s = s.replace(/&amp;/gi,"&");
	s = s.replace(/&nbsp;/gi," ");
	return s;
}

/**
 * url＆html解码
 *
 * @param   void  
 * @return {String}  解码后的url＆html字符串  
 */
function fUsc_hdc(){
  return unescape(this.unescapeHTML());
}

/**
 * 截取字符串左边n位
 *
 * @param  {number}n  要截取的位数 
 * @return {String}   截取的字符串 
 */
function fLeft(nLen){
  var i = 0;
  var j = 0;
  if(this.len() <= nLen){
	return this;
  }
  while(j < nLen){
	if(this.charCodeAt(i)>255){
	  j += 2;
	}else{
	  j ++;
	}
	i ++;
  }
  return this.substring(0,i) + "..";
}

/**
 * 日期变量增加n天
 *
 * @param  {number}n  要增加的天数,小于0表示减n天  
 * @return {Date}     增加了n天后的日期    
 */
function fDateAdd(n){
  return new Date(this.valueOf()+n*3600*24*1000);
};

/**
 * 格式化日期为 yyyy-MM-dd hh:mm:ss
 *
 * @param  {void}  
 * @return {String}
 *          返回字符串日期
 */
function fDateFormat(bOnlyDate){
	var nYear = this.getFullYear();
	var nMonth = this.getMonth() + 1;
	var nDate = this.getDate();
	var nHour = this.getHours();
	var nMinute = this.getMinutes();
	var nSecond = this.getSeconds();
	// 格式化时间
	var sTime = nYear + (nMonth<10 ? '-0' : '-')+ nMonth + (nDate<10 ? '-0' : '-')+ nDate + 
			   (nHour<10 ? ' 0' : ' ')+ nHour + (nMinute<10 ? ':0' : ":")+ nMinute + (nSecond<10 ? ':0' : ':')+ nSecond;
	if(bOnlyDate){
		return sTime.split(" ")[0];
	}
	// 返回时间
	return sTime
}

/**
 * 获取事件的触发元素
 * Returns the event's target element
 * @param {Event} ev Event对象
 * @param {boolean} resolveTextNode when set to true the target's
 *                  parent will be returned if the target is a 
 *                  text node
 * @return {HTMLElement} 返回事件的触发元素，如果是文本则返回父元素
 */
function fGetTarget(ev, resolveTextNode){
	if(!ev) ev = this.getEvent();
	var t = ev.target || ev.srcElement;

	if (resolveTextNode && t && "#text" == t.nodeName) {
		return t.parentNode;
	} else {
		return t;
	}
}

/**
 * 增加事件监视并cache下来
 * @param {element}oElement 事件的对象
 * @param {string}name 事件
 * @param {function}observer 绑定事件的函数
 * @param {bool}useCapture
 * @return {void}
 */
function fObserveAndCache(oElement, name, observer, useCapture) {
	if (!this.observers){
		this.observers = [];
	}
	if (oElement.addEventListener) {
		this.observers.push([oElement, name, observer, useCapture]);
		oElement.addEventListener(name, observer, useCapture);
	}else if(oElement.attachEvent) {
		this.observers.push([oElement, name, observer, useCapture]);
		oElement.attachEvent('on' + name, observer);
	}
}

/**
 * 获取event对象
 * @param {Event} event对象
 * @return {Event} event对象
 */
function fGetEvent (e) {
	var ev = e || window.event || top.event;
	
	if (!ev) {
		var aCaller = [];
		var c = this.getEvent.caller;
		while (c) {
			ev = c.arguments[0];
			if (ev && Event == ev.constructor) {
				break;
			}
			
			var b = false;
			for(var i=0;i<aCaller.length;i++){
				if(c == aCaller[i]){
					b = true;
					break;
				}
			}
			if(b){
				break;
			}else{
				aCaller.push(c);
			}
			c = c.caller;
		}
	}

	return ev;
}

/**
 * 阻止事件冒泡触发以及阻止默认事件触发
 * @param {Event} ev event对象
 * @return {void}
 */
function fStopEvent(ev) {
	if(!ev){
		ev = this.getEvent();
	}
	this.stopPropagation(ev);
	this.preventDefault(ev);
}

/**
 * 阻止事件冒泡触发
 * @param {Event} ev event对象
 * @return {void}
 */
function fStopPropagation(ev) {
	if(!ev){
		ev = this.getEvent();
	}
	if (ev.stopPropagation) {
		ev.stopPropagation();
	}else{
		ev.cancelBubble = true;
	}
}

/**
 * 阻止默认事件触发
 * @param {Event} ev event对象
 * @return {void}
 */
function fPreventDefault(ev) {
	if(!ev){
		ev = this.getEvent();
	}
	if (ev.preventDefault) {
		ev.preventDefault();
	} else {
		ev.returnValue = false;
	}
}

/**
 * 事件触发鼠标x坐标
 * @param {event}ev event对象
 * @return {number}返回x坐标
 */
function fPointerX(ev) {
	if(!ev){
		ev = this.getEvent();
	}
	var doc = document;
	return ev.pageX || (ev.clientX +
	  (doc.documentElement.scrollLeft || doc.body.scrollLeft));
}

/**
 * 事件触发鼠标x坐标
 * @param {event}ev event对象
 * @return {number}返回y坐标
 */
function fPointerY(ev) {
	if(!ev){
		ev = this.getEvent();
	}
	var doc = document;
	return ev.pageY || (ev.clientY +
	  (doc.documentElement.scrollTop || doc.body.scrollTop));
}

/**
 * 增加事件监视
 * @param {element}oElement 事件的对象
 * @param {string}name 事件
 * @param {function}observer 绑定事件的函数
 * @param {bool}useCapture
 * @return {void}
 */
function fObserve(oElement, name, observer, useCapture) {
	useCapture = useCapture || false;
	
	if (name == 'keypress' &&
		(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
		|| oElement.attachEvent)){
	  name = 'keydown';
	}
	
	this.observeAndCache(oElement, name, observer, useCapture);
}

/**
 * 移除事件监视
 * @param {element}oElement 事件的对象
 * @param {string}name 事件
 * @param {function}observer 绑定事件的函数
 * @param {bool}useCapture
 * @return {void}
 */
function fStopObserving(oElement, name, observer, useCapture) {
	useCapture = useCapture || false;
	
	if (name == 'keypress' &&
		(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
		|| oElement.detachEvent)){
	  name = 'keydown';
	}

	if (oElement.removeEventListener) {
	  oElement.removeEventListener(name, observer, useCapture);
	} else if (oElement.detachEvent) {
	  oElement.detachEvent('on' + name, observer);
	}
}

/**
 * 遍历数组
 * @param  {function} func 遍历需要调用的函数 
 * @return {void}
 */
function fArrayEach(func){
	for(var i=0,m=this.length;i<m;i++){
		try{
			func(i, this[i]);
		}catch(exp){
			if(exp == $break){
				break;
			}else if(exp == $continue){
				continue;
			}else{
				throw exp;
			}
		}
	}
}

/**
 * 遍历数组
 * @param  {object} value 判断数组中是否含有某个值 
 * @return {boolean} 
 */
function fArrayHas(value){
	var r = false;
	this.each(function(i, o){
		if(o == value){
			r = true;
			throw $break;
		}
	});
	return r;
};

/**
 * 转换成字符串
 */
function fArrayToString(sSplitter){
	if(!sSplitter){
		sSplitter = "";
	}
	return this.join(sSplitter);
}

/**
 * 检查当前是否真是onmouseover状态
 * @private
 * @param  {object}o 绑定事件的对象
 * @param  {object}e 事件
 * @return {bool} 返回状态     
 */
function fCheckIsOnMouseOver(o, e){
	try{
		var e = e || EV.getEvent();
		var to = e.toElement || e.target;
		var from = e.fromElement || e.relatedTarget;
		if(El.descendantOf(to, o) && !El.descendantOf(from, o)){
			return true;
		}else{
			return false;
		}
	}catch(exp){
		return true;
	}
}
/**
 * 检查当前是否真是onmouseout状态
 * @private
 * @param  {object}o 绑定事件的对象
 * @param  {object}e 事件
 * @return {bool} 返回状态     
 */
function fCheckIsOnMouseOut(o, e){
	try{
		var e = e || EV.getEvent();
		var to = e.toElement || e.relatedTarget;
		var from = e.fromElement || e.target;
		// var div = El.createElement("div");div.innerHTML = to.outerHTML.encodeHTML();document.body.appendChild(div);
		if(!El.descendantOf(to, o) && El.descendantOf(from, o)){
			return true;
		}else{
			return false;
		}
	}catch(exp){
		return true;
	}
}

/**
 * 使对象可以拖动
 * @param {object}obj 触发拖动的对象
 * @param {object}oMoveObj 被拖动的对象
 */
function fSetDragable(obj, oMoveObj){
	var oDoc = top.document;
	var fSelectEvent = function(){return false};
	var fObjMoving = function(e){
		var ev = e || top.event;		// 获取event对象
		if(oDoc.all && ev.button != 1){
			obj.onmouseup();
			return;
		}
		var x = EV.pointerX(ev);	// 获取鼠标坐标
		var y = EV.pointerY(ev);	
		var x1 = x - oMoveObj.x;
		var y1 = y - oMoveObj.y;
		oMoveObj.style.left = parseInt(oMoveObj.style.left, 10) + x1 + "px";	// 设置位置
		oMoveObj.style.top = parseInt(oMoveObj.style.top, 10) + y1 + "px";
		oMoveObj.x = x;
		oMoveObj.y = y;
		// $$("spnTest").innerHTML = "x:"+ oMoveObj.style.left +" -- y:" + oMoveObj.style.top;
	};
	obj.onmousedown = function(e){
		var ev = e || top.event;	// 获取event对象
		var x = EV.pointerX(ev);	// 获取鼠标坐标
		var y = EV.pointerY(ev);
		oMoveObj.x = x;
		oMoveObj.y = y;
		EV.observe(oDoc, "mousemove", fObjMoving);		// 添加oDoc onmousemove事件
		EV.observe(oDoc, "selectstart", fSelectEvent);	// 添加oDoc selectstart事件
	};
	obj.onmouseup = function(){
		EV.stopObserving(oDoc, "mousemove", fObjMoving);
		EV.stopObserving(oDoc, "selectstart", fSelectEvent);
		var oMaskItem = $("maskItem");
		if(oMaskItem){
			oMaskItem.style.display = "none";
		}
	};
}

// 检查文件名是否合法
function fCheckFolderName(sName){
	var sMsg = "";
	if(sName == ""){
		sMsg = "文件夹名称不能为空!";
	}
	if(sName.length > 12){
		sMsg = "长度不能大于12个字符!";
	}
	if(/[,%\'\"\/\\;|\<\>\&\.\*]/.test(sName)){
		sMsg = "请不要输入 ＂, % \' \" \\ \/ ；|<>&.*＂ 等特殊字符。";
	}
	try{
		if(top.frames.folder.aFolderId.has(sName)){
			sMsg = "同名文件夹已存在。";
		}
	}catch(exp){}
	return sMsg;
}
/**
 * 取本地cookie
 * @param   {String}sName cookie的name
 * @return  {void}
 */
function fGetCookie(sName) {
   var sSearch = sName + "=";
   if(document.cookie.length > 0) {
	  var sOffset = document.cookie.indexOf(sSearch);
	  if(sOffset != -1) {
		 sOffset += sSearch.length;
		 end = document.cookie.indexOf(";", sOffset);
		 if(end == -1) end = document.cookie.length;
		 return unescape(document.cookie.substring(sOffset, end));
	  }
	  else return "";
   }
}
function fRemoveById(sId){
	var o = document.getElementById(sId);
	if(o){
		o.parentNode.removeChild(o);
	}
}
function replace(eC,aKB){
	if(eC){
		return this.aFe(eC);
	}else{
		return this.toString();
	}
}
function aFe(eC){
	if(!this.Mc){
		this.Mc=this.anj.split(this.apM);
		this.arh=this.Mc.concat();
	}

	var aaZ=this.Mc,
	Ym=this.arh;

	for(var i=1,aP=aaZ.length;i<aP;i+=2){
		Ym[i]=eC[aaZ[i]];
	}

	return Ym.join("");
}
/**
 * 对象坐标
 * @param {element}oElement 坐标的对象
 * @return {array}返回坐标数组
 */
function fPosition(oElement) {
	if(!oElement){
		var oElement = this;
	}
	var nT = 0, nL = 0;
	do {
	  nT += oElement.offsetTop  || 0;
	  nL += oElement.offsetLeft || 0;
	  oElement = oElement.offsetParent;
	} while (oElement);
	return [nL, nT];
}
/**
 * 浏览器类型
 * @param   void
 * @return  {void}
 */
function fGetUserAgen(){
	GE.isIE = document.all ? true : false;
	var sUserAgent = window.navigator.userAgent.toLowerCase();
	var sAppName = "";
	var sVersion = "";
	if(/msie/.test(sUserAgent)){// IE
		sAppName   = "msie";
	}else if(/firefox/.test(sUserAgent)){// ff
		sAppName = "firefox";
	}else if(/netscape/.test(sUserAgent)){// netscape
		sAppName = "netscape";
	}else if(/opera/ig.test(sUserAgent)){// opera
		sAppName = "opera";
	}else if(/chrome/ig.test(sUserAgent)){// chrome
		sAppName = "chrome";
		sVersion = "0";
	}else if(/safari/ig.test(sUserAgent)){// safari
		sAppName = "safari";
		sVersion = "0";
	}
	var match = /(webkit)[ \/]([\w.]+)/.exec( sUserAgent ) ||
			/(opera)(?:.*version)?[ \/]([\w.]+)/.exec( sUserAgent ) ||
			/(msie) ([\w.]+)/.exec( sUserAgent ) ||
			!/compatible/.test( sUserAgent ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( sUserAgent ) ||
		  	[];
	GE.gAppName = sAppName; // 浏览器名称
	GE.gVersion = isNaN(match[2] - 0) ? match[2] : (match[2] - 0); // 版本号
	GE.gAppType = match[1];	// 类型
	if(sAppName == "msie" && sVersion >= 5 && sVersion < 5.5){
		GE.isIE5 = true;
	}
}

/**
 * 引用外部js
 * @method  getScript
 * @param {string} sUrl:外部js的url
 * @param {string} sCharset:编码
 * @return {boolean} 返回是否请求
 * @for CommonControl
 */
function fCommonGetScript(sUrl, sCharset){ 
	var oJs = document.createElement("script");
	oJs.setAttribute("src", sUrl);
	oJs.setAttribute("charset", sCharset || "utf-8");
	oJs.setAttribute("type", "text/javascript");
  var oHead = document.getElementsByTagName('HEAD').item(0);   
	oHead.appendChild(oJs);
	return true;
}
/**
 * 附加外部js
 * @method  AppendJsBySource
 * @param {source: js文本}
 */
function AppendJsBySource(source){  
  var oHead = document.getElementsByTagName('HEAD').item(0); 
  var oScript = document.createElement("script"); 
  oScript.language = "javascript";
  oScript.setAttribute("charset","utf-8");
  oScript.type = "text/javascript";
  oScript.text = source;
  oHead.appendChild(oScript);
};


fGetUserAgen();



    /**
 * 清除输入框的内容
 *
 * @param  {void}  输入框对象, 参数没有数量限制
 * @return {void}
 */
function fClear() {
	var nLength = arguments.length;
	for (var i = 0; i < nLength; i++){
		$(arguments[i]).value = '';
	}
}

/**
 * 激活对象
 *
 * @param  {object}element 需要激活的对象
 * @return {void}
 */
function fActivate(oElement) {
  try{
	$(oElement).focus();
	$(oElement).select();
  }catch(exp){}
}

/**
 * 在indexFrame里建立对象
 *
 * @param {string}tag  要建立的对象的标签
 * @param {string}sId  要建立的对象的id
 * @param {object}     返回根据标签建立的对象
 */
function fCreateElement(sTag, sId){
	if(sId){
		var o = $(sId);
		if(o){
			El.remove(o);
		}
	}
	var oElement = document.createElement(sTag);
	if(sId){
		oElement.id = sId;
	}
	return oElement;
}

/**
 * 网盘文件ico index 获取
 * @param {fname : 网盘文件全名}
 * @return  {index : css索引号}
 */
function fGetFileIconIndex(fname){
  var sName = fname;
  var sIco = (sName.replace(/(.|\n)*\./,"ext.")).toLowerCase();
  return GE.fileIco[sIco];
}

/**
 * 网盘文件contentType获取
 * @param {fname : 网盘文件全名}
 * @return  {contentType}
 */
function fGetFileContentType(fname){
  var sName = fname;
  var suffix = (sName.replace(/(.|\n)*\./,"ext.")).toLowerCase();
  return GE.contentType[suffix];
}
/**
 * 返回是否是图片文件
 * @param   {string}	sName	文件名
 * @param   {string}	sIndex	文件图标编号
 * @return  {boolean}	是否是图片文件
 */
function fWpIsPic(sName){
	sIndex = fGetFileIconIndex(sName);
	if(sIndex == "31" || sIndex == "32" || sIndex == "33" || sIndex == "34"){
		return true;
	}
	return false;
}

/**
 * 返回文件中心图片文件
 */
function fGotoFileCenter(){
  WebmailHelper.evalScript("top.CC.goFileCenter('file_center')");
};
/**
 * 返回是否支持试听
 * @param   {string}	sName	文件名
 * @return  {boolean}	是否支持在线试听
 * IE mp3,wav,wma,mid
 * 非IE mp3,wav,swf
 */
function fWpIsMedia(sName){
	var sIco = (sName.replace(/(.|\n)*\./,"ext.")).toLowerCase();
	if(GE.isIE){
		if(sIco == "ext.mp3" || sIco == "ext.wav" || sIco == "ext.wma" || sIco == "ext.mid" || sIco == "ext.midi"){
			return true;
		}
	}else{
		if(sIco == "ext.mp3" || sIco == "ext.wav" || sIco == "ext.swf"){
			return true;
		}
	}
	return false;
}

/**
 * 返回是否支持在线预览
 * @param   {string}	sName	文件名
 * @param   {string}	sIndex	文件图标编号
 * @return  {boolean}	是否支持在线预览
 */
function fCommonIsPrev(sName,sIndex){
	if(sName){
		var sIco = (sName.replace(/(.|\n)*\./,"ext.")).toLowerCase();
		sIndex = GE.fileIco[sIco];	
	}
	if(sIndex == "2" || sIndex == "3" || 
  		sIndex == "4" || sIndex == "6" || 
        sIndex == "7" || sIndex == "10" || 
         sIndex == "11" || sIndex == "12" || 
           sIndex == "17" || sIndex == "22" ||
             sIndex == "31" || sIndex == "32" || sIndex == "33" || sIndex == "34"){
		return true;
	}
	return false;
}

// helper
function BindThis(me, fun) {
    return function() {
      return fun.apply(me, arguments);
    };
};
function CompleteCall(fun, result) {
  if (fun === undefined) {
    return;
  } else if (typeof fun === "function") {
    return fun(result);
  }
};

function	ConvertFileSize(filesize) {
    if (filesize < 0)
      file_size = "0";
    else if (filesize < 1024)
      file_size = filesize + "B";
    else if (filesize < 1024 * 1024)
      file_size = (filesize / 1024).toFixed(2).replace(/\.*0+$/ig, "") + "K";
    else if (filesize < 1024 * 1024 * 1024)
      file_size = (filesize / (1024 * 1024)).toFixed(2).replace(/\.*0+$/ig, "") + "M";
    else
      file_size = (filesize / (1024 * 1024 * 1024)).toFixed(2).replace(/\.*0+$/ig, "") + "G";

    return file_size;
  };

//现在文件、目录的创建时间、更新是都是毫秒+6位的，相当于ns了
function ConvertFromEpoch(epoch){
     epoch = epoch /1000000;
  	var curDate =new Date;
    	var date = new Date(epoch);
     if(curDate.getFullYear() == date.getFullYear()){
    		return (date.getMonth() + 1) + "月" + date.getDate() + "日";
     }
     return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
  };

function memofix(memo){
  	if(memo){
    		return '-' + memo;
    }
    return memo;
  };
/**
 * html编码
 */
function EncodeHTML(sHtml){
  if(!sHtml)return '';
	return sHtml.replace(/\&/gi,"&amp;").replace(/</gi,"&lt;").replace(/>/gi,"&gt;").replace(/\"/gi,"&quot;");
}


/**
 * async generated object wrapper template
 * @param produce affect the usage of async generated object from fetch function below, which store in variable me
 *        in case the object is native object, return it dirctly (see AsyncCall)
 *        in case the object is a function which can be used to get a dynamic value lately, call it an return it's return value (see AsyncCallDymanic)
 *        other complex transformation can be done too
 */
var ___AsyncCallTemplate = function(produce) {
  /**
   * this one is the generated object wrapper, which is used to queue calls rely on the very object
   * @param fetch is the async function which used to generate the very object asynchronously
   *        fetch take a callback as parameter, which will receive the object after it's generated
   */
  return function(fetch) {
    var me;
    var cached_action;
    var fetch_state;
    var do_fetch = function() {
      var state = {
        done: false,
        cancel: false
      };
      fetch(function(result) {
        if (!state.cancel) {
          me = result;
          state.done = true;
          if (cached_action) {
          	 for(var j=0;j<cached_action.length;j++){
             	 cached_action[j](produce(me));
             }
            cached_action = undefined;
          }
        }
      });
      return state;
    };
    /**
     * this is the wrapper generated queued-base async-call on the fetched object
     * @param action can be three kind of values listed below
     *          string value "reset", which is used to reset the cached value
     *          function type value, which will be invoke on fetched object
     *          undefined/other value, which will return the fetched object directly
     *          notice that if the object fetching process is not yet done, undefined may be return.
     *          caller must be responsed to check the response value for sanity
     */
    return function(action) {
      if (typeof(action) === "string") {
        if (action === "reset") {
          if (fetch_state) {
            fetch_state.cancel = true;
          }
          me = undefined;
          fetch_state = do_fetch();
        }
      }
      if (typeof(action) === "function") {
        if (me) {
          action(produce(me));
        } else {
          if (cached_action !== undefined) {
            cached_action.push(action);
            return;
          }

          cached_action = [];
          cached_action.push(action);

          fetch_state = do_fetch();
        }
      } else {
        return me;
      }
    };
  };
};

var AsyncCall = ___AsyncCallTemplate(function(me) {
  return me;
});
var AsyncCallDymanic = ___AsyncCallTemplate(function(me) {
  return me();
}); 

/*
 * jQuery JSON Plugin
 * version: 2.1 (2009-08-14)
 *
 * This document is licensed as free software under the terms of the
 * MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org 
 * website's http://www.json.org/json2.js, which proclaims:
 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 * I uphold.
 *
 * It is also influenced heavily by MochiKit's serializeJSON, which is 
 * copyrighted 2005 by Bob Ippolito.
 */ 
(function($) {
    /** jQuery.toJSON( json-serializble )
        Converts the given argument into a JSON respresentation.

        If an object has a "toJSON" function, that will be used to get the representation.
        Non-integer/string keys are skipped in the object, as are keys that point to a function.

        json-serializble:
            The *thing* to be converted.
     **/
    $.toJSON = function(o)
    {
        if (typeof(JSON) == 'object' && JSON.stringify)
            return JSON.stringify(o);
        
        var type = typeof(o);
    
        if (o === null)
            return "null";
    
        if (type == "undefined")
            return undefined;
        
        if (type == "number" || type == "boolean")
            return o + "";
    
        if (type == "string")
            return $.quoteString(o);
    
        if (type == 'object')
        {
            if (typeof o.toJSON == "function") 
                return $.toJSON( o.toJSON() );
            
            if (o.constructor === Date)
            {
                var month = o.getUTCMonth() + 1;
                if (month < 10) month = '0' + month;

                var day = o.getUTCDate();
                if (day < 10) day = '0' + day;

                var year = o.getUTCFullYear();
                
                var hours = o.getUTCHours();
                if (hours < 10) hours = '0' + hours;
                
                var minutes = o.getUTCMinutes();
                if (minutes < 10) minutes = '0' + minutes;
                
                var seconds = o.getUTCSeconds();
                if (seconds < 10) seconds = '0' + seconds;
                
                var milli = o.getUTCMilliseconds();
                if (milli < 100) milli = '0' + milli;
                if (milli < 10) milli = '0' + milli;

                return '"' + year + '-' + month + '-' + day + 'T' +
                             hours + ':' + minutes + ':' + seconds + 
                             '.' + milli + 'Z"'; 
            }

            if (o.constructor === Array) 
            {
                var ret = [];
                for (var i = 0; i < o.length; i++)
                    ret.push( $.toJSON(o[i]) || "null" );

                return "[" + ret.join(",") + "]";
            }
        
            var pairs = [];
            for (var k in o) {
                var name;
                var type = typeof k;

                if (type == "number")
                    name = '"' + k + '"';
                else if (type == "string")
                    name = $.quoteString(k);
                else
                    continue;  //skip non-string or number keys
            
                if (typeof o[k] == "function") 
                    continue;  //skip pairs where the value is a function.
            
                var val = $.toJSON(o[k]);
            
                pairs.push(name + ":" + val);
            }

            return "{" + pairs.join(", ") + "}";
        }
    };

    /** jQuery.evalJSON(src)
        Evaluates a given piece of json source.
     **/
    $.evalJSON = function(src)
    {
        if (typeof(JSON) == 'object' && JSON.parse)
            return JSON.parse(src);
        return eval("(" + src + ")");
    };
    
    /** jQuery.secureEvalJSON(src)
        Evals JSON in a way that is *more* secure.
    **/
    $.secureEvalJSON = function(src)
    {
        if (typeof(JSON) == 'object' && JSON.parse)
            return JSON.parse(src);
        
        var filtered = src;
        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
        
        if (/^[\],:{}\s]*$/.test(filtered))
            return eval("(" + src + ")");
        else
            throw new SyntaxError("Error parsing JSON, source is not valid.");
    };

    /** jQuery.quoteString(string)
        Returns a string-repr of a string, escaping quotes intelligently.  
        Mostly a support function for toJSON.
    
        Examples:
            >>> jQuery.quoteString("apple")
            "apple"
        
            >>> jQuery.quoteString('"Where are we going?", she asked.')
            "\"Where are we going?\", she asked."
     **/
    $.quoteString = function(string)
    {
        if (string.match(_escapeable))
        {
            return '"' + string.replace(_escapeable, function (a) 
            {
                var c = _meta[a];
                if (typeof c === 'string') return c;
                c = a.charCodeAt();
                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
            }) + '"';
        }
        return '"' + string + '"';
    };
    
    var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
    
    var _meta = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    };
})(jQuery);

/*
 * jQuery Templates Plugin 1.0.0pre
 * http://github.com/jquery/jquery-tmpl
 * Requires jQuery 1.4.2
 *
 * Copyright Software Freedom Conservancy, Inc.
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h<m;h++){c=h;k=(h>0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i<j&&!(f=a.data(h[i++],"tmplItem")));if(f&&c)g[2]=function(b){a.tmpl.afterManip(this,b,k)};r.apply(this,g)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var i,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(j(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);i=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(j(c,null,i)):i},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e<p;e++){if((k=o[e]).nodeType!==1)continue;j=k.getElementsByTagName("*");for(h=j.length-1;h>=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery);


/**
*  Base64 encode / decode
**/
var Base64 = {
	// private property
	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
	// public method for encoding
	encode : function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;
		input = Base64._utf8_encode(input);
		while (i < input.length) {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}
			output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
		}
		return output;
	},
 
	// public method for decoding
	decode : function (input) {
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;
		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
		while (i < input.length) {
			enc1 = this._keyStr.indexOf(input.charAt(i++));
			enc2 = this._keyStr.indexOf(input.charAt(i++));
			enc3 = this._keyStr.indexOf(input.charAt(i++));
			enc4 = this._keyStr.indexOf(input.charAt(i++));
			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;
		  output = output + String.fromCharCode(chr1);
			if (enc3 != 64) {
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) {
				output = output + String.fromCharCode(chr3);
			}
		}
		output = Base64._utf8_decode(output);
		return output;
	},
 
	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
		for (var n = 0; n < string.length; n++) {
			var c = string.charCodeAt(n);
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
		}
		return utftext;
	},
 
	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;
		while ( i < utftext.length ) {
			c = utftext.charCodeAt(i);
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
		}
		return string;
	}
};  