/**
 * 动态引用js文件
 * @param  {[type]} url [description]
 * @return {[type]}     [description]
 */
function importJsFile(url) {
	return new Promise((resolve, reject) => {
		var script = document.createElement("script");
	  script.type = "text/javascript";
    if (script.readyState) {
      script.onreadystatechange = function () {
        if (script.readyState == "loaded" || script.readyState == "complete") {
          script.onreadystatechange = null;
          resolve();
        }
      };
    } else {
      script.onload = function () {
        resolve();
      };
    }
	  script.src = url;
	  document.body.appendChild(script);
	});
}

/**
 * 引用ES6模块文件
 * @param  {[type]}   files [description]
 * @param  {Function} cb    [description]
 * @return {[type]}         [description]
 */
async function importModules(files, cb) {
	let arrIn = files;
	let arrOut = [];
	if(typeof(arrIn)=="string") arrIn = [arrIn];
	for(let i=0;i<arrIn.length;i++) {
		arrOut.push(await import(arrIn[i]));
	}
	if(cb) cb(arrOut);
}

/**
 * 获得需要导入的组件类型
 * @return {[type]} [description]
 */
function getNeedImportCptType(tfp, cptTypeName, needImportCptTypes) {
	let metadata = tfp.metaDatas[cptTypeName];
	if(!metadata) return;

	//如果有依赖项，则需要优先加载
	let dependencies = [];
	if(metadata.dependencies) {
		if(typeof(metadata.dependencies)=="string") {
			dependencies.push(metadata.dependencies);
		} else if(Array.isArray(metadata.dependencies)) {
			dependencies.push.apply(dependencies, metadata.dependencies);
		}
	}
	if(dependencies.length>0) {
		for(let i=0;i<dependencies.length;i++) {
			getNeedImportCptType(tfp, dependencies[i], needImportCptTypes);
		}
	}

	if(!needImportCptTypes.includes(cptTypeName)) needImportCptTypes.push(cptTypeName);
}

/**
 * 添加组件相关文件的引用
 * @param  {[type]}   tfp           [description]
 * @param  {[type]}   pageDataModel [description]
 * @param  {[type]}   cptTypes      [description]
 * @param  {Function} cb            [description]
 * @return {[type]}                 [description]
 */
async function importCptFiles(tfp, pageDataModel, cptTypes, cb) {
	//获得所有需要导入的组件类型，包括相关依赖组件
	let needImportCptTypes = [];
	for(let i=0;i<cptTypes.length;i++) {
		getNeedImportCptType(tfp, cptTypes[i], needImportCptTypes);
	}

	for(let i=0;i<needImportCptTypes.length;i++) {
		let cptTypeName = needImportCptTypes[i];
		let metadata = tfp.metaDatas[cptTypeName];
		if(!metadata) continue;
		let metadataFile = "./components/"+cptTypeName.toLowerCase()+"/metadata";
		let controllerFile = "./components/"+cptTypeName.toLowerCase()+"/controller";
		let renderFile = "./components/"+cptTypeName.toLowerCase()+"/render";
		let clientType = pageDataModel.client;
		if(clientType=="tb") clientType = "pc";
		if(!metadata.matchAllClient) {
			metadataFile += "."+clientType;
			controllerFile += "."+clientType;
			renderFile += "."+clientType;
		}
		metadataFile += ".js";
		controllerFile += ".js";
		renderFile += ".js";
		if(!metadata.isLoaded) {
			let matchAllClient = metadata.matchAllClient;
			let isInvisible = metadata.isInvisible;
			let dependencies = null;
			if(metadata.dependencies) dependencies = metadata.dependencies;
			let metaModule = await import(metadataFile);
      
			metadata = metaModule.default;
			metadata.isLoaded = true;
			if(matchAllClient) metadata.matchAllClient = matchAllClient;
			if(isInvisible) metadata.isInvisible = isInvisible;
			if(dependencies) metadata.dependencies = dependencies;
		  tfp.metaDatas[cptTypeName] = metadata;
		}
		if(!tfp.controllers[cptTypeName]) {
			let controllerModule = await import(controllerFile);
			tfp.controllers[cptTypeName] = controllerModule.default;
		}
		if(!tfp.renders[cptTypeName]) {
			let renderModule = await import(renderFile);
			tfp.renders[cptTypeName] = renderModule.default;
		}
		if(typeof(window) != "undefined") {
			let tfpPath = tfp.rootPath+"/src/components/";
			let cssFiles = [];
			if(metadata.cssFiles) {
				if(typeof(metadata.cssFiles)=="string") {
				  cssFiles = [metadata.cssFiles];
				} else if(Array.isArray(metadata.cssFiles)) {
					cssFiles = metadata.cssFiles;
				}
			}
			let bgColorMode = "light";
			if(pageDataModel.bgColorMode=="dark") bgColorMode = "dark";
			for(let j=0;j<cssFiles.length;j++) {
				let styleFile = cssFiles[j];
				if(styleFile.onlyRuntime) {
					if(tfp.isDesigning) continue;
					styleFile = styleFile.src;
				}
				styleFile = styleFile.replace("{client}", clientType);
				styleFile = styleFile.replace("{bgColorMode}", bgColorMode);
				styleFile = tfp.getCptIncludeFile(styleFile, cptTypeName);
				if($("link[href='"+styleFile+"']").length==0) {
					if(styleFile.endsWith(".less")) {
						$("head").append("<link href=\"" + styleFile + "\" rel=\"stylesheet/less\" type=\"text/css\" />");
					} else {
			      $("head").append("<link href=\"" + styleFile + "\" rel=\"stylesheet\" type=\"text/css\" />");
			    }
			  }
		  }
		  let jsFiles = [];
			if(metadata.jsFiles) {
				if(typeof(metadata.jsFiles)=="string") {
				  jsFiles = [metadata.jsFiles];
				} else if(Array.isArray(metadata.jsFiles)) {
					jsFiles = metadata.jsFiles;
				}
			}
			for(let j=0;j<jsFiles.length;j++) {
				let jsFile = jsFiles[j];
				if(jsFile.onlyRuntime) {
					if(tfp.isDesigning) continue;
					jsFile = jsFile.src;
				}
				jsFile = tfp.getCptIncludeFile(jsFile, cptTypeName);
				if($("script[src='"+jsFile+"']").length==0) {
			    await importJsFile(jsFile);
			  }
		  }
	  }
	}
	if(cb) cb();
}

