/*******************************************************************************
* KindEditor - WYSIWYG HTML Editor for Internet
*
* @author Roddy <luolonghao@gmail.com>
* @site http://www.kindsoft.net/
* @licence LGPL(http://www.opensource.org/licenses/lgpl-license.php)
* @version 3.4
*******************************************************************************/

var KE = {};

KE.version = '3.4';

KE.lang = {
	source : '切换模式',
	undo : '后退(Ctrl+Z)',
	redo : '前进(Ctrl+Y)',
	cut : '剪切(Ctrl+X)',
	copy : '复制(Ctrl+C)',
	paste : '粘贴(Ctrl+V)',
	plainpaste : '粘贴为无格式文本',
	wordpaste : '从Word粘贴',
	selectall : '全选',
	justifyleft : '左对齐',
	justifycenter : '居中',
	justifyright : '右对齐',
	justifyfull : '两端对齐',
	insertorderedlist : '编号',
	insertunorderedlist : '项目符号',
	indent : '增加缩进',
	outdent : '减少缩进',
	subscript : '下标',
	superscript : '上标',
	title : '标题',
	fontname : '字体',
	fontsize : '文字大小',
	textcolor : '文字颜色',
	bgcolor : '文字背景',
	bold : '粗体',
	italic : '斜体',
	underline : '下划线',
	strikethrough : '删除线',
	removeformat : '删除格式',
	image : '图片',
	flash : '插入Flash',
	media : '插入多媒体',
	table : '插入表格',
	hr : '插入横线',
	emoticons : '插入表情',
	link : '超级连接',
	unlink : '取消超级连接',
	fullscreen : '全屏显示',
	about : '关于',
	print : '打印',
	yes : '确定',
	no : '取消',
	close : '关闭',
	editImage : '图片属性',
	deleteImage : '删除图片',
	editLink : '超级连接属性',
	deleteLink : '取消超级连接',
	noColor : '无填充颜色',
	invalidImg : "请输入有效的URL地址。\n只允许jpg,gif,bmp,png格式。",
	invalidMedia : "请输入有效的URL地址。\n只允许swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb格式。",
	invalidWidth : "宽度必须为数字。",
	invalidHeight : "高度必须为数字。",
	invalidBorder : "边框必须为数字。",
	invalidUrl : "请输入有效的URL地址。",
	pleaseInput : "请输入内容",
	cutError : '您的浏览器安全设置不允许使用剪切操作，请使用快捷键(Ctrl+X)来完成。',
	copyError : '您的浏览器安全设置不允许使用复制操作，请使用快捷键(Ctrl+C)来完成。',
	pasteError : '您的浏览器安全设置不允许使用粘贴操作，请使用快捷键(Ctrl+V)来完成。'
};

KE.scriptPath = (function() {
	var elements = document.getElementsByTagName('script');
	for (var i = 0, len = elements.length; i < len; i++) {
		if (elements[i].src && elements[i].src.match(/kindeditor[\w\-\.]*\.js/)) {
			return elements[i].src.substring(0, elements[i].src.lastIndexOf('/') + 1);
		}
	}
	return "";
})();

KE.browser = (function() {
	var ua = navigator.userAgent.toLowerCase();
	return {
		VERSION: ua.match(/(msie|firefox|webkit|opera)[\/:\s](\d+)/) ? RegExp.$2 : '0',
		IE: (ua.indexOf('msie') > -1 && ua.indexOf('opera') == -1),
		GECKO: (ua.indexOf('gecko') > -1 && ua.indexOf('khtml') == -1),
		WEBKIT: (ua.indexOf('applewebkit') > -1),
		OPERA: (ua.indexOf('opera') > -1)
	};
})();

KE.setting = {
	wyswygMode : true,
	autoOnsubmitMode : true,
	loadStyleMode : true,
	resizeMode : 2,
	filterMode : false,
	urlType : 'relative',
	skinType : 'oxygen',
	newlineTag : 'br',
	cssPath : '',
	skinsPath : KE.scriptPath + 'skins/',
	pluginsPath : KE.scriptPath + 'plugins/',
	minWidth : 200,
	minHeight : 100,
	minChangeSize : 5,
	items : [
		'source', 'fullscreen', 'undo', 'redo', 'print', 'cut', 'copy', 'paste',
		'plainpaste', 'wordpaste', 'justifyleft', 'justifycenter', 'justifyright',
		'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
		'superscript', 'selectall', '-',
		'title', 'fontname', 'fontsize', 'textcolor', 'bgcolor', 'bold',
		'italic', 'underline', 'strikethrough', 'removeformat', 'image',
		'flash', 'media', 'table', 'hr', 'emoticons', 'link', 'unlink', 'about'
	],
	colorTable : [
		["#FFFFFF","#E5E4E4","#D9D8D8","#C0BDBD","#A7A4A4","#8E8A8B","#827E7F","#767173","#5C585A","#000000"],
		["#FEFCDF","#FEF4C4","#FEED9B","#FEE573","#FFED43","#F6CC0B","#E0B800","#C9A601","#AD8E00","#8C7301"],
		["#FFDED3","#FFC4B0","#FF9D7D","#FF7A4E","#FF6600","#E95D00","#D15502","#BA4B01","#A44201","#8D3901"],
		["#FFD2D0","#FFBAB7","#FE9A95","#FF7A73","#FF483F","#FE2419","#F10B00","#D40A00","#940000","#6D201B"],
		["#FFDAED","#FFB7DC","#FFA1D1","#FF84C3","#FF57AC","#FD1289","#EC0078","#D6006D","#BB005F","#9B014F"],
		["#FCD6FE","#FBBCFF","#F9A1FE","#F784FE","#F564FE","#F546FF","#F328FF","#D801E5","#C001CB","#8F0197"],
		["#E2F0FE","#C7E2FE","#ADD5FE","#92C7FE","#6EB5FF","#48A2FF","#2690FE","#0162F4","#013ADD","#0021B0"],
		["#D3FDFF","#ACFAFD","#7CFAFF","#4AF7FE","#1DE6FE","#01DEFF","#00CDEC","#01B6DE","#00A0C2","#0084A0"],
		["#EDFFCF","#DFFEAA","#D1FD88","#BEFA5A","#A8F32A","#8FD80A","#79C101","#3FA701","#307F00","#156200"],
		["#D4C89F","#DAAD88","#C49578","#C2877E","#AC8295","#C0A5C4","#969AC2","#92B7D7","#80ADAF","#9CA53B"]
	],
	noEndTags : ['br', 'hr', 'img', 'area', 'col', 'embed', 'input', 'param'],
	inlineTags : ['b', 'del', 'em', 'font', 'i', 'span', 'strike', 'strong', 'sub', 'sup', 'u'],
	endlineTags : [
		'br', 'hr', 'table', 'tbody', 'td', 'tr', 'th', 'div', 'p', 'ol', 'ul',
		'li', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
	],
	htmlTags : {
		font : ['color', 'size', 'face', '.background-color'],
		span : ['style'],
		div : ['class', 'align', 'style'],
		table: ['class', 'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'style'],
		'td,th': ['class', 'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor', 'style'],
		a : ['class', 'href', 'target', 'name', 'style'],
		embed : ['src', 'width', 'height', 'type', 'loop', 'autostart', 'quality', 'style', 'align', 'allowscriptaccess', '/'],
		img : ['src', 'width', 'height', 'border', 'alt', 'title', 'align', 'style', '/'],
		hr : ['class', '/'],
		br : ['/'],
		'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6' : ['align', 'style'],
		'tbody,tr,strong,b,sub,sup,em,i,u,strike' : []
	},
	mediaTypes : {
		rm : 'audio/x-pn-realaudio-plugin',
		flash : 'application/x-shockwave-flash',
		media : 'video/x-ms-asf-plugin'
	}
};

KE.g = {};

KE.plugin = {};

KE.$ = function(id, doc){
	var doc = doc || document;
	return doc.getElementById(id);
};

KE.$$ = function(name, doc){
	var doc = doc || document;
	return doc.createElement(name);
};

KE.event = {
	add : function(el, event, listener) {
		if (el.addEventListener){
			el.addEventListener(event, listener, false);
		} else if (el.attachEvent){
			el.attachEvent('on' + event, listener);
		}
	},
	remove : function(el, event, listener) {
		if (el.removeEventListener){
			el.removeEventListener(event, listener, false);
		} else if (el.detachEvent){
			el.detachEvent('on' + event, listener);
		}
	},
	input : function(el, func) {
		this.add(el, 'keyup', function(e) {
			if (!e.ctrlKey && !e.altKey && (e.keyCode < 16 || e.keyCode > 18) && e.keyCode != 116) {
				func(e);
				if (e.preventDefault) e.preventDefault();
				if (e.stopPropagation) e.stopPropagation();
				return false;
			}
		});
	},
	ctrl : function(el, key, func) {
		this.add(el, 'keydown', function(e) {
			if (e.ctrlKey && e.keyCode == key.toUpperCase().charCodeAt(0) && !e.shiftKey && !e.altKey) {
				func(e);
				if (e.preventDefault) e.preventDefault();
				if (e.stopPropagation) e.stopPropagation();
				return false;
			}
		});
	},
	ready : function(func, win, doc) {
		var win = win || window;
		var doc = doc || document;
		var loaded = false;
		var readyFunc = function() {
			if (loaded) return;
			loaded = true;
			func();
		};
		if (doc.addEventListener) {
			this.add(doc, "domcontentloaded", readyFunc);
		} else if (doc.attachEvent){
			this.add(doc, "readystatechange", function() {
				if (doc.readyState == "complete") readyFunc();
			});
			if ( doc.documentElement.doScroll && typeof win.frameElement === "undefined" ) {
				var ieReadyFunc = function() {
					if (loaded) return;
					try {
						doc.documentElement.doScroll("left");
					} catch(e) {
						window.setTimeout(ieReadyFunc, 0);
						return;
					}
					readyFunc();
				};
				ieReadyFunc();
			}
		}
		this.add(win, 'load', readyFunc);
	}
};

KE.each = function(obj, func) {
	for (var key in obj) {
		if (obj.hasOwnProperty(key)) func(key, obj[key]);
	}
};

KE.eachNode = function(node, func) {
	var walkNodes = function(parent) {
		if (KE.util.getNodeType(parent) != 1) return true;
		var n = parent.firstChild;
		while (n) {
			var next = n.nextSibling;
			if (!func(n)) return false;
			if (!walkNodes(n)) return false;
			n = next;
		}
		return true;
	};
	walkNodes(node);
};

KE.selection = function(win, doc) {
	this.sel = null;
	this.range = null;
	this.keRange = null;
	this.init = function() {
		var sel = win.getSelection ? win.getSelection() : doc.selection;
		var range;
		try {
			if (sel.rangeCount > 0) range = sel.getRangeAt(0);
			else range = sel.createRange();
		} catch(e) {}
		if (!range) range = KE.util.createRange(doc);
		this.sel = sel;
		this.range = range;
		var startNode, startPos, endNode, endPos;
		if (KE.browser.IE) {
			if (range.item) {
				var el = range.item(0);
				startNode = endNode = el;
				startPos = endPos = 0;
			} else {
				var getStartEnd = function(isStart) {
					var pointRange = range.duplicate();
					pointRange.collapse(isStart);
					var parentNode = pointRange.parentElement();
					var nodes = parentNode.childNodes;
					if (nodes.length == 0) return {node: parentNode, pos: 0};
					var startNode;
					var endElement;
					var startPos = 0;
					var isEnd = false;
					var testRange = range.duplicate();
					testRange.moveToElementText(parentNode);
					for (var i = 0, len = nodes.length; i < len; i++) {
						var node = nodes[i];
						var cmp = testRange.compareEndPoints('StartToStart', pointRange);
						if (cmp > 0) {
							isEnd = true;
						} else if (cmp == 0) {
							if (node.nodeType == 1) {
								var keRange = new KE.range(doc);
								keRange.selectTextNode(node);
								return {node: keRange.startNode, pos: 0};
							} else {
								return {node: node, pos: 0};
							}
						}
						if (node.nodeType == 1) {
							var nodeRange = range.duplicate();
							nodeRange.moveToElementText(node);
							testRange.setEndPoint('StartToEnd', nodeRange);
							if (isEnd) startPos += nodeRange.text.length;
							else startPos = 0;
						} else if (node.nodeType == 3) {
							testRange.moveStart('character', node.nodeValue.length);
							startPos += node.nodeValue.length;
						}
						if (!isEnd) startNode = node;
					}
					if (!isEnd && startNode.nodeType == 1) {
						var startNode = parentNode.lastChild;
						return {node: startNode, pos: startNode.nodeType == 1 ? 1 : startNode.nodeValue.length};
					}
					testRange = range.duplicate();
					testRange.moveToElementText(parentNode);
					testRange.setEndPoint('StartToEnd', pointRange);
					startPos -= testRange.text.replace(/\r\n|\n|\r/g, '').length;
					return {node: startNode, pos: startPos};
				};
				var start = getStartEnd(true);
				var end = getStartEnd(false);
				startNode = start.node;
				startPos = start.pos;
				endNode = end.node;
				endPos = end.pos;
			}
		} else {
			startNode = range.startContainer;
			startPos = range.startOffset;
			endNode = range.endContainer;
			endPos = range.endOffset;
			if (startNode.nodeType == 1 && typeof startNode.childNodes[startPos] != "undefined") {
				startNode = startNode.childNodes[startPos];
				startPos = 0;
			}
			if (endNode.nodeType == 1 && endPos > 0) {
				if (typeof endNode.childNodes[endPos - 1] != "undefined") {
					endNode = endNode.childNodes[endPos - 1];
					endPos = (endNode.nodeType == 1) ? 0 : endNode.nodeValue.length;
				}
			}
		}
		var keRange = new KE.range(doc);
		keRange.setTextStart(startNode, startPos);
		keRange.setTextEnd(endNode, endPos);
		this.keRange = keRange;
	};
	this.init();
	this.addRange = function(keRange) {
		if (KE.browser.GECKO && KE.browser.VERSION < 3) return;
		this.keRange = keRange;
		if (KE.browser.IE) {
			var getEndRange = function(isStart) {
				var range = KE.util.createRange(doc);
				var node = isStart ? keRange.startNode : keRange.endNode;
				if (node.nodeType == 1) {
					range.moveToElementText(node);
					range.collapse(isStart);
				} else if (node.nodeType == 3) {
					range = KE.util.getNodeStartRange(doc, node);
					var pos = isStart ? keRange.startPos : keRange.endPos;
					range.moveStart('character', pos);
				}
				return range;
			}
			if (!this.range.item) {
				var node = keRange.startNode;
				if (node == keRange.endNode && KE.util.getNodeType(node) == 1 && KE.util.getNodeTextLength(node) == 0) {
					var temp = doc.createTextNode(" ");
					node.appendChild(temp);
					this.range.moveToElementText(node);
					this.range.collapse(false);
					this.range.select();
					node.removeChild(temp);
				} else {
					this.range.setEndPoint('StartToStart', getEndRange(true));
					this.range.setEndPoint('EndToStart', getEndRange(false));
					this.range.select();
				}
			}
		} else {
			var getNodePos = function(node) {
				var pos = 0;
				while (node) {
					node = node.previousSibling;
					pos++;
				}
				return --pos;
			};
			var range = new KE.range(doc);
			range.setTextStart(keRange.startNode, keRange.startPos);
			range.setTextEnd(keRange.endNode, keRange.endPos);
			if (KE.util.getNodeType(range.startNode) == 88) {
				this.range.setStart(range.startNode.parentNode, getNodePos(range.startNode));
			} else {
				this.range.setStart(range.startNode, range.startPos);
			}
			if (KE.util.getNodeType(range.endNode) == 88) {
				this.range.setEnd(range.endNode.parentNode, getNodePos(range.endNode) + 1);
			} else {
				this.range.setEnd(range.endNode, range.endPos);
			}
			this.sel.removeAllRanges();
			this.sel.addRange(this.range);
		}
	};
	this.focus = function() {
		if (KE.browser.IE && this.range != null) this.range.select();
	}
};

KE.range = function(doc) {
	this.startNode = null;
	this.startPos = null;
	this.endNode = null;
	this.endPos = null;
	this.getParentElement = function() {
		var scanParent = function(node, func) {
			while (node && (!node.tagName || node.tagName.toLowerCase() != 'body')) {
				node = node.parentNode;
				if (func(node)) return;
			}
		}
		var nodeList = [];
		scanParent(this.startNode, function(node) {
			nodeList.push(node);
		});
		var parentNode;
		scanParent(this.endNode, function(node) {
			if (KE.util.inArray(node, nodeList)) {
				parentNode = node;
				return true;
			}
		});
		return parentNode ? parentNode : doc.body;
	};
	this.getNodeList = function() {
		var self = this;
		var parentNode = this.getParentElement();
		var nodeList = [];
		var isStarted = false;
		if (parentNode == self.startNode) isStarted = true;
		if (isStarted) nodeList.push(parentNode);
		KE.eachNode(parentNode, function(node) {
			if (node == self.startNode) isStarted = true;
			var range = new KE.range(doc);
			range.selectTextNode(node);
			if (range.comparePoints('START_TO_END', self) >= 0) return false;
			if (isStarted) nodeList.push(node);
			return true;
		});
		return nodeList;
	};
	this.comparePoints = function(how, range) {
		var compareNodes = function(nodeA, posA, nodeB, posB) {
			var cmp;
			if (KE.browser.IE) {
				var getStartRange = function(node, pos, isStart) {
					var range = KE.util.createRange(doc);
					var type = KE.util.getNodeType(node);
					if (type == 1) {
						range.moveToElementText(node);
						range.collapse(isStart);
					} else if (type == 3) {
						range = KE.util.getNodeStartRange(doc, node);
						range.moveStart('character', pos);
						range.collapse(true);
					}
					return range;
				}
				var rangeA, rangeB;
				if (how == 'START_TO_START' || how == 'START_TO_END') rangeA = getStartRange(nodeA, posA, true);
				else rangeA = getStartRange(nodeA, posA, false);
				if (how == 'START_TO_START' || how == 'END_TO_START') rangeB = getStartRange(nodeB, posB, true);
				else rangeB = getStartRange(nodeB, posB, false);
				return rangeA.compareEndPoints('StartToStart', rangeB);
			} else {
				var rangeA = KE.util.createRange(doc);
				rangeA.selectNode(nodeA);
				if (how == 'START_TO_START' || how == 'START_TO_END') rangeA.collapse(true);
				else rangeA.collapse(false);
				var rangeB = KE.util.createRange(doc);
				rangeB.selectNode(nodeB);
				if (how == 'START_TO_START' || how == 'END_TO_START') rangeB.collapse(true);
				else rangeB.collapse(false);
				if (rangeA.compareBoundaryPoints(Range.START_TO_START, rangeB) > 0) {
					cmp = 1;
				} else if (rangeA.compareBoundaryPoints(Range.START_TO_START, rangeB) == 0) {
					if (posA > posB) cmp = 1;
					else if (posA == posB) cmp = 0;
					else cmp = -1;
				} else {
					cmp = -1;
				}
			}
			return cmp;
		}
		if (how == 'START_TO_START') return compareNodes(this.startNode, this.startPos, range.startNode, range.startPos);
		if (how == 'START_TO_END') return compareNodes(this.startNode, this.startPos, range.endNode, range.endPos);
		if (how == 'END_TO_START') return compareNodes(this.endNode, this.endPos, range.startNode, range.startPos);
		if (how == 'END_TO_END') return compareNodes(this.endNode, this.endPos, range.endNode, range.endPos);
	};
	this.setTextStart = function(node, pos) {
		var textNode = node;
		KE.eachNode(node, function(n) {
			if (KE.util.getNodeType(n) == 3 && n.nodeValue.length > 0) {
				textNode = n;
				pos = 0;
				return false;
			}
			return true;
		});
		this.setStart(textNode, pos);
	};
	this.setStart = function(node, pos) {
		this.startNode = node;
		this.startPos = pos;
		if (this.endNode === null) {
			this.endNode = node;
			this.endPos = pos;
		}
	};
	this.setTextEnd = function(node, pos) {
		var textNode = node;
		KE.eachNode(node, function(n) {
			if (KE.util.getNodeType(n) == 3 && n.nodeValue.length > 0) {
				textNode = n;
				pos = n.nodeValue.length;
			}
			return true;
		});
		this.setEnd(textNode, pos);
	};
	this.setEnd = function(node, pos) {
		this.endNode = node;
		this.endPos = pos;
		if (this.startNode === null) {
			this.startNode = node;
			this.startPos = pos;
		}
	};
	this.selectNode = function(node) {
		this.setStart(node, 0);
		this.setEnd(node, node.nodeType == 1 ? 0 : node.nodeValue.length);
	};
	this.selectTextNode = function(node) {
		this.setTextStart(node, 0);
		this.setTextEnd(node, node.nodeType == 1 ? 0 : node.nodeValue.length);
	};
	this.extractContents = function(isDelete) {
		isDelete = (isDelete === false) ? false : true;
		var thisRange = this;
		var startNode = this.startNode;
		var startPos = this.startPos;
		var endNode = this.endNode;
		var endPos = this.endPos;
		var extractTextNode = function(node, startPos, endPos) {
			var length = node.nodeValue.length;
			var cloneNode = node.cloneNode(true);
			var centerNode = cloneNode.splitText(startPos);
			centerNode.splitText(endPos - startPos);
			if (isDelete) {
				var center = node;
				if (startPos > 0) center = node.splitText(startPos);
				if (endPos < length) center.splitText(endPos - startPos);
				center.parentNode.removeChild(center);
			}
			return centerNode;
		};
		var isStarted = false;
		var isEnd = false;
		var extractNodes = function(parent, frag) {
			if (KE.util.getNodeType(parent) != 1) return true;
			var node = parent.firstChild;
			while (node) {
				if (node == startNode) isStarted = true;
				if (node == endNode) isEnd = true;
				var nextNode = node.nextSibling;
				var type = node.nodeType;
				if (type == 1) {
					var range = new KE.range(doc);
					range.selectNode(node);
					if (isStarted && range.comparePoints('END_TO_END', thisRange) < 0) {
						var cloneNode = node.cloneNode(true);
						frag.appendChild(cloneNode);
						if (isDelete) {
							node.parentNode.removeChild(node);
						}
					} else {
						var childFlag = node.cloneNode(false);
						frag.appendChild(childFlag);
						if (!extractNodes(node, childFlag)) return false;
					}
				} else if (type == 3) {
					if (isStarted) {
						var textNode;
						if (node == startNode && node == endNode) {
							textNode = extractTextNode(node, startPos, endPos);
							frag.appendChild(textNode);
							return false;
						} else if (node == startNode) {
							textNode = extractTextNode(node, startPos, node.nodeValue.length);
							frag.appendChild(textNode);
						} else if (node == endNode) {
							textNode = extractTextNode(node, 0, endPos);
							frag.appendChild(textNode);
							return false;
						} else {
							textNode = extractTextNode(node, 0, node.nodeValue.length);
							frag.appendChild(textNode);
						}
					}
				}
				node = nextNode;
				if (isEnd) return false;
			}
			return true;
		}
		var parentNode = this.getParentElement();
		var docFrag = parentNode.cloneNode(false);
		extractNodes(parentNode, docFrag);
		return docFrag;
	};
	this.cloneContents = function() {
		return this.extractContents(false);
	};
	this.getText = function() {
		var html = this.cloneContents().innerHTML;
		return html.replace(/<.*?>/g, "");
	};
};

KE.cmd = function(id) {
	this.doc = KE.g[id].iframeDoc;
	this.keSel = KE.g[id].keSel;
	this.keRange = KE.g[id].keRange;
	this.mergeAttributes = function(el, attr) {
		for (var i = 0, len = attr.length; i < len; i++) {
			KE.each(attr[i], function(key, value) {
				if (key.charAt(0) == '.') {
					var jsKey = KE.util.getJsKey(key.substr(1));
					eval('el.style.' + jsKey + ' = value;');
				} else {
					el.setAttribute(key, value);
				}
			});
		}
		return el;
	};
	this.wrapTextNode = function(node, startPos, endPos, element, attributes) {
		var length = node.nodeValue.length;
		var isFull = (startPos == 0 && endPos == length);
		var range = new KE.range(this.doc);
		range.selectTextNode(node.parentNode);
		if (isFull &&
			node.parentNode.tagName == element.tagName &&
			range.comparePoints('END_TO_END', this.keRange) <= 0 &&
			range.comparePoints('START_TO_START', this.keRange) >= 0) {
			this.mergeAttributes(node.parentNode, attributes);
			return node;
		} else {
			var el = element.cloneNode(true);
			if (isFull) {
				var cloneNode = node.cloneNode(true);
				el.appendChild(cloneNode);
				node.parentNode.replaceChild(el, node);
				return cloneNode;
			} else {
				var centerNode = node;
				if (startPos < endPos) {
					if (startPos > 0) centerNode = node.splitText(startPos);
					if (endPos < length) centerNode.splitText(endPos - startPos);
					var cloneNode = centerNode.cloneNode(true);
					el.appendChild(cloneNode);
					centerNode.parentNode.replaceChild(el, centerNode);
					return cloneNode;
				} else {
					if (startPos < length) {
						centerNode = node.splitText(startPos);
						centerNode.parentNode.insertBefore(el, centerNode);
					} else {
						if (centerNode.nextSibling) {
							centerNode.parentNode.insertBefore(el, centerNode.nextSibling);
						} else {
							centerNode.parentNode.appendChild(el);
						}
					}
					return el;
				}
			}
		}
	};
	this.wrap = function(tagName, attributes) {
		var self = this;
		this.keSel.focus();
		var element = KE.$$(tagName, this.doc);
		this.mergeAttributes(element, attributes);
		var keRange = this.keRange;
		var startNode = keRange.startNode;
		var startPos = keRange.startPos;
		var endNode = keRange.endNode;
		var endPos = keRange.endPos;
		var parentNode = keRange.getParentElement();
		var isStarted = false;
		KE.eachNode(parentNode, function(node) {
			if (node == startNode) isStarted = true;
			if (node.nodeType == 1) {
				if (node == startNode && node == endNode) {
					if (KE.util.inArray(node.tagName.toLowerCase(), KE.g[id].noEndTags)) {
						if (startPos > 0) node.parentNode.appendChild(element);
						else node.parentNode.insertBefore(element, node);
					} else {
						node.appendChild(element);
					}
					keRange.selectNode(element);
					return false;
				} else if (node == startNode) {
					keRange.setStart(node, 0);
				} else if (node == endNode) {
					keRange.setEnd(node, 0);
					return false;
				}
			} else if (node.nodeType == 3) {
				if (isStarted) {
					if (node == startNode && node == endNode) {
						var rangeNode = self.wrapTextNode(node, startPos, endPos, element, attributes);
						keRange.selectNode(rangeNode);
						return false;
					} else if (node == startNode) {
						var rangeNode = self.wrapTextNode(node, startPos, node.nodeValue.length, element, attributes);
						keRange.setStart(rangeNode, 0);
					} else if (node == endNode) {
						var rangeNode = self.wrapTextNode(node, 0, endPos, element, attributes);
						keRange.setEnd(rangeNode, rangeNode.nodeType == 1 ? 0 : rangeNode.nodeValue.length);
						return false;
					} else {
						self.wrapTextNode(node, 0, node.nodeValue.length, element, attributes);
					}
				}
			}
			return true;
		});
		this.keSel.addRange(keRange);
	};
	this.getTopParent = function(tagNames, node) {
		var parent = null;
		while (node) {
			node = node.parentNode;
			if (KE.util.inArray(node.tagName.toLowerCase(), tagNames)) {
				parent = node;
			} else {
				break;
			}
		}
		return parent;
	};
	this.splitNodeParent = function(parent, node, pos) {
		var leftRange = new KE.range(this.doc);
		leftRange.selectNode(parent.firstChild);
		leftRange.setEnd(node, pos);
		var leftFrag = leftRange.extractContents();
		parent.parentNode.insertBefore(leftFrag, parent);
		return {left : leftFrag, right : parent};
	};
	this.remove = function(tags) {
		var keRange = this.keRange;
		var startNode = keRange.startNode;
		var startPos = keRange.startPos;
		var endNode = keRange.endNode;
		var endPos = keRange.endPos;
		this.keSel.focus();
		if (keRange.getText().replace(/\s+/g, '') === '') return;
		var tagNames = [];
		KE.each(tags, function(key, val) {
			if (key != '*') tagNames.push(key);
		});
		var startParent = this.getTopParent(tagNames, startNode);
		var endParent = this.getTopParent(tagNames, endNode);
		if (startParent) {
			var startFrags = this.splitNodeParent(startParent, startNode, startPos);
			keRange.setStart(startFrags.right, 0);
			if (startNode == endNode && KE.util.getNodeTextLength(startFrags.right) > 0) {
				keRange.selectNode(startFrags.right);
				var range = new KE.range(this.doc);
				range.selectTextNode(startFrags.left);
				if (startPos > 0) endPos -= range.endNode.nodeValue.length;
				range.selectTextNode(startFrags.right);
				endNode = range.startNode;
			}
		}
		if (endParent) {
			var endFrags = this.splitNodeParent(endParent, endNode, endPos);
			this.keRange.setEnd(endFrags.left, 0);
			if (startParent == endParent) {
				keRange.setStart(endFrags.left, 0);
			}
		}
		var removeAttr = function(node, attr) {
			if (attr.charAt(0) == '.') {
				var jsKey = KE.util.getJsKey(attr.substr(1));
				eval('node.style.' + jsKey + ' = "";');
			} else {
				node.removeAttribute(attr);
			}
		};
		var nodeList = keRange.getNodeList();
		keRange.setTextStart(keRange.startNode, keRange.startPos);
		keRange.setTextEnd(keRange.endNode, keRange.endPos);
		for (var i = 0, length = nodeList.length; i < length; i++) {
			var node = nodeList[i];
			if (node.nodeType == 1) {
				var tagName = node.tagName.toLowerCase();
				if (tags[tagName]) {
					var attr = tags[tagName];
					for (var j = 0, len = attr.length; j < len; j++) {
						if (attr[j] == '*') {
							KE.util.removeParent(node);
							break;
						} else {
							removeAttr(node, attr[j]);
							if (node.attributes.length == 0) {
								KE.util.removeParent(node);
								break;
							} else if (node.attributes[0].name == 'style' && node.attributes[0].value === '') {
								KE.util.removeParent(node);
								break;
							}
						}
					}
				}
				if (tags['*']) {
					var attr = tags['*'];
					for (var j = 0, len = attr.length; j < len; j++) {
						removeAttr(node, attr[j]);
					}
				}
			}
		}
		try {
			this.keSel.addRange(keRange);
		} catch(e) {}
	};
};

KE.format = {
	getUrl : function(url, mode, host, pathname) {
		if (!mode) return url;
		mode = mode.toLowerCase();
		if (!KE.util.inArray(mode, ['absolute', 'relative', 'domain'])) return url;
		host = host || location.protocol + '//' + location.host;
		pathname = pathname || (location.pathname.match(/^(\/.*)\//) ? RegExp.$1 : '');
		if (url.match(/^(\w+:\/\/[^\/]*)/)) {
			if (RegExp.$1 !== host) return url;
		} else if (url.match(/^\w+:/)) {
			return url;
		}
		var getRealPath = function(path) {
			var parts = path.split('/');
			paths = [];
			for (var i = 0, len = parts.length; i < len; i++) {
				var part = parts[i];
				if (part == '..') {
					if (paths.length > 0) paths.pop();
				} else if (part !== '' && part != '.') {
					paths.push(part);
				}
			}
			return '/' + paths.join('/');
		};
		if (url.match(/^\//)) {
			url = host + getRealPath(url.substr(1));
		} else if (!url.match(/^\w+:\/\//)) {
			url = host + getRealPath(pathname + '/' + url);
		}
		if (mode == 'relative') {
			var getRelativePath = function(path, depth) {
				if (url.substr(0, path.length) === path) {
					var arr = [];
					for (var i = 0; i < depth; i++) {
						arr.push('..');
					}
					var prefix = '.';
					if (arr.length > 0) prefix += '/' + arr.join('/');
					if (pathname == '/') prefix += '/';
					return prefix + url.substr(path.length);
				} else {
					if (path.match(/^(.*)\//)) {
						return getRelativePath(RegExp.$1, ++depth);
					}
				}
			};
			url = getRelativePath(host + pathname, 0);
		} else if (mode == 'absolute') {
			if (url.substr(0, host.length) === host) {
				url = url.substr(host.length);
			}
		}
		return url;
	},
	getHtml : function(html, htmlTags, urlType) {
		var isFilter = htmlTags ? true : false;
		var htmlTagHash = {};
		if (isFilter) {
			KE.each(htmlTags, function(key, val) {
				var arr = key.split(',');
				for (var i = 0, len = arr.length; i < len; i++) htmlTagHash[arr[i]] = KE.util.arrayToHash(val);
			});
		}
		var noEndTagHash = KE.util.arrayToHash(KE.setting.noEndTags);
		var inlineTagHash = KE.util.arrayToHash(KE.setting.inlineTags);
		var endlineTagHash = KE.util.arrayToHash(KE.setting.endlineTags);
		html = html.replace(/\r\n|\n|\r/g, '');
		html = html.replace(/<(\/)?(\w+)(.*?)(\/)?>/g, function($0, $1, $2, $3, $4) {
			var startSlash = $1 || '';
			var tagName = $2.toLowerCase();
			var attr = $3 || '';
			var endSlash = $4 || '';
			if (isFilter && typeof htmlTagHash[tagName] == "undefined") return '';
			if (endSlash === '' && typeof noEndTagHash[tagName] != "undefined") endSlash = ' /';
			var nl = '';
			if ((startSlash || endSlash) && typeof endlineTagHash[tagName] != "undefined") nl = "\r\n";
			if (attr !== '') {
				attr = attr.replace(/\s*([^\s]+?)=(".*?"|[^\s]*)/g, function($0, $1, $2) {
					var key = $1.toLowerCase();
					var val = $2 || '';
					if (isFilter) {
						if (key.charAt(0) === "." || (key !== "style" && typeof htmlTagHash[tagName][key] == "undefined")) return ' ';
					}
					if (val === '') {
						val = '""';
					} else {
						if (key === "style") {
							val = val.substr(1, val.length - 2);
							val = val.replace(/\s*([^\s]+?)\s*:(.*?)(;|$)/g, function($0, $1, $2) {
								var k = $1.toLowerCase();
								if (isFilter) {
									if (typeof htmlTagHash[tagName]['style'] == "undefined" && typeof htmlTagHash[tagName]['.' + k] == "undefined") return '';
								}
								var v = KE.util.trim($2.toLowerCase());
								v = KE.util.rgbToHex(v);
								return k + ':' + v + ';';
							});
							val = KE.util.trim(val);
							if (val === '') return '';
							val = '"' + val + '"';
						}
						if (KE.util.inArray(key, ['src', 'href'])) {
							if (val.charAt(0) === '"') {
								val = val.substr(1, val.length - 2);
							}
							val = KE.format.getUrl(val, urlType);
						}
						if (val.charAt(0) !== '"') val = '"' + val + '"';
					}
					return ' ' + key + '=' + val + ' ';
				});
				attr = attr.replace(/\s+(checked|selected|disabled|readonly)(\s+|$)/ig, function($0, $1) {
					var key = $1.toLowerCase();
					if (isFilter) {
						if (key.charAt(0) === "." || typeof htmlTagHash[tagName][key] == "undefined") return ' ';
					}
					return ' ' + key + '="' + key + '"' + ' ';
				});
				attr = KE.util.trim(attr);
				attr = attr.replace(/\s+/g, ' ');
				if (attr) attr = ' ' + attr;
				return '<' + startSlash + tagName + attr + endSlash + '>' + nl;
			} else {
				return '<' + startSlash + tagName + endSlash + '>' + nl;
			}
		});
		var reg = KE.setting.inlineTags.join('|');
		var trimHtml = function(inHtml) {
			var outHtml = inHtml.replace(new RegExp('<(' + reg + ')[^>]*><\\/(' + reg + ')>', 'ig'), function($0, $1, $2) {
				if ($1 == $2) return '';
				else return $0;
			});
			if (inHtml !== outHtml) outHtml = trimHtml(outHtml);
			return outHtml;
		};
		return trimHtml(html);
	}
};

KE.util = {
	getDocumentElement : function(doc) {
		doc = doc || document;
		return (doc.compatMode != "CSS1Compat") ? doc.body : doc.documentElement;
	},
	getDocumentHeight : function(doc) {
		var el = this.getDocumentElement(doc);
		return Math.max(el.scrollHeight, el.clientHeight);
	},
	getDocumentWidth : function(doc) {
		var el = this.getDocumentElement(doc);
		return Math.max(el.scrollWidth, el.clientWidth);
	},
	createTable : function(doc) {
		var table = KE.$$('table', doc);
		table.cellPadding = 0;
		table.cellSpacing = 0;
		table.border = 0;
		return {table: table, cell: table.insertRow(0).insertCell(0)};
	},
	loadStyle : function(path) {
		var link = KE.$$('link');
		link.setAttribute('type', 'text/css');
		link.setAttribute('rel', 'stylesheet');
		link.setAttribute('href', path);
		document.getElementsByTagName("head")[0].appendChild(link);
	},
	inArray : function(str, arr) {
		for (var i = 0; i < arr.length; i++) {if (str == arr[i]) return true;}
		return false;
	},
	trim : function(str) {
		return str.replace(/^\s+|\s+$/g, "");
	},
	getJsKey : function(key) {
		var arr = key.split('-');
		key = '';
		for (var i = 0, len = arr.length; i < len; i++) {
			key += (i > 0) ? arr[i].charAt(0).toUpperCase() + arr[i].substr(1) : arr[i];
		}
		return key;
	},
	arrayToHash : function(arr) {
		var hash = {};
		for (var i = 0, len = arr.length; i < len; i++) hash[arr[i]] = 1;
		return hash;
	},
	escape : function(html) {
		html = html.replace(/&/g, "&amp;");
		html = html.replace(/</g, "&lt;");
		html = html.replace(/>/g, "&gt;");
		html = html.replace(/\xA0/g, "&nbsp;");
		html = html.replace(/\x20/g, " ");
		return html;
	},
	getScrollPos : function() {
		var x, y;
		if (KE.browser.IE || KE.browser.OPERA) {
			var el = this.getDocumentElement();
			x = el.scrollLeft;
			y = el.scrollTop;
		} else {
			x = window.scrollX;
			y = window.scrollY;
		}
		return {x : x, y : y};
	},
	getElementPos : function(el) {
		var x = 0, y = 0;
		if (el.getBoundingClientRect) {
			var box = el.getBoundingClientRect();
			var el = this.getDocumentElement();
			var pos = this.getScrollPos();
			x = box.left + pos.x - el.clientLeft;
			y = box.top + pos.y - el.clientTop;
		} else {
            x = el.offsetLeft;
            y = el.offsetTop;
            var parent = el.offsetParent;
            while (parent) {
                x += parent.offsetLeft;
                y += parent.offsetTop;
                parent = parent.offsetParent;
            }
		}
		return {x : x, y : y};
	},
	getCoords : function(ev) {
		ev = ev || window.event;
		return {
			x : ev.clientX,
			y : ev.clientY
		};
	},
	setOpacity : function(el, opacity) {
		if (typeof el.style.opacity == "undefined") {
			el.style.filter = (opacity == 100) ? "" : "alpha(opacity=" + opacity + ")";
		} else {
			el.style.opacity = (opacity == 100) ? "" : "0." + opacity.toString();
		}
	},
	getIframeDoc : function(iframe) {
		return iframe.contentDocument || iframe.contentWindow.document;
	},
	rgbToHex : function(str) {
		function hex(s) {
			s = parseInt(s).toString(16);
			return s.length > 1 ? s : '0' + s;
		};
		return str.replace(/rgb\s*?\(\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?\)/ig,
			function($0, $1, $2, $3) {
				return '#' + hex($1) + hex($2) + hex($3);
			}
		);
	},
	hashToStr : function(hash) {
		var str = '';
		KE.each(hash, function(key, val) {
			str += key + ":'" + val + "',";
		});
		return str.length > 0 ? str.substr(0, str.length - 1) : '';
	},
	strToHash : function(str) {
		var hash = {};
		var re = /(\w+)\s*:\s*'([^']+)'/g;
		while (re.exec(str)) {
			hash[RegExp.$1] = RegExp.$2;
		}
		return hash;
	},
	createRange : function(doc) {
		return doc.createRange ? doc.createRange() : doc.body.createTextRange();
	},
	getNodeType : function(node) {
		return (node.nodeType == 1 && KE.util.inArray(node.tagName.toLowerCase(), KE.setting.noEndTags)) ? 88 : node.nodeType;
	},
	getNodeTextLength : function(node) {
		var type = KE.util.getNodeType(node);
		if (type == 1) {
			var html = node.innerHTML;
			return html.replace(/<.*?>/ig, "").length;
		} else if (type == 3) {
			return node.nodeValue.length;
		}
	},
	getNodeStartRange : function(doc, node) {
		var range = KE.util.createRange(doc);
		var type = node.nodeType;
		if (type == 1) {
			range.moveToElementText(node);
			return range;
		} else if (type == 3) {
			var offset = 0;
			var sibling = node.previousSibling;
			while (sibling) {
				if (sibling.nodeType == 1) {
					var nodeRange = KE.util.createRange(doc);
					nodeRange.moveToElementText(sibling);
					range.setEndPoint('StartToEnd', nodeRange);
					range.moveStart('character', offset);
					return range;
				} else if (sibling.nodeType == 3) {
					offset += sibling.nodeValue.length;
				}
				sibling = sibling.previousSibling;
			}
			range.moveToElementText(node.parentNode);
			range.moveStart('character', offset);
			return range;
		}
	},
	removeParent : function(parent) {
		if (parent.hasChildNodes) {
			var node = parent.firstChild;
			while (node) {
				var nextNode = node.nextSibling;
				parent.parentNode.insertBefore(node, parent);
				node = nextNode;
			}
		}
		parent.parentNode.removeChild(parent);
	},
	drag : function(id, mousedownObj, moveObj, func) {
		var g = KE.g[id];
		mousedownObj.onmousedown = function(e) {
			e = e || window.event;
			if (e.preventDefault) e.preventDefault();
			if (e.stopPropagation) e.stopPropagation();
			var pos = KE.util.getCoords(e);
			var objTop = parseInt(moveObj.style.top);
			var objLeft = parseInt(moveObj.style.left);
			var objWidth = moveObj.style.width;
			var objHeight = moveObj.style.height;
			if (objWidth.match(/%$/)) objWidth = moveObj.offsetWidth + 'px';
			if (objHeight.match(/%$/)) objHeight = moveObj.offsetHeight + 'px';
			objWidth = parseInt(objWidth);
			objHeight = parseInt(objHeight);
			var mouseTop = pos.y;
			var mouseLeft = pos.x;
			var dragFlag = true;
			var moveListener = function(e) {
				if (dragFlag) {
					var pos = KE.util.getCoords(e);
					var top = pos.y - mouseTop;
					var left = pos.x - mouseLeft;
					func(objTop, objLeft, objWidth, objHeight, top, left);
				}
			};
			var iframePos = KE.util.getElementPos(g.iframe);
			var iframeMoveListener = function(e) {
				if (dragFlag) {
					var pos = KE.util.getCoords(e, g.iframeDoc);
					var top = iframePos.y + pos.y - mouseTop;
					var left = iframePos.x + pos.x - mouseLeft;
					func(objTop, objLeft, objWidth, objHeight, top, left);
				}
			};
			var upListener = function(e) {
				dragFlag = false;
				KE.event.remove(document, 'mousemove', moveListener);
				KE.event.remove(document, 'mouseup', upListener);
				KE.event.remove(g.iframeDoc, 'mousemove', iframeMoveListener);
				KE.event.remove(g.iframeDoc, 'mouseup', upListener);
			};
			KE.event.add(document, 'mousemove', moveListener);
			KE.event.add(document, 'mouseup', upListener);
			KE.event.add(g.iframeDoc, 'mousemove', iframeMoveListener);
			KE.event.add(g.iframeDoc, 'mouseup', upListener);
		};
	},
	resize : function(id, width, height, isCheck, isResizeWidth) {
		isResizeWidth = (typeof isResizeWidth == "undefined") ? true : isResizeWidth;
		var g = KE.g[id];
		if (!g.container) return;
		if (isCheck && (parseInt(width) <= g.minWidth || parseInt(height) <= g.minHeight)) return;
		if (isResizeWidth) g.container.style.width = width;
		g.container.style.height = height;
		if (!g.toolbarTable.offsetHeight) {
			window.setTimeout(function () {
				KE.util.resize(id, width, height, isCheck, isResizeWidth);
			}, 0);
			return;
		}
		var diff = parseInt(height) - g.toolbarTable.offsetHeight - g.bottom.offsetHeight;
		if (diff >= 0) {
			g.textareaTable.style.height = diff + 'px';
			g.iframe.style.height = diff + 'px';
			g.newTextarea.style.height = (((KE.browser.IE && KE.browser.VERSION < 8) || document.compatMode != 'CSS1Compat') ? diff - 2 : diff) + 'px';
		}
	},
	hideLoadingPage : function(id) {
		var stack = KE.g[id].dialogStack;
		var dialog = stack[stack.length - 1];
		dialog.loading.style.display = 'none';
		dialog.iframe.style.display = '';
	},
	showLoadingPage : function(id) {
		var stack = KE.g[id].dialogStack;
		var dialog = stack[stack.length - 1];
		dialog.loading.style.display = '';
		dialog.iframe.style.display = 'none';
	},
	setDefaultPlugin : function(id) {
		var items = [
			'selectall', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', 
			'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript', 
			'superscript', 'bold', 'italic', 'underline', 'strikethrough', 'unlink'
		];
		for (var i = 0; i < items.length; i++) {
			KE.plugin[items[i]] = {
				click : new Function('id', 'KE.util.execCommand(id, "' + items[i] + '", null);')
			};
		}
	},
	getFullHtml : function(id) {
		var html = '<html>';
		html += '<head>';
		html += '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
		html += '<title>KindEditor</title>';
		html += '<link href="' + KE.g[id].skinsPath + 'common/editor.css?ver=' + KE.version + '" rel="stylesheet" type="text/css" />';
		if (KE.g[id].cssPath) {
			html += '<link href="' + KE.g[id].cssPath + '" rel="stylesheet" type="text/css" />';
		}
		html += '</head>';
		html += '<body class="ke-content"></body>';
		html += '</html>';
		return html;
	},
	getMediaType : function(type, url) {
		if (type === null) {
			if (url.match(/\.(rm|rmvb)(\?|$)/i)) return 'rm';
			else if (url.match(/\.(swf|flv)(\?|$)/i)) return 'flash';
			else return 'media';
		}
		return type.toLowerCase();
	},
	getMediaImage : function(id, type, url, width, height, autostart) {
		type = this.getMediaType(type, url);
		var hash = {
			src : url,
			width : width,
			height : height
		};
		if (typeof autostart != 'undefined') hash.autostart = autostart;
		var title = this.hashToStr(hash);
		var style = '';
		if (width > 0) style += 'width:' + width + 'px;';
		if (height > 0) style += 'height:' + height + 'px;';
		var className = 'ke-' + type;
		var html = '<img class="' + className + '" src="' + KE.g[id].skinsPath + 'common/blank.gif" ';
		if (style !== '') html += 'style="' + style + '" ';
		html += 'title="' + title + '" alt="flash" />';
		return html;
	},
	getMediaEmbed : function(id, type, url, width, height, autostart) {
		type = this.getMediaType(type, url);
		var html = '<embed src="' + url + '" ';
		if (width > 0) html += 'width="' + width + '" ';
		if (height > 0) html += 'height="' + height + '" ';
		var mediaType = KE.g[id].mediaTypes[type];
		if (type == 'rm') {
			html += 'type="' + mediaType + '" loop="true" autostart="' + autostart + '" />';
		} else if (type == 'flash') {
			html += 'type="' + mediaType + '" quality="high" />';
		} else {
			html += 'type="' + mediaType + '" loop="true" autostart="' + autostart + '" />';
		}
		return html;
	},
	execGetHtmlHooks : function(id, html) {
		var hooks = KE.g[id].getHtmlHooks;
		for (var i = 0, len = hooks.length; i < len; i++) {
			html = hooks[i](html);
		}
		return html;
	},
	execSetHtmlHooks : function(id, html) {
		var hooks = KE.g[id].setHtmlHooks;
		for (var i = 0, len = hooks.length; i < len; i++) {
			html = hooks[i](html);
		}
		return html;
	},
	getData : function(id) {
		var g = KE.g[id];
		if (!g.wyswygMode) {
			g.iframeDoc.body.innerHTML = g.newTextarea.value;
		}
		var html = this.execGetHtmlHooks(id, g.iframeDoc.body.innerHTML);
		html = html.replace(/^\s*<br[^>]*>\s*$/ig, '');
		if (g.filterMode) {
			return KE.format.getHtml(html, g.htmlTags, g.urlType);
		} else {
			return KE.format.getHtml(html, null, g.urlType);
		}
	},
	getSrcData : function(id) {
		var g = KE.g[id];
		if (!g.wyswygMode) {
			g.iframeDoc.body.innerHTML = g.newTextarea.value;
		}
		return this.execGetHtmlHooks(id, g.iframeDoc.body.innerHTML);
	},
	getPureData : function(id) {
		var data = this.getData(id);
		data = data.replace(/<(?!img|embed).*?>/ig, '');
		data = data.replace(/&nbsp;/ig, ' ');
		return data;
	},
	setData : function(id) {
		if (KE.g[id].srcTextarea) KE.g[id].srcTextarea.value = this.getData(id);
	},
	focus : function(id) {
		var g = KE.g[id];
		if (g.wyswygMode) {
			g.iframeWin.focus();
		} else {
			g.newTextarea.focus();
		}
	},
	click : function(id, cmd) {
		KE.layout.hide(id);
		KE.util.focus(id);
		KE.plugin[cmd].click(id);
	},
	selection : function(id) {
		var g = KE.g[id];
		g.keSel = new KE.selection(g.iframeWin, g.iframeDoc);
		g.keRange = g.keSel.keRange;
		g.sel = g.keSel.sel;
		g.range = g.keSel.range;
	},
	select : function(id) {
		if (KE.browser.IE) KE.g[id].range.select();
	},
	execCommand : function(id, cmd, value) {
		try {
			KE.g[id].iframeDoc.execCommand(cmd, false, value);
		} catch(e) {}
		KE.toolbar.updateState(id);
		KE.history.add(id, false);
	},
	pasteHtml : function(id, html, isStart) {
		var g = KE.g[id];
		var imgStr = '<img id="__ke_temp_tag__" style="display:none;" />';
		if (isStart) html = imgStr + html;
		else html += imgStr;
		if (KE.browser.IE) {
			if (g.sel.type.toLowerCase() == 'control') g.range.item(0).outerHTML = html;
			else g.range.pasteHTML(html);
		} else {
			g.range.deleteContents();
			var frag = g.range.createContextualFragment(html);
			g.range.insertNode(frag);
		}
		var node = KE.$('__ke_temp_tag__', g.iframeDoc);
		var blank = g.iframeDoc.createTextNode('');
		node.parentNode.replaceChild(blank, node);
		g.keRange.selectNode(blank);
		g.keSel.addRange(g.keRange);
	},
	insertHtml : function(id, html) {
		if (html === '') return;
		var g = KE.g[id];
		if (!g.wyswygMode) return;
		if (KE.browser.IE) {
			this.select(id);
			if (g.sel.type.toLowerCase() == 'control') g.range.item(0).outerHTML = html;
			else g.range.pasteHTML(html);
		} else if (KE.browser.GECKO && KE.browser.VERSION < 3) {
			this.execCommand(id, 'inserthtml', html);
			return;
		} else {
			this.pasteHtml(id, html);
		}
		KE.history.add(id, false);
	},
	setFullHtml : function(id, html) {
		html = html.replace(/\r\n|\n|\r/g, '');
		if (!KE.browser.IE && html === '') html = '<br />';
		KE.g[id].iframeDoc.body.innerHTML = KE.util.execSetHtmlHooks(id, html);
	},
	addTabEvent : function(id) {
		KE.event.add(KE.g[id].iframeDoc, 'keydown', function(e) {
			if (e.keyCode == 9) {
				KE.util.selection(id);
				KE.util.insertHtml(id, '　　');
				if (e.preventDefault) e.preventDefault();
				if (e.stopPropagation) e.stopPropagation();
				return false;
			}
		});
	},
	addContextmenuEvent : function(id) {
		var g = KE.g[id];
		if (g.contextmenuItems.length == 0) return;
		KE.event.add(g.iframeDoc, 'contextmenu', function(e){
			KE.util.selection(id);
			var showFlag = false;
			for (var i = 0, len = g.contextmenuItems.length; i < len; i++) {
				var item = g.contextmenuItems[i];
				if (item.cond(id)) {
					showFlag = true;
					break;
				}
			}
			if (showFlag) {
				var menu = new KE.menu({
					id : id,
					event : e,
					type : 'contextmenu'
				});
				for (var i = 0, len = g.contextmenuItems.length; i < len; i++) {
					var item = g.contextmenuItems[i];
					if (item.cond(id)) {
						menu.add(item.text, (function(item) {
							return function() {
								item.click(id, menu);
							};
						})(item));
					}
				}
				menu.show();
				if (e.preventDefault) e.preventDefault();
				if (e.stopPropagation) e.stopPropagation();
				return false;
			}
			return true;
		});
	},
	addNewlineEvent : function(id) {
		var g = KE.g[id];
		if (KE.browser.IE && g.newlineTag.toLowerCase() != 'br') return;
		if (KE.browser.GECKO && KE.browser.VERSION < 3 && g.newlineTag.toLowerCase() != 'p') return;
		if (KE.browser.OPERA) return;
		KE.event.add(g.iframeDoc, 'keydown', function(e) {
			if (e.keyCode != 13 || e.shiftKey || e.ctrlKey || e.altKey) return true;
			KE.util.selection(id);
			var parent = g.keRange.getParentElement();
			var tagName = parent.tagName.toLowerCase();
			if (g.newlineTag.toLowerCase() == 'br') {
				if (!KE.util.inArray(tagName, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li'])) {
					KE.util.pasteHtml(id, '<br />');
					var nextNode = g.keRange.startNode.nextSibling;
					if (KE.browser.IE) {
						if (!nextNode) KE.util.pasteHtml(id, '<br />', true);
					} else if (KE.browser.WEBKIT) {
						if (!nextNode) {
							KE.util.pasteHtml(id, '<br />', true);
						} else {
							var range = new KE.range(g.iframeDoc);
							range.selectNode(nextNode.parentNode);
							range.setStart(nextNode, 0);
							if (range.cloneContents().innerHTML.replace(/<(?!img|embed).*?>/ig, '') === '') {
								KE.util.pasteHtml(id, '<br />', true);
							}
						}
					}
					if (e.preventDefault) e.preventDefault();
					if (e.stopPropagation) e.stopPropagation();
					return false;
				}
			} else {
				if (!KE.util.inArray(tagName, ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'div', 'li'])) {
					KE.util.execCommand(id, 'formatblock', '<P>');
				}
			}
			return true;
		});
	}
};

KE.layout = {
	hide : function(id) {
		var g = KE.g[id];
		g.dialogStack = [];
		g.hideDiv.innerHTML = '';
		g.hideDiv.style.display = 'none';
		g.maskDiv.style.display = 'none';
	}
};

KE.menu = function(arg){
	this.getPos = function() {
		var id = arg.id;
		var x = 0;
		var y = 0;
		if (this.type == 'menu') {
			var obj = KE.g[id].toolbarIcon[arg.cmd];
			var pos = KE.util.getElementPos(obj[0]);
			x = pos.x;
			y = pos.y + obj[0].offsetHeight;
		} else {
			var pos = KE.util.getCoords(arg.event);
			var iframePos = KE.util.getElementPos(KE.g[id].iframe);
			x = pos.x + iframePos.x;
			y = pos.y + iframePos.y + 5;
		}
		return { x : x, y : y };
	};
	this.init = function() {
		this.type = (arg.type && arg.type == 'contextmenu') ? arg.type : 'menu';
		var div = KE.$$('div');
		div.className = 'ke-' + this.type;
		var pos = this.getPos();
		div.style.top = pos.y + 'px';
		div.style.left = pos.x + 'px';
		this.div = div;
	};
	this.init();
	this.add = function(html, event) {
		var self = this;
		var cDiv = KE.$$('div');
		cDiv.className = 'ke-' + this.type + '-noselected';
		cDiv.onmouseover = function() { this.className = 'ke-' + self.type + '-selected'; }
		cDiv.onmouseout = function() { this.className = 'ke-' + self.type + '-noselected'; }
		cDiv.onclick = event;
		cDiv.innerHTML = html;
		this.append(cDiv);
	};
	this.append = function(el) {
		this.div.appendChild(el);
	};
	this.insert = function(html) {
		this.div.innerHTML = html;
	};
	this.hide = function() {
		KE.layout.hide(arg.id);
	};
	this.show = function() {
		this.hide();
		var id = arg.id;
		KE.g[id].hideDiv.style.display = '';
		KE.g[id].hideDiv.appendChild(this.div);
	};
	this.picker = function() {
		var colorTable = KE.g[arg.id].colorTable;
		var table = KE.$$('table');
		table.className = 'ke-picker-table';
		table.cellPadding = 0;
		table.cellSpacing = 0;
		table.border = 0;
		var row = table.insertRow(0);
		var cell = row.insertCell(0);
		cell.colSpan = colorTable[0].length;
		cell.className = 'ke-picker-td-top';
		cell.title = KE.lang['noColor'];
		cell.onmouseover = function() {this.style.borderColor = '#000000'; };
		cell.onmouseout = function() {this.style.borderColor = '#F0F0EE'; };
		cell.onclick = function() { KE.plugin[arg.cmd].exec(arg.id, ''); };
		cell.innerHTML = KE.lang['noColor'];
		for (var i = 0; i < colorTable.length; i++) {
			var row = table.insertRow(i + 1);
			for (var j = 0; j < colorTable[i].length; j++) {
				var cell = row.insertCell(j);
				cell.className = 'ke-picker-td';
				cell.style.backgroundColor = colorTable[i][j];
				cell.title = colorTable[i][j];
				cell.onmouseover = function() {this.style.borderColor = '#000000'; };
				cell.onmouseout = function() {this.style.borderColor = '#F0F0EE'; };
				cell.onclick = new Function('KE.plugin["' + arg.cmd + '"].exec("' + arg.id + '", "' + colorTable[i][j] + '")');
				cell.innerHTML = '&nbsp;';
			}
		}
		this.append(table);
		this.show();
	};
};

KE.dialog = function(arg){
	this.widthMargin = 20;
	this.heightMargin = 90;
	this.zIndex = 19811214;
	this.getPos = function() {
		var id = arg.id;
		var el = KE.util.getDocumentElement();
		var scrollPos = KE.util.getScrollPos();
		var width = arg.width + this.widthMargin;
		var height = arg.height + this.heightMargin;
		var x = Math.round(scrollPos.x + (el.clientWidth - width) / 2);
		var y = Math.round(scrollPos.y + (el.clientHeight - height) / 2);
		x = x < 0 ? 0 : x;
		y = y < 0 ? 0 : y;
		return {x : x, y : y};
	};
	this.hide = function() {
		if (arg.beforeHide) arg.beforeHide();
		var id = arg.id;
		var stack = KE.g[id].dialogStack;
		if (stack[stack.length - 1] != this) return;
		stack.pop();
		KE.g[id].hideDiv.removeChild(this.div);
		if (stack.length < 1) {
			KE.g[id].hideDiv.style.display = 'none';
			KE.g[id].maskDiv.style.display = 'none';
		}
		if (arg.afterHide) arg.afterHide();
		KE.util.focus(id);
	};
	this.show = function() {
		if (arg.beforeShow) arg.beforeShow();
		var self = this;
		var id = arg.id;
		var div = KE.$$('div');
		div.className = 'ke-dialog';
		var stack = KE.g[id].dialogStack;
		if (stack.length > 0) {
			this.zIndex = stack[stack.length - 1].zIndex + 1;
		}
		div.style.zIndex = this.zIndex;
		var pos = this.getPos();
		div.style.width = (arg.width + this.widthMargin) + 'px';
		div.style.height = (arg.height + this.heightMargin) + 'px';
		div.style.top = pos.y + 'px';
		div.style.left = pos.x + 'px';
		var titleDiv = KE.$$('div');
		titleDiv.className = 'ke-dialog-title';
		titleDiv.innerHTML = arg.title;
		var span = KE.$$('span');
		span.className = "ke-toolbar-close";
		span.alt = KE.lang['close'];
		span.title = KE.lang['close'];
		span.onclick = function () { self.hide(); };
		titleDiv.appendChild(span);
		KE.util.drag(id, titleDiv, div, function(objTop, objLeft, objWidth, objHeight, top, left) {
			div.style.top = (objTop + top) + 'px';
			div.style.left = (objLeft + left) + 'px';
		});
		div.appendChild(titleDiv);
		var bodyDiv = KE.$$('div');
		bodyDiv.className = 'ke-dialog-body';
		var loadingTable = KE.util.createTable();
		loadingTable.table.className = 'ke-loading-table';
		loadingTable.table.style.width = arg.width + 'px';
		loadingTable.table.style.height = arg.height + 'px';
		loadingTable.cell.align = 'center';
		loadingTable.cell.vAlign = 'middle';
		var loadingImg = KE.$$('span');
		loadingImg.className = 'ke-loading-img';
		loadingTable.cell.appendChild(loadingImg);
		var iframe = KE.$$('iframe');
		if (arg.useFrameCSS) {
			iframe.className = 'ke-dialog-iframe';
		}
		iframe.width = arg.width + 'px';
		iframe.height = arg.height + 'px';
		iframe.setAttribute("frameBorder", "0");
		iframe.style.display = 'none';
		bodyDiv.appendChild(iframe);
		bodyDiv.appendChild(loadingTable.table);
		div.appendChild(bodyDiv);

		var bottomDiv = KE.$$('div');
		bottomDiv.className = 'ke-dialog-bottom';
		var noButton = null;
		var yesButton = null;
		var previewButton = null;
		if (arg.noButton) {
			noButton = KE.$$('input');
			noButton.className = 'ke-dialog-no';
			noButton.type = 'button';
			noButton.name = 'noButton';
			noButton.value = arg.noButton;
			noButton.onclick = function () {
				self.hide();
				KE.util.select(id);
			};
			bottomDiv.appendChild(noButton);
		}
		if (arg.yesButton) {
			yesButton = KE.$$('input');
			yesButton.className = 'ke-dialog-yes';
			yesButton.type = 'button';
			yesButton.name = 'yesButton';
			yesButton.value = arg.yesButton;
			yesButton.onclick = function() {
				var stack = KE.g[id].dialogStack;
				if (stack[stack.length - 1] == self) {
					KE.plugin[arg.cmd].exec(id);
				}
			};
			bottomDiv.appendChild(yesButton);
		}
		if (arg.previewButton) {
			previewButton = KE.$$('input');
			previewButton.className = 'ke-dialog-preview';
			previewButton.type = 'button';
			previewButton.name = 'previewButton';
			previewButton.value = arg.previewButton;
			previewButton.onclick = function() {
				var stack = KE.g[id].dialogStack;
				if (stack[stack.length - 1] == self) {
					KE.plugin[arg.cmd].preview(id);
				}
			};
			bottomDiv.appendChild(previewButton);
		}
		div.appendChild(bottomDiv);
		KE.g[id].hideDiv.style.display = '';
		KE.g[id].hideDiv.appendChild(div);
		window.focus();
		if (yesButton) yesButton.focus();
		else if (noButton) noButton.focus();
		if (typeof arg.html != "undefined") {
			var dialogDoc = KE.util.getIframeDoc(iframe);
			var html = KE.util.getFullHtml(id, false);
			dialogDoc.open();
			dialogDoc.write(html);
			dialogDoc.close();
			dialogDoc.body.innerHTML = arg.html;
		} else {
			if (typeof arg.file == "undefined") {
				iframe.src = KE.g[id].pluginsPath + arg.cmd + '.html?ver=' + KE.version;
			} else {
				iframe.src = KE.g[id].pluginsPath + arg.file;
			}
		}
		KE.g[id].maskDiv.style.width = KE.util.getDocumentWidth() + 'px';
		KE.g[id].maskDiv.style.height = KE.util.getDocumentHeight() + 'px';
		KE.g[id].maskDiv.style.display = 'block';
		this.iframe = iframe;
		this.loading = loadingTable.table;
		this.noButton = noButton;
		this.yesButton = yesButton;
		this.previewButton = previewButton;
		this.div = div;
		KE.g[id].dialogStack.push(this);
		KE.g[id].dialog = iframe;
		KE.g[id].yesButton = yesButton;
		KE.g[id].noButton = noButton;
		KE.g[id].previewButton = previewButton;
		if (!arg.loadingMode) KE.util.hideLoadingPage(id);
		if (arg.afterShow) arg.afterShow();
	};
};

KE.toolbar = {
	updateState : function(id) {
		var cmdList = [
			'justifyleft', 'justifycenter', 'justifyright',
			'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript','superscript',
			'bold', 'italic', 'underline', 'strikethrough'
		];
		for (var i = 0; i < cmdList.length; i++) {
			var cmd = cmdList[i];
			var state = false;
			try {
				state = KE.g[id].iframeDoc.queryCommandState(cmd);
			} catch(e) {}
			if (state) {
				KE.toolbar.select(id, cmd);
			} else {
				KE.toolbar.unselect(id, cmd);
			}
		}
	},
	isSelected : function(id, cmd) {
		if (KE.plugin[cmd] && KE.plugin[cmd].isSelected) return true;
		else return false;
	},
	select : function(id, cmd) {
		if (KE.g[id].toolbarIcon[cmd]) {
			var a = KE.g[id].toolbarIcon[cmd][0];
			a.className = "ke-icon-selected";
			a.onmouseover = null;
			a.onmouseout = null;
		}
	},
	unselect : function(id, cmd) {
		if (KE.g[id].toolbarIcon[cmd]) {
			var a = KE.g[id].toolbarIcon[cmd][0];
			a.className = "ke-icon";
			a.onmouseover = function(){ this.className = "ke-icon-on"; };
			a.onmouseout = function(){ this.className = "ke-icon"; };
		}
	},
	able : function(id, arr) {
		KE.each(KE.g[id].toolbarIcon, function(cmd, obj) {
			if (!KE.util.inArray(cmd, arr)) {
				var a = obj[0];
				var span = obj[1];
				a.className = 'ke-icon';
				KE.util.setOpacity(span, 100);
				a.onclick = (function(id, cmd) {
					return function() {
						KE.util.click(id, cmd);
					};
				})(id, cmd);
				a.onmouseover = function(){ this.className = "ke-icon-on"; };
				a.onmouseout = function(){ this.className = "ke-icon"; };
			}
		});
	},
	disable : function(id, arr) {
		KE.each(KE.g[id].toolbarIcon, function(cmd, obj) {
			if (!KE.util.inArray(cmd, arr)) {
				var a = obj[0];
				var span = obj[1];
				a.className = 'ke-icon-disabled';
				KE.util.setOpacity(span, 50);
				a.onclick = null;
				a.onmouseover = null;
				a.onmouseout = null;
			}
		});
	},
	create : function(id) {
		var defaultItemHash = KE.util.arrayToHash(KE.setting.items);
		KE.g[id].toolbarIcon = [];
		var tableObj = KE.util.createTable();
		var toolbar = tableObj.table;
		toolbar.className = 'ke-toolbar';
		toolbar.oncontextmenu = function() { return false; };
		toolbar.onmousedown = function() { return false; };
		toolbar.onmousemove = function() { return false; };
		var toolbarCell = tableObj.cell;
		var length = KE.g[id].items.length;
		var cellNum = 0;
		var row;
		for (var i = 0; i < length; i++) {
			var cmd = KE.g[id].items[i];
			if (i == 0 || cmd == '-') {
				var table = KE.util.createTable().table;
				table.className = 'ke-toolbar-table';
				row = table.insertRow(0);
				cellNum = 0;
				toolbarCell.appendChild(table);
				if (cmd == '-') continue;
			}
			var cell = row.insertCell(cellNum);
			cell.hideforcus = true;
			cellNum++;
			var a = KE.$$('a');
			a.className = 'ke-icon';
			a.href = 'javascript:;';
			a.onclick = (function(id, cmd) {
				return function() {
					KE.util.click(id, cmd);
				};
			})(id, cmd);
			a.onmouseover = function(){ this.className = 'ke-icon-on'; };
			a.onmouseout = function(){ this.className = 'ke-icon'; };
			a.hidefocus = true;
			a.title = KE.lang[cmd];
			var span = KE.$$('span');
			if (typeof defaultItemHash[cmd] == 'undefined') {
				span.className = 'ke-common-icon ke-icon-' + cmd;
			} else {
				span.className = 'ke-common-icon ke-common-icon-url ke-icon-' + cmd;
			}
			a.appendChild(span);
			cell.appendChild(a);
			KE.g[id].toolbarIcon[cmd] = [a, span];
			if (KE.toolbar.isSelected(id, cmd)) KE.toolbar.select(id, cmd);
		}
		return toolbar;
	}
};

KE.history = {
	add : function(id, minChangeFlag) {
		var g = KE.g[id];
		var html = KE.util.getSrcData(id);
		if (g.undoStack.length > 0) {
			var prevHtml = g.undoStack[g.undoStack.length - 1];
			if (html == prevHtml) return;
			if (minChangeFlag && Math.abs(html.length - prevHtml.length) < g.minChangeSize) return;
		}
		g.undoStack.push(html);
		g.redoStack = [];
	},
	undo : function(id) {
		var g = KE.g[id];
		if (g.undoStack.length == 0) return;
		var html = KE.util.getSrcData(id);
		g.redoStack.push(html);
		var prevHtml = g.undoStack.pop();
		if (html === prevHtml && g.undoStack.length > 0) {
			prevHtml = g.undoStack.pop();
		}
		g.iframeDoc.body.innerHTML = KE.util.execSetHtmlHooks(id, prevHtml);
		g.newTextarea.value = KE.util.execGetHtmlHooks(id, prevHtml);
	},
	redo : function(id) {
		var g = KE.g[id];
		if (g.redoStack.length == 0) return;
		var html = KE.util.getSrcData(id);
		g.undoStack.push(html);
		var nextHtml = g.redoStack.pop();
		g.iframeDoc.body.innerHTML = KE.util.execSetHtmlHooks(id, nextHtml);
		g.newTextarea.value = KE.util.execGetHtmlHooks(id, nextHtml);
	}
};

KE.remove = function(id, mode) {
	if (!KE.g[id].container) return false;
	mode = (typeof mode == "undefined") ? 0 : mode;
	var container = KE.g[id].container;
	if (mode == 1) {
		document.body.removeChild(container);
	} else {
		var srcTextarea = KE.g[id].srcTextarea;
		srcTextarea.parentNode.removeChild(container);
		if (mode == 0) srcTextarea.style.display = '';
	}
	document.body.removeChild(KE.g[id].hideDiv);
	document.body.removeChild(KE.g[id].maskDiv);
	KE.g[id].container = null;
	KE.g[id].dialogStack = [];
	KE.g[id].contextmenuItems = [];
	KE.g[id].getHtmlHooks = [];
	KE.g[id].setHtmlHooks = [];
};

KE.create = function(id, mode) {
	if (KE.g[id].beforeCreate) KE.g[id].beforeCreate();
	if (KE.browser.IE && KE.browser.VERSION < 7) try { document.execCommand('BackgroundImageCache', false, true); }catch(e){}
	var srcTextarea = KE.$(id) || document.getElementsByName(id)[0];
	mode = (typeof mode == "undefined") ? 0 : mode;
	if (mode == 0 && KE.g[id].container) return;
	var width = KE.g[id].width || srcTextarea.style.width || srcTextarea.offsetWidth + 'px';
	var height = KE.g[id].height || srcTextarea.style.height || srcTextarea.offsetHeight + 'px';
	var tableObj = KE.util.createTable();
	var container = tableObj.table;
	container.className = 'ke-container';
	container.style.width = width;
	container.style.height = height;
	var toolbarOuter = tableObj.cell;
	toolbarOuter.className = 'ke-toolbar-outer';
	var textareaOuter = container.insertRow(1).insertCell(0);
	textareaOuter.className = 'ke-textarea-outer';
	tableObj = KE.util.createTable();
	var textareaTable = tableObj.table;
	textareaTable.className = 'ke-textarea-table';
	var textareaCell = tableObj.cell;
	textareaOuter.appendChild(textareaTable);
	var bottomOuter = container.insertRow(2).insertCell(0);
	bottomOuter.className = 'ke-bottom-outer';
	srcTextarea.style.display = 'none';
	if (mode == 1) document.body.appendChild(container);
	else srcTextarea.parentNode.insertBefore(container, srcTextarea);
	var toolbarTable = KE.toolbar.create(id);
	toolbarOuter.appendChild(toolbarTable);
	var iframe = KE.$$('iframe');
	iframe.className = 'ke-iframe';
	iframe.setAttribute("frameBorder", "0");
	iframe.style.height = 0;
	var newTextarea = KE.$$('textarea');
	newTextarea.className = 'ke-textarea';
	newTextarea.style.display = 'none';
	newTextarea.style.height = 0;
	textareaCell.appendChild(iframe);
	textareaCell.appendChild(newTextarea);
	var bottom = KE.$$('table');
	bottom.className = 'ke-bottom';
	bottom.cellPadding = 0;
	bottom.cellSpacing = 0;
	bottom.border = 0;
	var row = bottom.insertRow(0);
	var bottomLeft = row.insertCell(0);
	bottomLeft.className = 'ke-bottom-left';
	var bottomRight = row.insertCell(1);
	bottomRight.className = 'ke-bottom-right';
	bottomRight.align = 'right';
	bottomRight.vAlign = 'bottom';
	var span = KE.$$('span');
	span.className = 'ke-bottom-right-img';
	bottomRight.appendChild(span);
	bottomOuter.appendChild(bottom);
	var hideDiv = KE.$$('div');
	hideDiv.style.display = 'none';
	var maskDiv = KE.$$('div');
	maskDiv.className = 'ke-mask';
	KE.util.setOpacity(maskDiv, 50);
	document.body.appendChild(hideDiv);
	document.body.appendChild(maskDiv);
	KE.util.setDefaultPlugin(id);
	var iframeWin = iframe.contentWindow;
	var iframeDoc = KE.util.getIframeDoc(iframe);
	iframeDoc.designMode = "On";
	var html = KE.util.getFullHtml(id);
	iframeDoc.open();
	iframeDoc.write(html);
	iframeDoc.close();
	if (!KE.g[id].wyswygMode) {
		newTextarea.value = KE.util.execSetHtmlHooks(id, srcTextarea.value);
		newTextarea.style.display = 'block';
		iframe.style.display = 'none';
		KE.toolbar.disable(id, ['source', 'fullscreen']);
		KE.toolbar.select(id, 'source');
	}
	if (KE.g[id].autoOnsubmitMode) {
		var node = srcTextarea.parentNode;
		while (node) {
			if (node.nodeType == 1 && node.tagName.toLowerCase() == 'form') {
				KE.event.add(node, 'submit', new Function('KE.util.setData("' + id + '")'));
				break;
			}
			node = node.parentNode;
		}
	}
	var hideMenu = function() {
		KE.layout.hide(id);
	};
	var updateToolbar = function () {
		KE.toolbar.updateState(id);
	};
	var addHistory = function () {
		KE.history.add(id, true);
	};
	var selectImageForWebkit = function(e) {
		if (KE.browser.WEBKIT) {
			var target = e.srcElement || e.target;
			if (target.tagName.toLowerCase() == 'img') {
				KE.util.selection(id);
				var range = KE.g[id].keRange;
				range.selectNode(target);
				KE.g[id].keSel.addRange(range);
			}
		}
	};
	KE.event.add(iframeDoc, 'click', selectImageForWebkit);
	KE.event.add(iframeDoc, 'contextmenu', selectImageForWebkit);
	KE.event.add(iframeDoc, 'click', hideMenu);
	KE.event.add(iframeDoc, 'click', updateToolbar);
	KE.event.input(iframeDoc, addHistory);
	KE.event.input(iframeDoc, updateToolbar);
	KE.event.add(newTextarea, 'click', hideMenu);
	KE.event.input(newTextarea, addHistory);
	KE.g[id].container = container;
	KE.g[id].toolbarTable = toolbarTable;
	KE.g[id].textareaTable = textareaTable;
	KE.g[id].iframe = iframe;
	KE.g[id].newTextarea = newTextarea;
	KE.g[id].srcTextarea = srcTextarea;
	KE.g[id].bottom = bottom;
	KE.g[id].hideDiv = hideDiv;
	KE.g[id].maskDiv = maskDiv;
	KE.g[id].iframeWin = iframeWin;
	KE.g[id].iframeDoc = iframeDoc;
	KE.g[id].width = width;
	KE.g[id].height = height;
	KE.util.resize(id, width, height);
	KE.util.drag(id, bottomRight, container, function(objTop, objLeft, objWidth, objHeight, top, left) {
		if (KE.g[id].resizeMode == 2) KE.util.resize(id, (objWidth + left) + 'px', (objHeight + top) + 'px', true);
		else if (KE.g[id].resizeMode == 1) KE.util.resize(id, objWidth + 'px', (objHeight + top) + 'px', true, false);
	});
	KE.util.drag(id, bottomLeft, container, function(objTop, objLeft, objWidth, objHeight, top, left) {
		if (KE.g[id].resizeMode > 0) KE.util.resize(id, objWidth + 'px', (objHeight + top) + 'px', true, false);
	});
	for (var i = 0, len = KE.g[id].items.length; i < len; i++) {
		var cmd = KE.g[id].items[i];
		if (KE.plugin[cmd] && KE.plugin[cmd].init) KE.plugin[cmd].init(id);
	}
	KE.util.addContextmenuEvent(id);
	KE.util.addNewlineEvent(id);
	KE.util.addTabEvent(id);
	window.setTimeout(function() {
		KE.util.setFullHtml(id, srcTextarea.value);
		KE.history.add(id, false);
		if (KE.g[id].afterCreate) KE.g[id].afterCreate();
	}, 0);
};

KE.init = function(args) {
	var g = KE.g[args.id] = args;
	g.config = {};
	g.undoStack = [];
	g.redoStack = [];
	g.dialogStack = [];
	g.contextmenuItems = [];
	g.getHtmlHooks = [];
	g.setHtmlHooks = [];
	KE.each(KE.setting, function(key, val) {
		g[key] = (typeof args[key] == 'undefined') ? val : args[key];
		g.config[key] = g[key];
	});
	if (g.loadStyleMode) KE.util.loadStyle(g.skinsPath + g.skinType + '.css');
}

KE.show = function(args) {
	KE.init(args);
	KE.event.ready(function() { KE.create(args.id); });
};

KE.plugin['about'] = {
	click : function(id) {
		KE.util.selection(id);
		var dialog = new KE.dialog({
			id : id,
			cmd : 'about',
			file : 'about.html?id=' + id + '&ver=' + KE.version,
			width : 300,
			height : 70,
			loadingMode : true,
			title : KE.lang['about'],
			noButton : KE.lang['close']
		});
		dialog.show();
	}
};

KE.plugin['undo'] = {
	init : function(id) {
		KE.event.ctrl(KE.g[id].iframeDoc, 'Z', function(e) {
			KE.plugin['undo'].click(id);
			KE.util.focus(id);
		});
	},
	click : function(id) {
		KE.history.undo(id);
	}
};

KE.plugin['redo'] = {
	init : function(id) {
		KE.event.ctrl(KE.g[id].iframeDoc, 'Y', function(e) {
			KE.plugin['redo'].click(id);
			KE.util.focus(id);
		});
	},
	click : function(id) {
		KE.history.redo(id);
	}
};

KE.plugin['cut'] = {
	click : function(id) {
		try {
			if (!KE.g[id].iframeDoc.queryCommandSupported('cut')) throw 'e';
		} catch(e) {
			alert(KE.lang.cutError);
			return;
		}
		KE.util.execCommand(id, 'cut', null);
	}
};

KE.plugin['copy'] = {
	click : function(id) {
		try {
			if (!KE.g[id].iframeDoc.queryCommandSupported('copy')) throw 'e';
		} catch(e) {
			alert(KE.lang.copyError);
			return;
		}
		KE.util.execCommand(id, 'copy', null);
	}
};

KE.plugin['paste'] = {
	click : function(id) {
		try {
			if (!KE.g[id].iframeDoc.queryCommandSupported('paste')) throw 'e';
		} catch(e) {
			alert(KE.lang.pasteError);
			return;
		}
		KE.util.execCommand(id, 'paste', null);
	}
};

KE.plugin['plainpaste'] = {
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'plainpaste',
			file : 'plainpaste.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 300,
			loadingMode : true,
			title : KE.lang['plainpaste'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	exec : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var html = KE.$('textArea', dialogDoc).value;
		html = KE.util.escape(html);
		html = html.replace(/\r\n|\n|\r/g, "<br />$&");
		KE.util.insertHtml(id, html);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['wordpaste'] = {
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'wordpaste',
			file : 'wordpaste.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 300,
			loadingMode : true,
			title : KE.lang['wordpaste'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	exec : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var wordIframe = KE.$('wordIframe', dialogDoc);
		var str = KE.util.getIframeDoc(wordIframe).body.innerHTML;
		str = str.replace(/<meta(\n|.)*?>/ig, "");
		str = str.replace(/<!(\n|.)*?>/ig, "");
		str = str.replace(/<style[^>]*>(\n|.)*?<\/style>/ig, "");
		str = str.replace(/<script[^>]*>(\n|.)*?<\/script>/ig, "");
		str = str.replace(/<w:[^>]+>(\n|.)*?<\/w:[^>]+>/ig, "");
		str = str.replace(/<xml>(\n|.)*?<\/xml>/ig, "");
		str = str.replace(/\r\n|\n|\r/ig, "");
		str = KE.util.execGetHtmlHooks(id, str);
		str = KE.format.getHtml(str, KE.g[id].htmlTags, KE.g[id].urlType);
		KE.util.insertHtml(id, str);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['fullscreen'] = {
	click : function(id) {
		var g = KE.g[id];
		var self = this;
		var resetSize = function() {
			var el = KE.util.getDocumentElement();
			g.width = el.clientWidth + 'px';
			g.height = el.clientHeight + 'px';
		};
		var windowSize = '';
		var resizeListener = function() {
			if (!self.isSelected) return;
			var el = KE.util.getDocumentElement();
			var size = [el.clientWidth, el.clientHeight].join('');
			if (windowSize != size) {
				windowSize = size;
				resetSize();
				KE.util.resize(id, g.width, g.height);
			}
		}
		if (this.isSelected) {
			this.isSelected = false;
			KE.util.setData(id);
			KE.remove(id, 1);
			g.width = this.width;
			g.height = this.height;
			KE.create(id, 2);
			document.body.parentNode.style.overflow = 'auto';
			KE.event.remove(window, 'resize', resizeListener);
			g.resizeMode = g.config.resizeMode;
			KE.toolbar.unselect(id, "fullscreen");
		} else {
			this.isSelected = true;
			KE.util.setData(id);
			this.width = g.container.style.width;
			this.height = g.container.style.height;
			KE.remove(id, 2);
			document.body.parentNode.style.overflow = 'hidden';
			resetSize();
			KE.create(id, 1);
			var pos = KE.util.getScrollPos();
			var div = g.container;
			div.style.position = 'absolute';
			div.style.left = pos.x + 'px';
			div.style.top = pos.y + 'px';
			div.style.zIndex = 19811211;
			KE.event.add(window, 'resize', resizeListener);
			g.resizeMode = 0;
			KE.toolbar.select(id, "fullscreen");
		}
	}
};

KE.plugin['bgcolor'] = {
	click : function(id) {
		KE.util.selection(id);
		this.menu = new KE.menu({
			id : id,
			cmd : 'bgcolor'
		});
		this.menu.picker();
	},
	exec : function(id, value) {
		var cmd = new KE.cmd(id);
		if (value == '') {
			cmd.remove({
				'span' : ['.background-color']
			});
		} else {
			cmd.wrap('span', [{'.background-color': value}]);
		}
		KE.history.add(id, false);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['fontname'] = {
	click : function(id) {
		var fontName = {
			'SimSun' : '宋体',
			'SimHei' : '黑体',
			'FangSong_GB2312' : '仿宋体',
			'KaiTi_GB2312' : '楷体',
			'NSimSun' : '新宋体',
			'Arial' : 'Arial',
			'Arial Black' : 'Arial Black',
			'Times New Roman' : 'Times New Roman',
			'Courier New' : 'Courier New',
			'Tahoma' : 'Tahoma',
			'Verdana' : 'Verdana'
		};
		var cmd = 'fontname';
		KE.util.selection(id);
		var menu = new KE.menu({
			id : id,
			cmd : cmd
		});
		KE.each(fontName, function(key, value) {
			var html = '<span style="font-family: ' + key + ';">' + value + '</span>';
			menu.add(html, new Function('KE.plugin["' + cmd + '"].exec("' + id + '", "' + key + '")'));
		});
		menu.show();
		this.menu = menu;
	},
	exec : function(id, value) {
		var cmd = new KE.cmd(id);
		cmd.wrap('span', [{'.font-family': value}]);
		KE.history.add(id, false);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['fontsize'] = {
	click : function(id) {
		var fontSize = ['9px', '10px', '12px', '14px', '16px', '18px', '24px', '32px'];
		var cmd = 'fontsize';
		KE.util.selection(id);
		var menu = new KE.menu({
			id : id,
			cmd : cmd
		});
		for (var i = 0, len = fontSize.length; i < len; i++) {
			var value = fontSize[i];
			var html = '<span style="font-size: ' + value + ';">' + value + '</span>';
			menu.add(html, new Function('KE.plugin["' + cmd + '"].exec("' + id + '", "' + value + '")'));
		}
		menu.show();
		this.menu = menu;
	},
	exec : function(id, value) {
		var cmd = new KE.cmd(id);
		cmd.wrap('span', [{'.font-size': value}]);
		KE.history.add(id, false);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['hr'] = {
	click : function(id) {
		KE.util.selection(id);
		KE.util.insertHtml(id, '<hr />');
		KE.util.focus(id);
	}
};

KE.plugin['print'] = {
	click : function(id) {
		KE.util.selection(id);
		KE.g[id].iframeWin.print();
	}
};

KE.plugin['removeformat'] = {
	click : function(id) {
		KE.util.selection(id);
		var cmd = new KE.cmd(id);
		var tags = {
			'*' : ['class', 'style']
		};
		for (var i = 0, len = KE.g[id].inlineTags.length; i < len; i++) {
			tags[KE.g[id].inlineTags[i]] = ['*'];
		}
		cmd.remove(tags);
		KE.history.add(id, false);
		KE.toolbar.updateState(id);
		KE.util.focus(id);
	}
};

KE.plugin['source'] = {
	click : function(id) {
		var g = KE.g[id];
		if (!g.wyswygMode) {
			KE.util.setFullHtml(id, g.newTextarea.value);
			g.iframe.style.display = 'block';
			g.newTextarea.style.display = 'none';
			KE.toolbar.able(id, ['source', 'fullscreen']);
			g.wyswygMode = true;
			this.isSelected = false;
			KE.toolbar.unselect(id, "source");
		} else {
			KE.layout.hide(id);
			g.newTextarea.value = KE.util.getData(id);
			g.iframe.style.display = 'none';
			g.newTextarea.style.display = 'block';
			KE.toolbar.disable(id, ['source', 'fullscreen']);
			g.wyswygMode = false;
			this.isSelected = true;
			KE.toolbar.select(id, "source");
		}
		KE.util.focus(id);
	}
};

KE.plugin['textcolor'] = {
	click : function(id) {
		KE.util.selection(id);
		this.menu = new KE.menu({
			id : id,
			cmd : 'textcolor'
		});
		this.menu.picker();
	},
	exec : function(id, value) {
		var cmd = new KE.cmd(id);
		if (value == '') {
			cmd.remove({
				'span' : ['.color'],
				'font' : ['color']
			});
		} else {
			cmd.wrap('span', [{'.color': value}]);
		}
		KE.history.add(id, false);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['title'] = {
	click : function(id) {
		var title = {
			'H1' : '标题 1',
			'H2' : '标题 2',
			'H3' : '标题 3',
			'H4' : '标题 4',
			'H5' : '标题 5',
			'H6' : '标题 6'
		};
		var cmd = 'title';
		KE.util.selection(id);
		var menu = new KE.menu({
			id : id,
			cmd : cmd
		});
		KE.each(title, function(key, value) {
			var html = '<' + key + ' style="margin:0px;">' + value + '</' + key + '>';
			menu.add(html, new Function('KE.plugin["' + cmd + '"].exec("' + id + '", "<' + key + '>")'));
		});
		menu.show();
		this.menu = menu;
	},
	exec : function(id, value) {
		KE.util.select(id);
		KE.util.execCommand(id, 'formatblock', value);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['emoticons'] = {
	click : function(id) {
		var cmd = 'emoticons';
		var rows = 9, cells = 15;
		KE.util.selection(id);
		var table = KE.$$('table');
		table.className = 'ke-plugin-emoticons-table';
		table.cellPadding = 0;
		table.cellSpacing = 2;
		table.border = 0;
		var num = 0;
		for (var i = 0; i < rows; i++) {
			var row = table.insertRow(i);
			for (var j = 0; j < cells; j++) {
				var cell = row.insertCell(j);
				cell.className = 'ke-plugin-emoticons-td';
				cell.onmouseover = function() {this.style.borderColor = '#000000'; }
				cell.onmouseout = function() {this.style.borderColor = '#F0F0EE'; }
				cell.onclick = new Function('KE.plugin["' + cmd + '"].exec("' + id + '", "' + num + '.gif")');
				var span = KE.$$('span');
				span.className = 'ke-plugin-emoticons-span';
				span.style.backgroundPosition = '-' + (24 * num) + 'px 0px';
				cell.appendChild(span);
				num++;
			}
		}
		var menu = new KE.menu({
			id : id,
			cmd : cmd
		});
		menu.append(table);
		menu.show();
		this.menu = menu;
	},
	exec : function(id, value) {
		var html = '<img src="' + KE.g[id].pluginsPath + 'emoticons/' + value + '" width="24" height="24" border="0" alt="" />';
		KE.util.insertHtml(id, html);
		this.menu.hide();
		KE.util.focus(id);
	}
};

KE.plugin['flash'] = {
	init : function(id) {
		var self = this;
		KE.g[id].getHtmlHooks.push(function(html) {
			return html.replace(/<img[^>]*class="?ke-flash"?[^>]*>/ig, function(imgStr) {
				var width = imgStr.match(/style="[^"]*;?\s*width:\s*(\d+)/i) ? RegExp.$1 : 0;
				var height = imgStr.match(/style="[^"]*;?\s*height:\s*(\d+)/i) ? RegExp.$1 : 0;
				width = width || (imgStr.match(/width="([^"]+)"/i) ? RegExp.$1 : 0);
				height = height || (imgStr.match(/height="([^"]+)"/i) ? RegExp.$1 : 0);
				if (imgStr.match(/title="([^"]+)"/i)) {
					var hash = KE.util.strToHash(RegExp.$1);
					var src = hash.src || '';
					width = width || hash.width || 0;
					height = height || hash.height || 0;
					return KE.util.getMediaEmbed(id, 'flash', src, width, height);
				}
			});
		});
		KE.g[id].setHtmlHooks.push(function(html) {
			return html.replace(/<embed[^>]*type="application\/x-shockwave-flash"[^>]*>/ig, function($0) {
				var src = $0.match(/\s+src="([^"]+)"/i) ? RegExp.$1 : '';
				var width = $0.match(/\s+width="([^"]+)"/i) ? RegExp.$1 : 0;
				var height = $0.match(/\s+height="([^"]+)"/i) ? RegExp.$1 : 0;
				return KE.util.getMediaImage(id, 'flash', src, width, height);
			});
		});
	},
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'flash',
			file : 'flash.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 140,
			loadingMode : true,
			title : KE.lang['flash'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	check : function(id, url, width, height) {
		if (!url.match(/\w+:\/\/.{3,}/)) {
			alert(KE.lang['invalidUrl']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!width.match(/^\d*$/)) {
			alert(KE.lang['invalidWidth']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!height.match(/^\d*$/)) {
			alert(KE.lang['invalidHeight']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		return true;
	},
	exec : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var url = KE.$('url', dialogDoc).value;
		var width = KE.$('width', dialogDoc).value;
		var height = KE.$('height', dialogDoc).value;
		if (!this.check(id, url, width, height)) return false;
		var html = KE.util.getMediaImage(id, 'flash', url, width, height);
		KE.util.insertHtml(id, html);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['image'] = {
	getSelectedNode : function(id) {
		var g = KE.g[id];
		var startNode = g.keRange.startNode;
		if (startNode.nodeType != 1) return;
		if (startNode.tagName.toLowerCase() != 'img') return;
		if (!startNode.className.match(/^ke-\w+/i)) return startNode;
	},
	init : function(id) {
		var self = this;
		var g = KE.g[id];
		g.contextmenuItems.push({
			text : KE.lang['editImage'],
			click : function(id, menu) {
				KE.util.select(id);
				menu.hide();
				self.click(id);
			},
			cond : function(id) {
				return self.getSelectedNode(id);
			}
		});
		g.contextmenuItems.push({
			text : KE.lang['deleteImage'],
			click : function(id, menu) {
				KE.util.select(id);
				menu.hide();
				var img = self.getSelectedNode(id);
				img.parentNode.removeChild(img);
			},
			cond : function(id) {
				return self.getSelectedNode(id);
			}
		});
	},
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'image',
			file : 'image/image.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 230,
			loadingMode : true,
			title : KE.lang['image'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	check : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var type = KE.$('type', dialogDoc).value;
		var width = KE.$('imgWidth', dialogDoc).value;
		var height = KE.$('imgHeight', dialogDoc).value;
		var title = KE.$('imgTitle', dialogDoc).value;
		var url = '';
		if (type == 2) {
			url = KE.$('imgFile', dialogDoc).value;
		} else {
			url = KE.$('url', dialogDoc).value;
		}
		if (!url.match(/\.(jpg|jpeg|gif|bmp|png)(\s|$)/i)) {
			alert(KE.lang['invalidImg']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!width.match(/^\d*$/)) {
			alert(KE.lang['invalidWidth']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!height.match(/^\d*$/)) {
			alert(KE.lang['invalidHeight']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		return true;
	},
	exec : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var type = KE.$('type', dialogDoc).value;
		if (!this.check(id)) return false;
		if (type == 2) {
			KE.$('editorId', dialogDoc).value = id;
			dialogDoc.uploadForm.submit();
			return false;
		} else {
			var url = KE.$('url', dialogDoc).value;
			var width = KE.$('imgWidth', dialogDoc).value;
			var height = KE.$('imgHeight', dialogDoc).value;
			var title = KE.$('imgTitle', dialogDoc).value;
			var alignElements = dialogDoc.getElementsByName('align');
			var align = '';
			for (var i = 0, len = alignElements.length; i < len; i++) {
				if (alignElements[i].checked) {
					align = alignElements[i].value;
					break;
				}
			}
			this.insert(id, url, title, width, height, 0, align);
		}
	},
	insert : function(id, url, title, width, height, border, align) {
		var html = '<img src="' + url + '" ';
		if (width > 0) html += 'width="' + width + '" ';
		if (height > 0) html += 'height="' + height + '" ';
		if (title) html += 'title="' + title + '" ';
		if (align) html += 'align="' + align + '" ';
		html += 'alt="' + title + '" ';
		html += 'border="' + border + '" />';
		KE.util.insertHtml(id, html);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['link'] = {
	getSelectedNode : function(id) {
		var g = KE.g[id];
		var range = g.keRange;
		var startNode = range.startNode;
		var startPos = range.startPos;
		var endNode = range.endNode;
		var endPos = range.endPos;
		var inlineTagHash = KE.util.arrayToHash(g.inlineTags);
		var findLinkNode = function(node) {
			while (node) {
				if (node.nodeType == 1) {
					var tagName = node.tagName.toLowerCase();
					if (tagName == 'a') return node;
				}
				node = node.parentNode;
			}
			return null;
		};
		var startLink = findLinkNode(startNode);
		var endLink = findLinkNode(endNode);
		if (startLink && endLink && startLink === endLink) {
			return startLink;
		}
	},
	init : function(id) {
		var self = this;
		KE.g[id].contextmenuItems.push({
			text : KE.lang['editLink'],
			click : function(id, menu) {
				KE.util.select(id);
				menu.hide();
				self.click(id);
			},
			cond : function(id) {
				return self.getSelectedNode(id);
			}
		});
		KE.g[id].contextmenuItems.push({
			text : KE.lang['deleteLink'],
			click : function(id, menu) {
				KE.util.select(id);
				menu.hide();
				KE.util.execCommand(id, 'unlink', null);
			},
			cond : function(id) {
				return self.getSelectedNode(id);
			}
		});
	},
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'link',
			file : 'link/link.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 100,
			loadingMode : true,
			title : KE.lang['link'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	exec : function(id) {
		KE.util.select(id);
		var iframeDoc = KE.g[id].iframeDoc;
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var url = KE.$('hyperLink', dialogDoc).value;
		var target = KE.$('linkType', dialogDoc).value;
		if (!url.match(/.+/) || url.match(/^\w+:\/\/\/?$/)) {
			alert(KE.lang['invalidUrl']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		var node = KE.g[id].keRange.getParentElement();
		while (node) {
			if (node.tagName.toLowerCase() == 'a' || node.tagName.toLowerCase() == 'body') break;
			node = node.parentNode;
		}
		node = node.parentNode;
		iframeDoc.execCommand('createlink', false, '__ke_temp_url__');
		var arr = node.getElementsByTagName('a');
		for (var i = 0, l = arr.length; i < l; i++) {
			if (arr[i].href.match(/\/?__ke_temp_url__$/) != null) {
				arr[i].href = url;
				if (target) arr[i].target = target;
			}
		}
		KE.history.add(id);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['media'] = {
	init : function(id) {
		var self = this;
		var typeHash = {};
		KE.each(KE.g[id].mediaTypes, function(key, val) {
			typeHash[val] = key;
		});
		KE.g[id].getHtmlHooks.push(function(html) {
			return html.replace(/<img[^>]*class="?ke-\w+"?[^>]*>/ig, function($0) {
				var width = $0.match(/style="[^"]*;?\s*width:\s*(\d+)/i) ? RegExp.$1 : 0;
				var height = $0.match(/style="[^"]*;?\s*height:\s*(\d+)/i) ? RegExp.$1 : 0;
				width = width || ($0.match(/width="([^"]+)"/i) ? RegExp.$1 : 0);
				height = height || ($0.match(/height="([^"]+)"/i) ? RegExp.$1 : 0);
				if ($0.match(/\s+title="([^"]+)"/i)) {
					var hash = KE.util.strToHash(RegExp.$1);
					var src = hash.src || '';
					width = width || hash.width || 0;
					height = height || hash.height || 0;
					autostart = hash.autostart || 'false';
					return KE.util.getMediaEmbed(id, null, src, width, height, autostart);
				}
			});
		});
		KE.g[id].setHtmlHooks.push(function(html) {
			return html.replace(/<embed[^>]*type="([^"]+)"[^>]*>/ig, function($0, $1) {
				if (typeof typeHash[$1] == 'undefined') return $0;
				var src = $0.match(/\s+src="([^"]+)"/i) ? RegExp.$1 : '';
				var width = $0.match(/\s+width="([^"]+)"/i) ? RegExp.$1 : 0;
				var height = $0.match(/\s+height="([^"]+)"/i) ? RegExp.$1 : 0;
				var autostart = $0.match(/\s+autostart="([^"]+)"/i) ? RegExp.$1 : 'false';
				return KE.util.getMediaImage(id, null, src, width, height, autostart);
			});
		});
	},
	click : function(id) {
		KE.util.selection(id);
		this.dialog = new KE.dialog({
			id : id,
			cmd : 'media',
			file : 'media.html?id=' + id + '&ver=' + KE.version,
			width : 400,
			height : 170,
			loadingMode : true,
			title : KE.lang['media'],
			yesButton : KE.lang['yes'],
			noButton : KE.lang['no']
		});
		this.dialog.show();
	},
	check : function(id, url, width, height) {
		if (!url.match(/^\w+:\/\/.{3,}\.(swf|flv|mp3|wav|wma|wmv|mid|avi|mpg|mpeg|asf|rm|rmvb)(\?|$)/i)) {
			alert(KE.lang['invalidMedia']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!width.match(/^\d*$/)) {
			alert(KE.lang['invalidWidth']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		if (!height.match(/^\d*$/)) {
			alert(KE.lang['invalidHeight']);
			window.focus();
			this.dialog.yesButton.focus();
			return false;
		}
		return true;
	},
	exec : function(id) {
		var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
		var url = KE.$('url', dialogDoc).value;
		var width = KE.$('width', dialogDoc).value;
		var height = KE.$('height', dialogDoc).value;
		if (!this.check(id, url, width, height)) return false;
		var autostart = KE.$('autostart', dialogDoc).checked ? 'true' : 'false';
		var html = KE.util.getMediaImage(id, null, url, width, height, autostart);
		KE.util.insertHtml(id, html);
		KE.layout.hide(id);
		KE.util.focus(id);
	}
};

KE.plugin['table'] = {
	click : function(id) {
		var self = this;
		var num = 10;
		var cmd = 'table';
		var cellArr = [];
		KE.util.selection(id);
		var table = KE.$$('table');
		table.cellPadding = 0;
		table.cellSpacing = 0;
		table.border = 0;
		table.className = 'ke-plugin-table-table';
		for (var i = 0; i < num; i++) {
			var row = table.insertRow(i);
			cellArr[i] = [];
			for (var j = 0; j < num; j++) {
				var value = (i + 1) + ',' + (j + 1);
				var cell = row.insertCell(j);
				cell.className = 'ke-plugin-table-td';
				var div = KE.$$('div');
				div.className = 'ke-plugin-table-div';
				cell.appendChild(div);
				cellArr[i][j] = div;
				div.onmouseover = (function(x, y) {
					return function() {
						self.locationCell.innerHTML = x + ' by ' + y + ' Table';
						for (var m = 0; m < num; m++) {
							for (var n = 0; n < num; n++) {
								var cell = cellArr[m][n];
								if (m < x && n < y) cell.style.backgroundColor = '#CCCCCC';
								else cell.style.backgroundColor = '#FFFFFF';
							}
						}
					};
				})(i + 1, j + 1);
				div.onclick = (function(value) {
					return function() { KE.plugin[cmd].exec(id, value); };
				})(value);
			}
		}
		var row = table.insertRow(num);
		var cell = row.insertCell(0);
		cell.className = 'ke-plugin-table-td-bottom';
		cell.colSpan = 10;
		self.locationCell = cell;
		var menu = new KE.menu({
			id : id,
			cmd : cmd
		});
		menu.append(table);
		menu.show();
		this.menu = menu;
	},
	exec : function(id, value) {
		var location = value.split(',');
		var html = '<table border="1">';
		for (var i = 0; i < location[0]; i++) {
			html += '<tr>';
			for (var j = 0; j < location[1]; j++) {
				html += '<td>&nbsp;</td>';
			}
			html += '</tr>';
		}
		html += '</table>';
		KE.util.insertHtml(id, html);
		this.menu.hide();
		KE.util.focus(id);
	}
};