export default class TaskFrontPage {

	constructor() {
		this.metaDatas = {};    //当前页面注册的组件定义信息
		this.controllers = {};      //当前页面注册的组件类
		this.renders = {};      //当前页面注册的组件渲染器类
		this.curPage = null;    //当前页面对象
		this.components = {};   //当前页面的组件集合
		this.cbOpenDialog = null;
		this.rootPath = "/node_modules/tfp";
	}

	/**
	 * 扩展系统对象
	 * @return {[type]} [description]
	 */
	extendSysObj() {
		
		if(typeof(String.prototype.startsWith) == "undefined") {
			/**
			 * 判断字符串是否以指定的子字符串开头
			 * @param  {[type]} prefix [description]
			 * @return {[type]}        [description]
			 */
			String.prototype.startWith = String.prototype.startsWith = function (prefix) {
			  return this.indexOf(prefix) === 0;
			}
		}

		if(typeof(String.prototype.endsWith) == "undefined") {
			/**
			 * 判断字符串是否以指定的子字符串结尾
			 * @param  {[type]} suffix [description]
			 * @return {[type]}        [description]
			 */
			String.prototype.endWith = String.prototype.endsWith = function (suffix) {
			  return this.match(suffix + "$") == suffix;
			}
		}

		if(typeof(String.prototype.replaceAll) == "undefined") {
			/**
			 * 全部替换
			 * @param  {[type]} s1 [description]
			 * @param  {[type]} s2 [description]
			 * @return {[type]}    [description]
			 */
			String.prototype.replaceAll = function (s1, s2) {
			  return this.replace(new RegExp(s1, "gm"), s2);
			}
		}

		if(typeof(String.prototype.trim) == "undefined") {
			/**
			 * 删除字符串两端的空格
			 * @return {[type]} [description]
			 */
			String.prototype.trim = function () {
			  return this.replace(/(^\s*)|(\s*$)/g,"");
			}
		}

		if(typeof(String.prototype.toDate) == "undefined") {
			/**
			 * 将字符串转换为日期
			 * @return {[type]} [description]
			 */
			String.prototype.toDate = function () {
			  return new Date(this.replace(/-/g, '/'));
			}
		}

		if(typeof(Date.prototype.format) == "undefined") {
			/**
			* 将日期格式化为字符串
			* @param  {[type]} style [description]
			* @return {[type]}       [description]
			*/
			Date.prototype.format = function (style) {
				var y = this.getFullYear();
				var M = "0" + (this.getMonth() + 1);
				M = M.substring(M.length - 2);
				var d = "0" + this.getDate();
				d = d.substring(d.length - 2);
				var H = "0" + this.getHours();
				H = H.substring(H.length - 2);
				var h = this.getHours();
				if (h >= 12) h = h - 12;
				h = "0" + h;
				h = h.substring(h.length - 2);
				var m = "0" + this.getMinutes();
				m = m.substring(m.length - 2);
				var s = "0" + this.getSeconds();
				s = s.substring(s.length - 2);
				var ms = "00" + this.getMilliseconds();
				ms = ms.substring(ms.length - 3);
				var ret = (style + "").replaceAll('yyyy', y);
				ret = ret.replaceAll('yy', (y + '').substring(2));
				ret = ret.replaceAll('MM', M);
				ret = ret.replaceAll('dd', d);
				ret = ret.replaceAll('HH', H);
				ret = ret.replaceAll('hh', h);
				ret = ret.replaceAll('mm', m);
				ret = ret.replaceAll('ss', s);
				ret = ret.replaceAll('SSS', ms);
				return ret;
			}
		}


		if(typeof(Date.prototype.toJSON) == "undefined") {
			/**
			* 日期对象转换为JSON值
			* @return {[type]} [description]
			*/
			Date.prototype.toJSON = function () {
				return this.format('yyyy-MM-dd hh:mm:ss');
			}
		}

		if(typeof(Array.prototype.remove) == "undefined") {
			/**
			* 删除数组中指定的元素
			* @param  {[type]} obj [description]
			* @return {[type]}     [description]
			*/
			Array.prototype.remove = function (obj) {
				for (var i = 0; i < this.length; i++) {
					if (this[i] === obj) {
						this.splice(i, 1);
						return;
					}
				}
			}
		}

		if(typeof(Array.prototype.contains) == "undefined") {
			/**
			 * 判断数组中是否存在指定的元素
			 * @param  {[type]} obj [description]
			 * @return {[type]}     [description]
			 */
			Array.prototype.contains = function (obj) {
			  var i = this.length;
			  while (i--) {
			    if (this[i] === obj) {
			        return true;
			    }
			  }
			  return false;
			}
		}

		if(typeof(window.isNull) == "undefined") {
			/**
			 * 是否为空值
			 * @param  {[type]}  val [description]
			 * @return {Boolean}     [description]
			 */
			window.isNull = function(val) {
				return val == null || val == undefined || (typeof val == "string" && val.trim() == "");
			}
		}

		if(typeof(window.isInt) == "undefined") {
			/**
			 * 判断是否是整数
			 * @param  {[type]}  i [description]
			 * @return {Boolean}   [description]
			 */
			window.isInt = function(i) {
				return i == parseInt(i);
			}
		}

		if(typeof(window.isObj) == "undefined") {
			/**
			 * 是否是对象
			 * @param  {[type]}  obj [description]
			 * @return {Boolean}     [description]
			 */
			window.isObj = function(obj) {
				return Object.prototype.toString.call(obj) === '[object Object]';
			}
		}
	}

  /**
   * 是否是设计时
   * @return {Boolean} [description]
   */
	get isDesigning() {
		return typeof window != "undefined" && window.parent 
		  && typeof window.parent.uiDesigner != "undefined";
	}

	/**
   * 是否是在前端浏览器内运行时
   * @return {Boolean} [description]
   */
	get isRuntime() {
		return typeof window != "undefined" && !this.isDesigning;
	}

	/**
	 * 格式化金额
	 * @param  {[type]} s 输入值
	 * @param  {[type]} n 小数点位数
	 * @return {[type]}   返回值
	 */
	formatMoney(s, n) {
		n = n > 0 && n <= 20 ? n : 2;
		s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
		var l = s.split(".")[0].split("").reverse();
		var r = s.split(".")[1];
		var t = "";
		for (var i = 0; i < l.length; i++) {
			t += l[i] + ((i + 1) % 3 === 0 && i + 1 != l.length ? "," : "");
		}
		return t.split("").reverse().join("") + "." + r;
	}

	/**
	 * 格式化日期
	 * @param  {[type]} d 输入值
	 * @param  {[type]} f 格式
	 * @return {[type]}   返回值
	 */
	formatDate(d, f) {
		if (typeof d == "number") return new Date(d).format(f);
		return new Date(Date.parse(d.replace("T", " ").replace("+08:00", " ").replace(/-/g, "/"))).format(f);
	}

	/**
	 * 格式化文件大小
	 * @param  {[type]} bytes [description]
	 * @return {[type]}       [description]
	 */
	formatFileSize(bytes) {
		if (bytes === 0) return '0 B';
		var k = 1000,
		    // or 1024
		sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
		    i = Math.floor(Math.log(bytes) / Math.log(k));
		return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
	}

	/**
	 * 获得样式中的像素值，将px字母删掉，转换为整数
	 * @param  {[type]} value [description]
	 * @return {[type]}       [description]
	 */
	formatPx(value) {
		if(isNull(value)) return "";
		return (value+"").indexOf("px")<0 ? value+"px" : value;
	}

	/**
	 * 获得样式中的像素值，将px字母删掉，转换为整数
	 * @param  {[type]} value [description]
	 * @return {[type]}       [description]
	 */
	getPixel(value) {
		return getPx(value);
	}

	getPx(value) {
		if(isNull(value)) return 0;
		return parseInt(new String(value+"").replace("px", ""));
	}

	/**
	 * 获得URL中的所有参数
	 * @param  {[type]} url [description]
	 * @return {[type]}     [description]
	 */
	getUrlArgs(url) {
		var args = {};
		var start = url.indexOf("?");
		if (start < 0) return args;
		var str = url.substr(start + 1);
		var strArr = str.split("&");
		for (var i = 0; i < strArr.length; i++) {
			var argName = strArr[i].substr(0, strArr[i].indexOf("="));
			var argValue = strArr[i].substr(strArr[i].indexOf("=") + 1);
			args[argName] = argValue;
		}
		return args;
	}

	/**
	 * 获得URL中的指定参数的值
	 * @param  {[type]} argName [description]
	 * @return {[type]}         [description]
	 */
	getUrlArg(argName) {
		var url = window.location.href;
		var start = url.indexOf("?");
		if (start < 0) return null;
		var str = url.substr(start + 1);
		var strArr = str.split("&");
		for (var i = 0; i < strArr.length; i++) {
			var aname = strArr[i].substr(0, strArr[i].indexOf("="));
			if (aname == argName) return decodeURIComponent(strArr[i].substr(strArr[i].indexOf("=") + 1));
		}
		return null;
	}

  /**
   * 替换数据绑定字段
   * @param  {[type]} obj [description]
   * @param  {[type]} str [description]
   * @return {[type]}     [description]
   */
  replaceDataField(obj, str) {
		var start = str.indexOf("{");
		if (start < 0) return str;
		var end = str.indexOf("}", start);
		if (end < 0) return str;
		var fieldStr = str.substr(start + 1, end - start - 1);
		var fieldName = fieldStr;
		var format = "";
		if (fieldStr.indexOf(",") > 0) {
			format = fieldStr.substr(fieldStr.indexOf(",") + 1);
			fieldName = fieldStr.substr(0, fieldStr.indexOf(","));
		}
		if (fieldName in obj && (obj[fieldName] || obj[fieldName] == 0)) {
			if (format == "datetime") {
				str = str.replace("{" + fieldStr + "}", new Date(obj[fieldName]).format("yyyy-MM-dd hh:mm:ss"));
			} else if (format == "date") {
				str = str.replace("{" + fieldStr + "}", new Date(obj[fieldName]).format("yyyy-MM-dd"));
			} else if (format == "time") {
				str = str.replace("{" + fieldStr + "}", new Date(obj[fieldName]).format("hh:mm:ss"));
			} else if ((format.indexOf("\"") >= 0 || format.indexOf("'") >= 0) && (format.indexOf("yy") >= 0 || format.indexOf("MM") >= 0 || format.indexOf("dd") >= 0 || format.indexOf("hh") >= 0 || format.indexOf("ss") >= 0)) {
				format = format.replace(/"/g, "").replace(/'/g, "");
				var dateStr = obj[fieldName] + '';
				//if (dateStr.indexOf("T") < 0) dateStr = dateStr.replace(/-/g, '/');
				str = str.replace("{" + fieldStr + "}", new Date(dateStr).format(format));
			} else if (format == "money") {
				str = str.replace("{" + fieldStr + "}", formatMoney(obj[fieldName], 2));
			} else if (format == "filesize") {
				str = str.replace("{" + fieldStr + "}", formatFileSize(obj[fieldName]));
			} else {
				str = str.replace("{" + fieldStr + "}", (obj[fieldName] + "").replace(/\{/g, "`(`").replace(/\}/g, "`)`"));
			}
		} else {
			str = str.replace("{" + fieldStr + "}", "");
		}
		return this.replaceDataField(obj, str);
	}

  /**
   * 执行表达式
   * @param  {[type]} str [description]
   * @return {[type]}     [description]
   */
	exeExpress(str) {
		var start = str.indexOf("#[");
		if (start < 0) return str.replace(/\`\(\`/g, "{").replace(/\`\)\`/g, "}");
		var end = str.indexOf("]", start);
		if (end < 0) return str;
		var express = str.substr(start + 2, end - start - 2);
		var value = eval(express);
		var newStr = "";
		if (start > 0) newStr += str.substr(0, start);
		newStr += eval(express);
		if (end < str.length - 1) newStr += str.substr(end + 1);
		return this.exeExpress(newStr);
	}

	/**
	 * 在iframe页面内获得顶部窗口对象
	 * @return {[type]} [description]
	 */
	get topWin() {
		let topWin = window;
		if ("undefined" != typeof top && top.location.href != location.href) {
			topWin = top;
		} else if (window.parent) {
			topWin = window.parent;
		}
		return topWin;
	}

	/**
   * 显示提示消息
   * @param  {[type]} msg [description]
   * @return {[type]}     [description]
   */
  showMsg(msg) {
		if (typeof window != "undefined") {
			if (this.topWin.showMsg) {
				this.topWin.showMsg(msg);
			} else {
				alert(msg);
			}
		} else {
			console.log(msg);
		}
	}

	/**
   * 跳转到系统首页
   * @return {[type]} [description]
   */
  gotoHome() {
		if (typeof(this.topWin.gotoHome) != "undefined") {
			this.topWin.gotoHome();
		} else {
			location = "/";
		}
	}

	/**
   * 跳转到指定页面
   * @param  {[type]} url [description]
   * @return {[type]}     [description]
   */
  gotoPage(url) {
		location = url;
	}

	/**
   * 打开指定页面
   * @param  {[type]} title [description]
   * @param  {[type]} url   [description]
   * @return {[type]}       [description]
   */
  openPage(title, url) {
		if (typeof(this.topWin.openPage) != "undefined") {
		  this.topWin.openPage(title, url);
		} else {
			location = url;
		}
	}

	/**
   * 显示指定页面
   * @param  {[type]} id [description]
   * @return {[type]}    [description]
   */
  showPage(id) {
		if (typeof(this.topWin.showPage) != "undefined") {
		  this.topWin.showPage(id);
		}
	}

	/**
   * 隐藏指定页面
   * @param  {[type]} id [description]
   * @return {[type]}    [description]
   */
  hidePage(id) {
		if (typeof(this.topWin.hidePage) != "undefined") {
		  this.topWin.hidePage(id);
		}
	}

	/**
   * 关闭指定页面
   * @param  {[type]} id [description]
   * @return {[type]}    [description]
   */
  closePage(id) {
		if (typeof(this.topWin.closePage) != "undefined") {
		  this.topWin.hidePage(id);
		}
	}

  /**
   * 关闭当前页面
   * @return {[type]} [description]
   */
	closeCurPage() {
		if (typeof(this.topWin.closeCurPage) != "undefined") {
		  this.topWin.hidePage(id);
		}
	}

	/**
   * 打开对话框
   * @param  {[type]} title      [description]
   * @param  {[type]} url        [description]
   * @param  {[type]} icon       [description]
   * @param  {[type]} width      [description]
   * @param  {[type]} height     [description]
   * @param  {[type]} srcElement [description]
   * @param  {[type]} opener     [description]
   * @return {[type]}            [description]
   */
  openDialog(title, url, width, height, args, cb) {
		if (typeof(this.topWin.openDialog) != "undefined") {
			let srcElement = null;
			if(window.event) srcElement = window.event.srcElement;
		  this.topWin.openDialog(title, url, width, height, srcElement, window, args, cb);
		} else {
			location = url;
		}
	}

	/**
   * 关闭对话框
   * @return {[type]} [description]
   */
  closeDialog(id) {
		if (typeof(this.topWin.closeDialog) != "undefined") {
			this.topWin.closeDialog(id);
		}
	}

	/**
   * 关闭当前对话框
   * @return {[type]} [description]
   */
  closeCurDialog(ret) {
		if (typeof(this.topWin.closeCurDialog) != "undefined") {
			if(ret) this.onDialogOK(ret);
			this.topWin.closeCurDialog();
		}
	}

	/**
   * 获得当前对话框
   * @return {[type]} [description]
   */
  getCurDialog() {
		if (typeof(this.topWin.getCurDialog) != "undefined") {
			return this.topWin.getCurDialog();
		}
  	return null;
	}

	/**
	 * 获得当前对话框ID
	 * @return {[type]} [description]
	 */
	getCurDialogId() {
	  if (typeof(this.topWin.getCurDialogId) != "undefined") {
			return this.topWin.getCurDialogId();
		}
  	return null;
	}

	/**
	 * 获得对话框请求参数
	 * @return {[type]} [description]
	 */
	getDialogArgs() {
	  return sessionStorage.getItem("_OpenDialogArgs");
	}

	/**
	 * 点击对话框确定按钮后
	 * @param  {[type]} ret [description]
	 * @return {[type]}     [description]
	 */
	onDialogOK(ret) {
		if (typeof(this.topWin.onDialogOK) != "undefined") {
			this.topWin.onDialogOK(ret);
		}
	}

	/**
   * 获得URL的实际路径
   * @param  {[type]} url [description]
   * @return {[type]}     [description]
   */
  getUrlRealPath(url) {
  	if(!url) return "";
		var realPath = "";
		if (this.curPage.client == "tb") {
			realPath = url;
			if (realPath.indexOf("/") == 0) realPath = ".." + realPath;
		} else {
			if (this.isDesigning && url.indexOf("http:") != 0 && url.indexOf("https:") != 0) {
				var curServer = top.taskBuilder.getCurServer();
				realPath = "http://";
				if (curServer.userHttps) realPath = "https://";
				realPath += curServer.address;
				if (curServer.port) realPath += ":" + curServer.port;
				if (url.indexOf("/") == 0) {
					realPath += url;
				} else {
					var curPath = window.parent.uiDesigner.curTFPFilePath.substring(window.parent.uiDesigner.curTFPFilePath.indexOf("/web/") + 4, window.parent.uiDesigner.curTFPFilePath.lastIndexOf("/") + 1);
					if (url.indexOf("../") == 0) {
						while (url.indexOf("../") == 0) {
							url = url.substring(3);
							if (curPath != "/") curPath = curPath.substring(0, curPath.lastIndexOf("/", curPath.length - 2) + 1);
						}
						realPath += curPath + url;
					} else if (url.indexOf("./") == 0) {
						realPath += curPath + url.substring(2);
					} else {
						realPath += curPath + url;
					}
				}
			} else {
				realPath = url;
			}
		}
		return realPath;
	}

	getCptIncludeFile(_filePath, cptTypeName) {
		let tfpPath = this.rootPath+"/src/components";
		let filePath = _filePath;

    if(filePath.indexOf("http://")!=0 && filePath.indexOf("https://")!=0) {
			if(filePath.indexOf("/")==0) {
				if(this.isDesigning || this.curPage.client=="tb") {
					filePath = ".."+filePath;
				}
			} else if(filePath.indexOf("/")<0) {
				filePath = tfpPath +"/"+ cptTypeName.toLowerCase()+ "/"+filePath;
			} else if(filePath.indexOf("../")==0) {
				filePath = tfpPath +"/"+ filePath.replaceAll("../", "");
			}
		}
		return filePath;
	}

	/**
   * 为URL设置身份验证信息
   * @param {[type]} url [description]
   */
  setUrlAuthData(url) {
		if (!url) return;
		if (typeof top != "undefined" && top.taskMsgAuthObj) {
			return top.taskMsgAuthObj.getAuthUrl(url);
		} else if (typeof window.parent != "undefined" && window.parent.taskMsgAuthObj) {
			return window.parent.taskMsgAuthObj.getAuthUrl(url);
		} else {
			var _auth_org = void 0;
			var _auth_ts = void 0;
			var _auth_data = void 0;
			if (typeof tmClient != "undefined" && (tmClient.clientOSType == "iOS" 
				&& typeof TMiOSClient != "undefined" || tmClient.clientOSType == "Android" 
				&& typeof TMAndroidClient != "undefined")) {
				var authData = JSON.parse(tmClient.getAuthData());
				_auth_org = authData._auth_org;
				_auth_ts = authData._auth_ts;
				_auth_data = authData._auth_data;
			} else {
				if (this.getUrlArg("_auth_org")) _auth_org = this.getUrlArg("_auth_org");
				if (this.getUrlArg("_auth_ts")) _auth_ts = this.getUrlArg("_auth_ts");
				if (this.getUrlArg("_auth_data")) _auth_data = this.getUrlArg("_auth_data");
			}
			if (!_auth_org && !_auth_ts && !_auth_data) return url;
			var ret = url;
			if (ret.indexOf('?') < 0) ret += "?";
			if (_auth_org) {
				if (!ret.endsWith("?")) ret += "&";
				ret += "_auth_org=" + _auth_org;
			}
			if (_auth_ts) {
				if (!ret.endsWith("?")) ret += "&";
				ret += "_auth_ts=" + _auth_ts;
			}
			if (_auth_data) {
				if (!ret.endsWith("?")) ret += "&";
				ret += "_auth_data=" + _auth_data;
			}
			return ret;
		}
	}

	/**
   * 获得指定id的组件
   * @param  {[type]} cptId [description]
   * @return {[type]}       [description]
   */
  get(cptId) {
		return this.components[cptId];
	}

	/**
  * 获得指定类型组件的新ID
  * @param  {[type]} ctype [description]
  * @return {[type]}       [description]
  */
  getNewCptIndex(ctype) {
		var index = 1;
		while (this.components[ctype.substr(0, 1).toLowerCase()+ctype.substr(1) + index]) {
			index++;
		}
		return index;
	}

	/**
   * 使用默认属性和样式创建新的组件
   * @param  {[type]} typeName  [description]
   * @param  {[type]} parent    [description]
   * @return {[type]}           [description]
   */
  new(typeName, parent) {
		if (!typeName) {
			throw new Error("请提供组件类型！");
			return null;
		}
		var cptTypeInfo = this.type(typeName);
		if (!cptTypeInfo) {
			throw new Error("未找到名为[" + typeName + "]的组件类型定义信息！");
			return null;
		}

		var cptClass = this.controllers[typeName];
		if (!cptClass) {
			throw new Error("未找到名为[" + typeName + "]的组件类！");
			return;
		}
		var cpt = new cptClass(this, null, parent);
		if(parent) cpt.render();
		return cpt;
	}

	/**
   * 根据指定的数据模型创建并渲染组件
   * @param  {[type]} dataModel [description]
   * @param  {[type]} parent    [description]
   * @return {[type]}           [description]
   */
  render(dataModel, parent) {
		if (!dataModel) {
			console.error("请提供组件的数据模型！");
			return null;
		}
		if (!dataModel.type) {
			console.error("请提供组件类型！");
			return null;
		}
		if (!parent && dataModel.type != "Page") {
			console.error("请提供父组件！");
			return;
		}
		var cptTypeInfo = this.type(dataModel.type);
		if (!cptTypeInfo) {
			throw new Error("未找到名为[" + dataModel.type + "]的组件类型定义信息！");
			return null;
		}
		var cptClass = this.controllers[dataModel.type];
		if (!cptClass) {
			throw new Error("未找到名为[" + dataModel.type + "]的组件控制器！");
			return;
		}
		
		var cpt = new cptClass(this, dataModel, parent);
		cpt.render();
		return cpt;
	}

	/**
   * 获得组件属性定义
   * @param  {[type]} cptType [description]
   * @param  {[type]} attrName   [description]
   * @return {[type]}          [description]
   */
  attrType(cptType, attrName) {
		if (!cptType.attrs) return null;
		for (var i = 0; i < cptType.attrs.length; i++) {
			if (cptType.attrs[i].name == attrName) return cptType.attrs[i];
		}
		return null;
	}

	/**
   * 获得为组件设置的事件处理函数的名称
   * @param  {[type]} eventSetting [description]
   * @return {[type]}              [description]
   */
  getCptEventFuncName(eventSetting) {
		var eventName = eventSetting;
		if (eventName.indexOf("(") > 0) eventName = eventName.substr(0, eventName.indexOf("("));
		return eventName;
	}

	/**
   * 获得Html标签样式属性名，删掉-号，并把-号后面的一个字母转换为大写
   * @param  {[type]} styleName [description]
   * @return {[type]}           [description]
   */
  getStyleAttrName(styleName) {
		var saName = styleName;
		while (saName.indexOf("-") > 0) {
			saName = saName.substr(0, saName.indexOf("-")) + saName.substr(saName.indexOf("-") + 1, 1).toUpperCase() + saName.substr(saName.indexOf("-") + 2);
		}
		return saName;
	}

	/**
	 * 加载组件分类
	 * @param  {[type]} pageData [description]
	 * @param  {[type]} cb       [description]
	 * @return {[type]}          [description]
	 */
	loadComponentCategorys(pageData, cb) {
		let that = this;
		let clientType = pageData.client;
		if(clientType=="tb") clientType = "pc";
		importModules("./components."+clientType+".js", function(modules) {
			let categorys = modules[0].default;
			for(let i=0;i<categorys.length;i++) {
				let category = categorys[i];
				for(let j=0;j<category.components.length;j++) {
					let cptMetaData = category.components[j];
					cptMetaData.isLoaded = false;
					that.metaDatas[cptMetaData.name] = cptMetaData;
				}
			}
			if(cb) cb(categorys);
		});
	}

	/**
	 * 添加组件相关文件的引用
	 * @param  {[type]}   cptTypes [description]
	 * @param  {Function} cb       [description]
	 * @return {[type]}            [description]
	 */
	use(cptTypes, cb) {
		let arrTypes = cptTypes;
		if(typeof(arrTypes)=="string") arrTypes = [arrTypes];
		importCptFiles(this, this.curPage.dataModel, arrTypes, function() {
			if(cb) cb();
		});
	}

	/**
	 * 解析组件类型
	 * @param  {[type]} dataModel [description]
	 * @param  {[type]} cptTypes  [description]
	 * @return {[type]}           [description]
	 */
	parseCptTypes(dataModel, cptTypes) {
		if(!this.metaDatas[dataModel.type]) return;
		if(dataModel.type!="Page" && !cptTypes.includes(dataModel.type)) 
			cptTypes.push(dataModel.type);
		if(dataModel.components) {
			for(let i=0;i<dataModel.components.length;i++) {
				this.parseCptTypes(dataModel.components[i], cptTypes);
			}
		}
	}

	/**
   * 解析组件数据模型
   * @param  {[type]} dataModel [description]
   * @param  {[type]} parent    [description]
   * @return {[type]}           [description]
   */
  parseCpt(dataModel, parent) {
		if (!dataModel) {
			console.error("请提供组件的数据模型！");
			return null;
		}
		if (!dataModel.type) {
			console.log(dataModel);
			console.error("请提供组件类型！");
			return null;
		}
		if (!parent && dataModel.type != "Page") {
			console.error("请提供父组件！");
			return;
		}
		var cptClass = this.controllers[dataModel.type];
		if (!cptClass) {
			throw new Error("未找到名为[" + dataModel.type + "]的组件控制器！");
			return;
		}
		
		var cpt = new cptClass(this, dataModel, parent);
	  if(dataModel.type=="Page") this.curPage = cpt;
		
		//如果不是TaskBuilder的本地页面，则需要绑定组件的html元素
		//因为TaskBuilder的本地页面在渲染时已经绑定，不需要再绑定
		//而普通tfp页面的HTML代码是由服务器事先编译好的，需要在运行时绑定
		if(this.curPage.client!="tb") {
			//将组件id设置为全局变量并指向组件，以便在代码中访问
			window[cpt.id] = cpt;
			//如果是可视组件，则设置组件的jQuery对象
			if(!cpt.isInvisible) {
				if(cpt.type=="Page") {
					cpt._jqObj = $("body");
				} else {
				  cpt._jqObj = $("#"+cpt.id);
				}
			  if(cpt._jqObj.length>0) cpt.el = cpt._jqObj[0];
		  }
		}

		if(dataModel.components) {
			for(let i=0;i<dataModel.components.length;i++) {
				let cdmChild = dataModel.components[i];
				this.parseCpt(cdmChild, cpt);
			}
		}
	}

	/**
	 * 初始化组件运行时
	 * @param  {[type]} cpt [description]
	 * @return {[type]}     [description]
	 */
	initCptRuntime(cpt) {
		//如果组件定义了初始化运行时的方法，则在此执行
		if(cpt["initRuntime"]) cpt.initRuntime();
		if(cpt.dataModel.components) {
			for(let i=0;i<cpt.dataModel.components.length;i++) {
				let cdmChild = cpt.dataModel.components[i];
				this.initCptRuntime(this.components[cdmChild.id]);
			}
		}
	}

	/**
	 * 运行时解析页面
	 * @param  {[type]} pageData [description]
	 * @return {[type]}          [description]
	 */
	parsePage(pageData) {
    //先解析所有组件并绑定其HTML元素
    this.parseCpt(pageData);
    //再执行各个组件的初始化
    //不能在解析组件时直接执行初始化
    //因为关联的组件可能还未解析，会导致初始化出错
    //例如Grid组件初始化时需要调用关联的Service组件
    //如果该Service组件未解析，则就会出错
    this.initCptRuntime(this.curPage);
	}

	/**
	 * 设计时或编译时加载页面
	 * @param  {[type]} pageData [description]
	 * @return {[type]}          [description]
	 */
	loadPage(pageData, cb) {
		if(!pageData) return;
		pageData.type = "Page";
		let that = this;
		this.isLoadingPage = true;
		this.components = {};
		this.loadComponentCategorys(pageData, function(categorys) {
			if(that.isDesigning) window.parent.uiDesigner.createCptLib(categorys);
			//先解析当前页面都有哪些类型的组件
			let cptTypes = [];
			that.parseCptTypes(pageData, cptTypes);
			//加载TFP页面通用CSS
			$("head").append("<link rel=\"stylesheet\" type=\"text/css\" href=\""
				+that.rootPath+"/src/tfp."+pageData.bgColorMode+".css\"><\/link>");
			//然后动态加载这些组件的元数据和类
			importCptFiles(that, pageData, cptTypes, function() {
				//加载页面引用的CSS
				if(pageData.cssFiles) {
          let cssFiles = pageData.cssFiles;
          if(typeof(cssFiles)=="string") cssFiles = [cssFiles];
          for(let i=0;i<cssFiles.length;i++) {
            $("head").append("<link rel=\"stylesheet\" type=\"text/css\" href=\""+cssFiles[i]+"\"><\/link>");
          }
        }
				that.render(pageData);
				if(that.isDesigning) {
					window.parent.uiDesigner.selectCpt(tfp.curPage.id);
				}
				that.isLoadingPage = false;
				if(cb) cb();
			});
		});
	}

	/**
   * 绑定输入项数据
   * @param  {[type]} cdm  [description]
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
   */
  bindCptData(cdm, data) {
		var cpt = this.get(cdm.id);
		if (!cpt.dataModel.dataBindingFormat) {
			if (cpt.dataModel.components) {
				for (var i = 0; i < cpt.dataModel.components.length; i++) {
					this.bindCptData(cpt.dataModel.components[i], data);
				}
			}
			return;
		}
		var cptVal = null;
		try {
			cptVal = this.replaceDataField(data, cpt.dataModel.dataBindingFormat);
			cptVal = this.exeExpress(cptVal);
		} catch (e) {
			console.log(e);
		}
		if (!isNull(cptVal)) cpt.value = cptVal;
		if (cpt.dataModel.components) {
			for (var i = 0; i < cpt.dataModel.components.length; i++) {
				this.bindCptData(cpt.dataModel.components[i], data);
			}
		}
	}

	/**
	 * 获得组件类型定义信息（元数据）
	 * @param  {[type]} typeName [description]
	 * @return {[type]}          [description]
	 */
	type(typeName) {
		return this.metaDatas[typeName];
	}

	/**
	 * 初始化组件设计时相关设置
	 * 1、组件列表中添加组件节点
	 * 2、为组件设置点击事件处理器
	 * 3、禁止选中
	 * 4、设置鼠标按下和经过事件处理器
	 * @param  {[type]} cpt [description]
	 * @return {[type]}     [description]
	 */
	initCptDesignSetting(cpt) {
		var node = window.parent.$("#cptTreeNode_" + cpt.id);
		if(node.length==0) window.parent.uiDesigner.addCptTreeNode(cpt);

    cpt._jqObj.click(function () {
    	if(cpt.isInvisible) {
			  window.parent.uiDesigner.cptOnClick(window.parent.event, cpt.id);
		  } else {
		  	window.parent.uiDesigner.cptOnClick(window.event, cpt.id);
		  }
		});

		cpt._jqObj.bind('selectstart', function () { return false; });

		if (!cpt.isInvisible) {
			if (this.curPage.positionType == "absolute") {
				//鼠标按下时，准备移动组件
				cpt._jqObj.mousedown(function () {
					window.parent.uiDesigner.cptOnMouseDown(cpt, window.event);
				});
			}

			cpt._jqObj.mouseover(function () {
				window.parent.uiDesigner.cptOnMouseOver(cpt, window.event);
			});
		}
	}
}