//alert("whitesmoke_plugin.local.js");
/********************************************************************************
Class WhiteSmokePluginEngine
********************************************************************************/
function WhiteSmokePluginEngine() {
	var ua = navigator.userAgent;
	this.isMSIE = (navigator.appName == "Microsoft Internet Explorer");
	this.isMSIE5 = this.isMSIE && (ua.indexOf('MSIE 5') != -1);
	this.isMSIE5_0 = this.isMSIE && (ua.indexOf('MSIE 5.0') != -1);
	this.isMSIE7 = this.isMSIE && (ua.indexOf('MSIE 7') != -1);
	this.isMSIE8 = this.isMSIE && (ua.indexOf('MSIE 8') != -1);
	this.isGecko = ua.indexOf('Gecko') != -1;
	this.isSafari = ua.indexOf('Safari') != -1;
	this.isOpera = window['opera'] && opera.buildNumber ? true: false;
	this.isMac = ua.indexOf('Mac') != -1;
	this.isNS7 = ua.indexOf('Netscape/7') != -1;
	this.isNS71 = ua.indexOf('Netscape/7.1') != -1;
}
WhiteSmokePluginEngine.prototype = {
	documentBasePath: null,
	documentURL: null,
	baseURL: '', //whitesmoke_plugin',
	srcMode: '',
	runningEngines: {},
	activeEngine: null,
	loadedFiles: [],
	init: function() {
		this.documentBasePath = document.location.href;
		if (this.documentBasePath.indexOf('?') != -1) {
			this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.indexOf('?'));
		}
		this.documentURL = this.documentBasePath;
		this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.lastIndexOf('/'));
		if (!this.baseURL) {
			var elements = [];
			var nl, i, head, baseHREF = '';
			head = document.getElementsByTagName('head')[0];
			if (head) {
				for (i = 0, nl = head.getElementsByTagName('script'); i < nl.length; i++) 
					elements.push(nl[i]);
			}
			for (i = 0, nl = document.getElementsByTagName('script'); i < nl.length; i++)
				elements.push(nl[i]);
			nl = document.getElementsByTagName('base');
			for (i = 0; i < nl.length; i++) {
				if (nl[i].href) baseHREF = nl[i].href;
			}
			for (i = 0; i < elements.length; i++) {
				if (elements[i].src && 
					(elements[i].src.indexOf("whitesmoke_plugin.js") != -1 || 
					 elements[i].src.indexOf("whitesmoke_plugin_src.js") != -1 || 
					 elements[i].src.indexOf("whitesmoke_plugin_dev.js") != -1 || 
					 elements[i].src.indexOf("whitesmoke_plugin.local.js") != -1))
				{
					src = elements[i].src;
					this.srcMode = (src.indexOf('_src') != -1) ? '_src': '';
					this.srcMode = (src.indexOf('_dev') != -1) ? '_src': this.srcMode;
					src = src.substring(0, src.lastIndexOf('/'));
					if (baseHREF !== '' && src.indexOf('://') == -1)
						this.baseURL = baseHREF + src;
					else
						this.baseURL = src;
					//alert("this.baseURL = " + this.baseURL);
					break;
				}
			}
		}
		//this.loadCSS("css/whitesmoke_popup.css");
		//this.loadCSS("whitesmoke_plugin/css/whitesmoke_popup.css");
	},
	loadCSS: function(url) {
		var ar = url.replace(/\s+/, '').split(',');
		var lflen = 0,
		csslen = 0,
		skip = false;
		var x = 0,
		i = 0,
		nl, le;
		for (x = 0, csslen = ar.length; x < csslen; x++) {
			if (ar[x] != null && ar[x] != 'null' && ar[x].length > 0) {
				for (i = 0, lflen = this.loadedFiles.length; i < lflen; i++) {
					if (this.loadedFiles[i] == ar[x]) {
						skip = true;
						break;
					}
				}
				if (!skip) {
					document.write('<link href="' + ar[x] + '" rel="stylesheet" type="text/css" />');
					this.loadedFiles[this.loadedFiles.length] = ar[x];
				}
			}
		}
	},
	importCSS: function(doc, css) {
		var css_ary = css.replace(/\s+/, '').split(',');
		var csslen, elm, headArr, x, css_file;
		for (x = 0, csslen = css_ary.length; x < csslen; x++) {
			css_file = css_ary[x];
			if (css_file != null && css_file != 'null' && css_file.length > 0) {
				if (css_file.indexOf('://') == -1 && css_file.charAt(0) != '/') css_file = this.documentBasePath + "/" + css_file;
				WSDebug.log("Adding stylesheet: " + css_file, "WhiteSmoke.importCSS");
				if (typeof(doc.createStyleSheet) == "undefined") {
					elm = doc.createElement("link");
					elm.rel = "stylesheet";
					elm.href = css_file;
					if ((headArr = doc.getElementsByTagName("head")) != null && headArr.length > 0) headArr[0].appendChild(elm);
				} else {
					WSDebug.log("using createStyleSheet", "importCSS");
					doc.createStyleSheet(css_file);
				}
			}
		}
	},
	//plugin_engine, plugin_actions
	add: function(c, m) {
		var n;
		for (n in m) {
			if (m.hasOwnProperty(n)) {
				c.prototype[n] = m[n];
			}
		}
	},
	extend: function(c, m) {
		var n;
		for (n in m) {
			if (m.hasOwnProperty(n))
				c[n] = m[n];
		}
	},
	cleanWSContentById: function(sContent, sIdPrefix, sTagName) {
		var sNewContent = "";
		var nPos = 0;
		for (; nPos < sContent.length; ++nPos) {
			var j = sContent.indexOf(sIdPrefix, nPos);
			if (j >= 2 && (sContent.charAt(j - 1) == "=" || sContent.charAt(j - 2) == "=")) {
				while (sContent.charAt(j) != "<") {--j;
				}
				if (j >= 0) {
					sNewContent += sContent.substr(nPos, j - nPos);
					var nStart = sContent.indexOf(">", j);
					if (nStart >= 0) {++nStart;
						var nEnd = sContent.indexOf("</", nStart);
						if (nEnd >= 0) {
							sNewContent += sContent.substr(nStart, nEnd - nStart);
							var tag = "</" + sTagName + ">";
							nPos = nEnd + tag.length - 1;
						} else {}
					} else {}
				}
			} else {
				sNewContent += sContent.substr(nPos);
				break;
			}
		}
		return sNewContent;
	},
	cleanWSContent: function(sContent) {
		var sNewContent = this.cleanWSContentById(sContent, "wsWord", "span");
		return sNewContent;
	},
	createEngine: function(settings, editorId, editorInstance) {
		return new WSEnrichmentEngine(settings, editorId, editorInstance);
	},
	initializeEngine: function(editorId) {
		this.runningEngines[editorId].initialize();
	}
};

var whitesmokePlugin = new WhiteSmokePluginEngine();
var wsplugin = whitesmokePlugin;
whitesmokePlugin.init();

/********************************************************************************
Class WhiteSmokePluginActions
********************************************************************************/
var WhiteSmokePluginActions = {
	doCommand: function(comm, editor_id) {
		//alert("comm = " + comm + ", editor_id = " + editor_id);
		this.activeEngine = this.runningEngines[editor_id];
		switch (comm) {
			case "spellcheck":
				this.doSpellCheck();
				break;
			case "redo":
				this.doRedo();
				break;
			case "undo":
				this.doUndo();
				break;
			case "config":
				this.showConfig();
				break;
			case "debug":
				this.showDebug();
				break;
		}
	},
	doSpellCheck: function() {
		this.activeEngine.updateContent();
	},
	doUndo: function() {
		this.activeEngine.undo();
	},
	doRedo: function() {
		this.activeEngine.redo();
	},
	showConfig: function() {
		this.activeEngine.openConfigWindow();
	},
	showDebug: function() {
		WSDebug.openDebugWindow();
	}
};

whitesmokePlugin.add(WhiteSmokePluginEngine, WhiteSmokePluginActions);
/********************************************************************************
Class WSDomUtils
********************************************************************************/
var WSDomUtils = {
	addEvent: function(obj, evType, fn) {
		if (obj.addEventListener) {
			obj.addEventListener(evType, fn, false);
			return true;
		}
		else if (obj.attachEvent) {
			var r = obj.attachEvent("on" + evType, fn);
			return r;
		}
		return false;
	},
	removeEvent: function(obj, evType, fn) {
		if (obj.removeEventListener) {
			obj.removeEventListener(evType, fn, false);
			return true;
		} else if (obj.dispatchEvent) {
			var r = obj.detachEvent("on" + evType, fn);
			return r;
		} else {
			return false;
		}
	},
	escapeHTML: function(str) {
		var div = document.createElement('div');
		var text = document.createTextNode(str);
		div.appendChild(text);
		return div.innerHTML;

	},

	getStyle: function(elm,sStyle)
	{
		if (!elm) return false;
		var val = null;
		if (elm.currentStyle) { //IE
			sStyle = sStyle.replace(/-(\D)/g, function(a,b) { return b.toUpperCase(); } );
			val = elm.currentStyle[sStyle];
		}	
		else if (window.getComputedStyle && elm.ownerDocument.defaultView) { //Mozzila etc
			val = elm.ownerDocument.defaultView.getComputedStyle(elm,null).getPropertyValue(sStyle);
		}	
		return val;
	},
	
	/*
	getStyle: function(elm, na) {
		if (!elm) return false;
		if (whitesmokePlugin.isGecko && elm.ownerDocument.defaultView) {
			try {
				return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(na);
			} catch(elm) {
				return null;
			}
		}
		na = na.replace(/-(\D)/g, function(a,b) { return b.toUpperCase(); } );
		if (elm.currentStyle) {
			return elm.currentStyle[na];
		}
		return false;
	},
	*/

	getElementMargin: function(elm)
	{
		var margin = {left: 0, right: 0, top: 0, bottom: 0};
		margin.left = parseInt(this.getStyle(elm,"margin-left"));
		margin.right = parseInt(this.getStyle(elm,"margin-right"));
		margin.top = parseInt(this.getStyle(elm,"margin-top"));
		margin.bottom = parseInt(this.getStyle(elm,"margin-bottom"));
		return margin;
	}
}
whitesmokePlugin.DomUtils = WSDomUtils;
/********************************************************************************
Class WSUtils
********************************************************************************/
var WSUtils = {
	majorVersion: 1,
	minorVersion: 1.2,
	releaseDate: "2007-12-04",
	zeroBounds: function() {
		return {x: 0, y: 0,	w: 0, h: 0};
	},
	zeroPoint: function() {
		return {x: 0, y: 0};
	}
};
WSUtils.absolutize = function(element) {
	if (element && element.style) {
		if (element.style.display == 'none') {
			element.style.display = '';
		}
	}
}

WSUtils.findElementPos = function(obj) {
	var finalPoint = WSUtils.zeroPoint();
	if (obj.offsetParent) {
		do {
			finalPoint.x += parseInt(obj.offsetLeft);
			finalPoint.y += parseInt(obj.offsetTop);
		} while (obj = obj.offsetParent)
	}	
	else {
		if (obj.x)
			finalPoint.x += obj.x;
		if (obj.y) {
			finalPoint.y += obj.y;
		}
	}

	return finalPoint;
}

WSUtils.pageWidth = function(oWindow) {
	if (oWindow.innerWidth != null) {
		return oWindow.innerWidth;
	}
	else if (oWindow.document.documentElement && 
			 oWindow.document.documentElement.clientWidth) {
		return oWindow.document.documentElement.clientWidth;
	}	
	else if (oWindow.document.body != null) {
		return oWindow.document.body.clientWidth;
	}
	return null;
}	

WSUtils.pageHeight = function(oWindow) {
	if (oWindow.innerHeight != null) {
		return oWindow.innerHeight;
	}
	else if (oWindow.document.documentElement && 
			 oWindow.document.documentElement.clientHeight) {
		return oWindow.document.documentElement.clientHeight;
	}	
	else if (oWindow.document.body != null) {
		return oWindow.document.body.clientHeight;
	}
	return null;
}	

WSUtils.posLeft = function(oWindow) {
	if (typeof oWindow.pageXOffset != 'undefined') {
		return oWindow.pageXOffset;
	}
	else if (oWindow.document.documentElement && 
			 oWindow.document.documentElement.scrollLeft) {
		return oWindow.document.documentElement.scrollLeft;
	}
	else if (oWindow.document.body.scrollLeft) {
		return oWindow.document.body.scrollLeft;
	}
	return 0;
}
WSUtils.posTop = function(oWindow) {
	if (typeof oWindow.pageYOffset != 'undefined') {
		return oWindow.pageYOffset;
	}
	else if (oWindow.document.documentElement && 
			 oWindow.document.documentElement.scrollTop) {
		return oWindow.document.documentElement.scrollTop;
	}
	else if (oWindow.document.body.scrollTop) {
		return oWindow.document.body.scrollTop;
	}
	return 0;
 }

WSUtils.getWindowBounds2 = function(oWindow) {
	var b = WSUtils.zeroBounds();
	b.x = WSUtils.posLeft(oWindow);
	b.y = WSUtils.posTop(oWindow);
	b.w = WSUtils.pageWidth(oWindow);
	b.h = WSUtils.pageHeight(oWindow);
	return b;
}

WSUtils.getWindowBounds = function(oWindow) {
	var b = WSUtils.zeroBounds();
	if (oWindow.innerHeight) {
		b.x = oWindow.pageXOffset;
		b.y = oWindow.pageYOffset;
		b.w = oWindow.innerWidth;
		b.h = oWindow.innerHeight;
	}
	else if (oWindow.document.documentElement && 
			   oWindow.document.documentElement.clientHeight) {
		b.w = oWindow.document.documentElement.clientWidth;
		b.h = oWindow.document.documentElement.clientHeight;
		b.x = oWindow.document.documentElement.scrollLeft;
		b.y = oWindow.document.documentElement.scrollTop;
	}
	else if (oWindow.document.body) {
		b.w = oWindow.document.body.clientWidth;
		b.h = oWindow.document.body.clientHeight;
		b.x = oWindow.document.body.scrollLeft;
		b.y = oWindow.document.body.scrollTop;
	}
	return b;
}
WSUtils.getElementBounds = function(element, safe) {
	if (element) {
		var orig_element = element;
		this.absolutize(element);
		var bounds = WSUtils.zeroBounds();
		bounds.x = parseInt(element.offsetLeft);
		bounds.y = parseInt(element.offsetTop);
		bounds.w = parseInt(element.offsetWidth);
		bounds.h = parseInt(element.offsetHeight);
		if (element.tagName == "IFRAME") {
			var obj = element;
			var curleft = 0;
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					curleft += obj.offsetLeft;
					obj = obj.offsetParent;
				}
			}
			else if (obj.x) {
				curleft += obj.x;
			}	
			bounds.x = curleft;
			obj = element;
			var curtop = 0;
			if (obj.offsetParent) {
				while (obj.offsetParent) {
					curtop += obj.offsetTop;
					obj = obj.offsetParent;
				}
			}
			else if (obj.y) {
				curtop += obj.y;
			}	
			bounds.y = curtop;
		}
		if (safe && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
			var valueT = 0,
			valueL = 0;
			var obj = element;
			do {
				valueT += obj.offsetTop || 0;
				valueL += obj.offsetLeft || 0;
				if (obj.offsetParent == document.body) {
					if (WSDomUtils.getStyle(obj,'position') == 'absolute')
						break;
				}
				obj = obj.offsetParent;
			} while ( obj );
			bounds.x = valueL;
			bounds.y = valueT;
		}
		//IE consider the margin when calculating element bounds, others not	
		if (! whitesmokePlugin.isMSIE) {
			var margin = WSDomUtils.getElementMargin(orig_element.ownerDocument.body);
			bounds.x += margin.left;	
			bounds.y += margin.top;
		}
		return bounds;
	}
	return zeroBounds();
}

WSUtils.hideElement = function(oElement) {
	if (oElement) {
		oElement.style.display = "none";
		oElement.style.visibility = "hidden";
		//oElement.innerHTML = "";
	}
}

WSUtils.showElement = function(oElement) {
	if (oElement) {
		oElement.style.display = "block";
		oElement.style.visibility = "visible";
	}
}

WSUtils.isVisible = function(oElement) {
	return ((oElement.style.display!="" && oElement.style.display!="none") || 
			(oElement.style.visibility!="" && oElement.style.visibility!="hidden"));
}

WSUtils.toggleVisibility = function(oElement) {
	if (WSUtils.isVisible(oElement)) {
		WSUtils.hideElement(oElement);
	}	
	else {
		WSUtils.showElement(oElement);
	}
	
}

WSUtils.calcCenteredPosition = function(oElement, oWindow) {
	var finalPoint   = WSUtils.zeroPoint();
	var windowBounds = WSUtils.getWindowBounds(oWindow, true);
	var elmBounds    = WSUtils.getElementBounds(oElement, true);
	
	//WSUtils.alert(windowBounds);
	//WSUtils.alert(elmBounds);

	finalPoint.x = windowBounds.x + (windowBounds.w - elmBounds.w)/2;
	finalPoint.y = windowBounds.y + (windowBounds.h - elmBounds.h)/2;
	//finalPoint.w = elmBounds.w;
	//finalPoint.h = elmBounds.h;

	return finalPoint;	
}

WSUtils.alert = function(obj) {
	if (obj) {
		var str = '{';
		for (var item in obj) {
			str += item + ": " + obj[item] + ", ";
		}
		alert(str + "}");
	}
	alert(obj);
}
WSUtils.getEventElement = function(e) {
	var targ = null;
	if (!e) {
		e = window.event;
		if (!e) {
			return null;
		}
	}
	if (e.target) {
		targ = e.target;
	} else if (e.srcElement) {
		targ = e.srcElement;
	}
	if (targ.nodeType == 3) {
		targ = targ.offsetParent;
	}
	return targ;
}
WSUtils.getElementIndex = function(oElement) {
	nIndex = -1;
	if (oElement) {
		var sId = oElement.id;
		if (sId) {
			var nStart = sId.indexOf("wsWord");
			if (nStart < 0) {
				return nStart;
			}
			nStart += String("wsWord").length;
			var sIndex = sId.substr(nStart);
			nIndex = parseInt(sIndex);
		}
	}
	return nIndex;
}
WSUtils.isRightClick = function(e) {
	if (!e) e = window.event;
	var bRightClick = false;
	if (e.which) {
		bRightClick = (e.which == 2 || e.which == 3);
	} else if (e.button) {
		bRightClick = (e.button == 2)
	}
	return (bRightClick);
}
WSUtils.deprecationWarning = function() {
	alert("the called : \n " + this.deprecationWarning.caller + "\n is depricated; \n It is called from: \n" + this.deprecationWarning.caller.caller);
}
WSUtils.getMousePoint = function(e) {
	var p = WSUtils.zeroPoint();
	if (!e) var e = window.event;
	if (!e) {
		throw ("Error!!");
	}
	if (e.pageX || e.pageY) {
		p.x = e.pageX;
		p.y = e.pageY;
	} else if (e.clientX || e.clientY) {
		p.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		p.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	return p;
}
WSUtils.getEscapedWord = function(sWord) {
	return sWord.replace(/\'/g, "\\'");
}
WSUtils.centerElement = function(oElement,oWindow) {
	var targetPos       = WSUtils.calcCenteredPosition(oElement,oWindow);
	oElement.style.left = (targetPos.x) + "px";
	oElement.style.top  = (targetPos.y) + "px";
	//oElement.style.width   = targetPos.w+"px";
	//oElement.style.height  = targetPos.h+"px";
}

WSUtils.escapeHTML = whitesmokePlugin.DomUtils.escapeHTML;
whitesmokePlugin.utils = WSUtils;
/********************************************************************************
Class WSSharedEnrichmentEngine
********************************************************************************/
var WSSharedEnrichmentEngine = {};
WSSharedEnrichmentEngine.timerID = null;
WSSharedEnrichmentEngine.startDictionaryTimer = function(e, oWSEE) {
	if (this.timerID == null) {
		if (oWSEE) {
			if (oWSEE.config.sMode == "min") {
				return false;
			}
			var oElement = WSUtils.getEventElement(e);
			if (!oElement) {
				return false;
			}
			var sWord = WSUtils.getEscapedWord(oElement.innerHTML);
			if (sWord.length < 2 && !oWSEE.isAlpha(sWord)) {
				return true;
			}
			var nIndex = WSUtils.getElementIndex(oElement);
			if (nIndex < 0) {
				nIndex = oWSEE.nSelectedIndex;
			}
			var nLangType = 0;
			if (nIndex >= 0) {
				var nLangType = oWSEE.vEnrichment[nIndex].nLangType;
			}
			var oMousePos = oWSEE.getMousePos(e);
			oWSEE.setLastPointedInfo(e, oMousePos, sWord, nLangType, nIndex, oElement);
			var sFunc = "onQueryDictionary(\"" + sWord + "\"," + nLangType + ")";
			this.word = sWord;
			this.langType = nLangType;
			this.currentEngine = oWSEE;
			if (WSEnrichmentDefaultConfig.enableDictionary) {
				timerID = setTimeout("WSSharedEnrichmentEngine.queryDictionary()", 2000);
			}	
		}
	}
}
WSSharedEnrichmentEngine.queryDictionary = function() {
	var oWSEE = this.currentEngine;
	this.stopDictionaryTimer();
	if (oWSEE) {
		oWSEE.queryDictionary(this.word, this.langType);
	}
}
WSSharedEnrichmentEngine.stopDictionaryTimer = function() {
	if (this.timerID) {
		clearTimeout(this.timerID);
	}
	timerID = null;
}
WSSharedEnrichmentEngine.onMouseOverSuggestion = function(e, controller) {
	this.startDictionaryTimer(e);
	var oElement = WSUtils.getEventElement(e);
	if (oElement) {
		oElement.className = this.config.suggestionSettings.mouseOver.className;
	}
}

/********************************************************************************
Class WSGlobalProgressWindow
********************************************************************************/
function WSGlobalProgressWindow() {
	this.openProgessWindows = [];
	this.globalProgessElement = null;
	
	this.addOpenProgressWindows = function(editor_id) {
		alert("addOpenProgressWindows editor_id=" + editor_id);
		if (this.openProgessWindows.indexOf(editor_id) > -1) {
			return;
		}	
		this.openProgessWindows.push(editor_id);
	};
	
	this.removeOpenProgressWindows = function(editor_id) {
		alert("removeOpenProgressWindows editor_id=" + editor_id);
		var curIndex = this.openProgessWindows.indexOf(editor_id);
		if (curIndex < 0) {
			return;
		}
		this.openProgessWindows.splice(curIndex, 1);
	};	
}
/*
var WSGlobalProgressWindow = {};
WSGlobalProgressWindow.openProgessWindows = [];
WSGlobalProgressWindow.globalProgessElement = null;

WSGlobalProgressWindow.addOpenProgressWindows = function(editor_id) {
	if (WSGlobalProgressWindow.openProgessWindows.indexOf(editor_id) > -1) {
		return;
	}	
	WSGlobalProgressWindow.openProgessWindows.push(editor_id);
};
WSGlobalProgressWindow.removeOpenProgressWindows = function(editor_id) {
	var curIndex = WSGlobalProgressWindow.openProgessWindows.indexOf(editor_id);
	if (curIndex < 0) {
		return;
	}	
	WSGlobalProgressWindow.openProgessWindows.splice(curIndex, 1);
};
*/
/********************************************************************************
Class WSTemplate
********************************************************************************/
function WSTemplate(str) {
	this.template = str.toString();
	this.pattern = /(^|.|\r|\n)(#\{(.*?)\})/;	

	this.evaluate = function(var_object) {
		var source = this.template;
		var result = '';
		var	match_res;
		
		while (source.length > 0) {
			if (match_res = source.match(this.pattern)) {
				//alert(source + " " + source[match_res.index]);
				result += source.slice(0, match_res.index);
				result += this.matchResult(match_res,var_object);
				source = source.slice(match_res.index + match_res[0].length);
			} else {
				result += source,
				source = '';
			}
		}
		return result;
	};
	this.matchResult = function(match_res, var_object) {
		//alert("match_res[1]=" + match_res[1] + ", match_res[2]=" + match_res[2] + ", match_res[3]= " + match_res[3]);
		var before = match_res[1];
		if (before == '\\') return match_res[2];
		return before + this.interpret(var_object[match_res[3]]);
	};
	this.interpret = function(value) {
		return value == null ? '': String(value);
	};
}

/********************************************************************************
Class WSEnrichmentDefaultConfig
********************************************************************************/
var WSEnrichmentDefaultConfig = {
	majorVersion: 1,
	minorVersion: 1.3,
	version: '1.1.3',
	releaseDate: "2008-01-15",	
	//version: {major: 1, minor: 1.3, str: "1.1.3", releaseDate: "2008-01-15"},

	userId: '1334641007',
	profile: '998',
	sendMethod: {iframe: 1, ajax: 0, ajax_ex: 0, json: 0}, //move to a different class
	
	commandNames: {spellcheck: 0, redo: 1, undo: 2, config: 3, debug: 4},
	
	//sServerURL: "http://www.whitesmoke.com/client_v2/mac/server2.php",
	//sAJAXExURL: "http://www.whitesmoke.com/client_v2/xmlhttp.php",
	//sServerURL: "whitesmoke_plugin/ajax_remote/server2.php",
	//sAJAXExURL: "whitesmoke_plugin/ajax_remote/xmlhttp.php",
	
	//-------------------------------------------------------------------------------
	resultElementsInfo: {
		suggestions: {id: "wsSuggestionsResult", className: "wsSuggestionsResult"},
		dictionary:  {id: "wsDictionaryResult",  className: "wsDictionaryResult"},
		feedback:    {id: "wsFeedbackResult", className: "wsFeedbackResult"},
		progress:    {id: "wsProgress", className: "wsProgress"}
	},
	
	bTrial: false,
	sMode: "full",
	sFormat: "html",
	nMaxSuggestions: (this.sMode == "min" ? 4 : 6),
	nMaxContentLength: 10000,
	
	sContent: "",
	sInitailContent: "",
	sRSFrameId: "WSRSIFrame",
	stLangType:  {
		unknown: 0,
		verb: 4,
		noun: 5,
		adjective: 6,
		adverb: 7
	},
	//-----------------------------------------------------------------------------

	//checkContentOnInit: false,
	//checkContentWhileTyping: true,
	//autoCheckOnChange: true,
	autoCheck: {onInit: false, whileTyping: true, onChange: true},

	enableDictionary: true,	

	enableProgressWindow: false,
	globalProgressWindow: false,
	progressWindow: '',
	//progressWindow: {obj: '', enabled: false, global: false}

	leftBorderHTML: '',
	rightBorderHTML: '',

	getLengthExceededMsg: function() {
		return "WhiteSmoke's Free Online Checker does not check more than " + 
		this.nMaxContentLength + 
		" characters at a time." + 
		"<p>Upgrade to the full desktop application for unlimited use.<BR>" + 
		"<a href='javascript: openLandingPage();' target='_blank'>Get it Now</a></p>";
	},
	
	feedbackTemplate: //Same format that the server sends
		'<div class="screenContainerMsg" id="scrCont">' + 
			'<div id="innerContainer">' + 
				'<div id="bgTopMsg">' + 
					'<div class="left"></div>' + 
					'<div class="center" style="color:#fff">' + 
						'<div id="questionMarkImg"></div>' + 
						'<div id="ServerFeedbackXButton"></div>' + 
					'</div>' + 
					'<div class="right"></div>' +
				'</div>' + 
				'<div id="screenContentMsg">' +
					'<div class="insideScreenMsg">' + 
						//'<div style="height:75px;">' + '#{sMessage}' + '</div>' + 
						'<div class=screenMsg>' + '#{sMessage}' + '</div>' + 
						'#{okButton}' +
						'#{downloadButton}' +
						//'<div id="ServerFeedbackOKButton" style="float:right;"></div>' +
						//'<div id="ServerFeedbackDownloadButton" style="float:left;"></div>' +
						//'<a href="http://www.whitesmoke.com/" target="_blank"></a>' + 
					'</div>' +
				'</div>' + 
				'<div id="bgBottomImg"></div>' + 
			'</div>' + 
		'</div>',
		
	
	suggestionBox:
		'<table CELLPADDING="0" CELLSPACING="0"><tr valign="top"><td>' +
		  '<table wrap class="wsSuggestions"><tr><td id="wsReplace" valign="top" class="wsSuggestionsColumn">' +
			'<table>' +
			  '<th align="left" class="wsSuggestionsHeader" style="color:#{sColor};">#{sHeader}</th>' +
			    '#{suggestion}' +
			'</table>' +
		  '</table>' +
		'</td></tr></table>',

	suggestionHTML:
		//'<tr><td colspan="3" id="#{id}" class="wsSuggestion">#{suggestion}</td></tr>',
		'<tr><td colspan="3" id="#{id}" name="#{name}" class=#{className}>#{suggestion}</td></tr>',
	
	extendedSuggestionBox:
		'<table CELLPADDING=0 CELLSPACING=0 wrap class="wsSuggestions"><tr>' +
		  '#{enrichmentSuggestion}#{thesaurusSuggestion}' +
		'</tr></table>',

	extendedSuggestionHTML:
		'<td valign="top" class="wsSuggestionsColumn"><table>' + 
		  '<th align="left" class="wsSuggestionsHeader" style="color:#{sColor};">#{sHeader}</th>' +
		  '#{suggestion}' +
		'</table></td>',

	/*
	enrichmentSuggestionHTML:
		'<td id="wsAdd" valign="top" class="wsSuggestionsColumn"><table>' + 
		  '<th align="left" class="wsSuggestionsHeader" style="color:blue;">Enrichments</th>' +
		  '#{suggestion}' +
		'</table></td>',

	thesaurusSuggestionHTML:
		'<td id="wsReplace" valign="top" class="wsSuggestionsColumn"><table>' + 
		  '<th align="left" class="wsSuggestionsHeader" style="color:blue;">Thesaurus</th>' + 
		  '#{suggestion}' + 
		'</table></td>',		
	*/	
		
	/*	
	suggestionBox: '<table CELLPADDING="0" CELLSPACING="0"><tr valign="top"><td><table wrap class="wsSuggestions"><tr><td id="wsReplace" valign="top" class="wsSuggestionsColumn"><table><th align="left" class="wsSuggestionsHeader" style="color:#{sColor};">#{sHeader}</th>' + '#{suggestion}' + '</table></td></tr></table>',
	suggestionHTML: '<tr><td colspan="3" id="#{id}" class="wsSuggestion">' + '#{suggestion}' + '</td></tr>',
	extendedSuggestionBox: '<table CELLPADDING=0 CELLSPACING=0 wrap class="wsSuggestions"><tr>' + '#{enrichmentSuggestion}#{thesaurusSuggestion}' + '</tr></table>',
	enrichmentSuggestionHTML: '<td id="wsAdd"    valign="top" class="wsSuggestionsColumn"><table><th align="left" class="wsSuggestionsHeader" style="color:blue;">Enrichments</th>' + '#{suggestion}' + '</table></td>',
	thesaurusSuggestionHTML: '<td id="wsReplace" valign="top" class="wsSuggestionsColumn"><table><th align="left" class="wsSuggestionsHeader" style="color:blue;">Thesaurus</th>'   + '#{suggestion}' + '</table></td>',
	*/

	//generalwordClassname: 'mceItemWS',
	//enrichmentClassname: 'mceItemWSEnrichment',
	//spellingClassname: 'mceItemWSSpelling',
	//grammarClassname: 'mceItemWSGrammar',
	/*
	colors: {
		general: "black",
		grammar: "limegreen",
		spelling: "red",
		enrichment: "blue"
	},
	*/

	enrichSettings: {
		general: {color: "black", title: "General", name: "wsGeneralSuggestion", className: "mceItemWS"},
		grammar: {color: "green", title: "Grammar", name: "wsGrammarSuggestion", className: "mceItemWSGrammar"},	
		spelling: {color: "red", title: "Spelling", name:"wsSpellingSuggestion", className: "mceItemWSSpelling"},
		enrichment: {color: "blue", title: "Enrichment", name:"wsEnrichmentSuggestion", className: "mceItemWSEnrichment"},
		thesaurus: {color: "blue", title: "Thesaurus", name: "wsThesaurusSuggestion", className: "mceItemWSEnrichment"}
	},

	suggestionSettings: {
		single: {className: "wsSuggestion"},
		box: {className: "wsSuggestions"},
		header: {className: "wsSuggestionsHeader"},
		column: {className: "wsSuggestionsColumn"},
		mouseOver: {className: "wsSuggestionOver"},
		disabled: {className: "wsSuggestionDisabled"}
	},

	/*
	toolbarLocation: "bottom",
	toolbarItems: "spellcheck,undo,redo,config,debug",
	toolbarSnippet: '<div class="whitesmokeMenuBarContainer"><table class="whitesmokeMenuBarTable"><tr>#{menubaritems}</tr></table></div>',
	toolbarItemSnippet: '<td><a href="javascript:#{action}"><img src="#{buttonUrl}" #{imageactions} id="#{buttonId}"/></a></td>'
	*/

	autoCreateToolBar: true,
	toolbar: {
		location: "bottom",
		items: "spellcheck,undo,redo,config,debug",
		snippet: '<div class="whitesmokeMenuBarContainer"><table class="whitesmokeMenuBarTable"><tr>#{menubaritems}</tr></table></div>',
		itemSnippet: '<td><a href="javascript:#{action}"><img src="#{buttonUrl}" #{imageactions} id="#{buttonId}"/></a></td>'
	}
}

/********************************************************************************
Class WSEnrichmentRecord
********************************************************************************/
function WSEnrichmentRecord(sWord, nPos, nLength, nLangType, nWordCount) {
	this.sWord = sWord;
	this.nPos = nPos;
	this.nLength = nLength;
	this.nLangType = nLangType;
	this.nWordCount = nWordCount;
	this.grammar = new Array();
	this.spelling = new Array();
	this.thesaurus = new Array();
	this.enrichment = new Array();
	this.dictionary = new Array();
}
WSEnrichmentRecord.prototype.add = function(nEnrichType, sWord, nScore) {
	var enrichType = {
		MATCHING: 3,
		THESAURUS: 4,
		IDIOM: 5,
		SPELL: 6,
		GRAMMAR: 7,
		DICTIONARY: 8
	};
	switch (nEnrichType) {
	case enrichType.MATCHING:
		this.enrichment.push(sWord);
		break;
	case enrichType.THESAURUS:
		this.thesaurus.push(sWord);
		break;
	case enrichType.IDIOM:
		break;
	case enrichType.SPELL:
		this.spelling.push(sWord);
		break;
	case enrichType.GRAMMAR:
		this.grammar.push(sWord);
		break;
	case enrichType.DICTIONARY:
		this.dictionary.push(sWord);
		break;
	default:
		alert("unknown type...");
	}
}

/********************************************************************************
********************************************************************************/
function haveThisIndexAlready(wIndex) {
	var i = 0;
	for (i = 0; i < g_summaryTempArray.length; i++) {
		if (g_summaryTempArray[i] == wIndex) {
			return true;
		}
	}
	return false;
}

function wsGetEnrichmentInfo(sResponseBody, vTokens, engineInstance) {
	engineInstance.warningMessage = "";
	g_summaryTempArray = new Array();
	g_wordIndexScoringArray = new Array();

	g_summaryObj = new Object();
	g_summaryObj.enrichments = 0;
	g_summaryObj.spelling = 0;
	g_summaryObj.grammar = 0;

	if (sResponseBody.length == 0) {
		return;
	}
	var vResponse = sResponseBody.split("\n");
	var nWords = parseInt(vResponse[0]);
	var nSize = vResponse.length - 1;
	var sEnrichmentInfo = "";
	var vEnrichInfo = new Array();
	var nTokenOffset = 0;
	for (var j = 0; j < nWords; ++j) {
		var i = j * 9 + 1;
		var nWordIndex = parseInt(vResponse[i]);
		var nWordCount = parseInt(vResponse[i + 2]);
		var nLangType = parseInt(vResponse[i + 3]);
		var nEnrichType = parseInt(vResponse[i + 4]);
		var nScore = parseInt(vResponse[i + 5]);
		var sEnrichWord = vResponse[i + 8];
		if (!haveThisIndexAlready(nWordIndex)) {
			g_summaryTempArray.push(nWordIndex);
			addToSummaryObject(nEnrichType);
		}
		if (nWordIndex == -1) {
			if (nEnrichType == 1 || nEnrichType == 2) {
				engineInstance.warningMessage = sEnrichWord;
			}
			else if (nEnrichType == 8) {}
		}
		else {
			if (!vEnrichInfo[nWordIndex]) {
				var nTokenIndex = nWordIndex + nTokenOffset;
				nTokenOffset += nWordCount - 1;
				var oToken = vTokens[nTokenIndex];
				if (!oToken) {
					vEnrichInfo = [];
					return;
				}
				var nPos = parseInt(oToken.nPos);
				var oTmpToken = vTokens[nTokenIndex + nWordCount - 1];
				var nLength = (parseInt(oTmpToken.nPos) - nPos) + parseInt(oTmpToken.nLength);
				var sWord = "";
				for (var k = 0; k < nWordCount; ++k) {
					if (sWord.length > 0) {
						sWord += " ";
					}
					sWord += vTokens[nTokenIndex + k].sWord;
				}
				//alert("vEnrichInfo["+nWordIndex+"]=("+sWord+","+nPos+","+nLength+","+nLangType+","+nWordCount+")");
				vEnrichInfo[nWordIndex] = new WSEnrichmentRecord(sWord, nPos, nLength, nLangType, nWordCount);
			}
			vEnrichInfo[nWordIndex].add(nEnrichType, sEnrichWord, nScore);
		}
	}
	var nTokenOffset = 0;
	for (var i = 0; i < vTokens.length; ++i) {
		if (i < vEnrichInfo.length && vEnrichInfo[i]) {
			nTokenOffset += vEnrichInfo[i].nWordCount - 1;
		} else {
			var j = i + nTokenOffset;
			if (j < vTokens.length) {
				vEnrichInfo[i] = new WSEnrichmentRecord(vTokens[j].sWord, vTokens[j].nPos, vTokens[j].nLength, 0, 1);
			}
		}
	}
	return wsConvertToEnrichmentInfo(vEnrichInfo);
}
function wsConvertToEnrichmentInfo(vEnrichInfo) {
	var vEnrichment = new Array();
	for (var i = 0; i < vEnrichInfo.length; ++i) {
		var enrichment = vEnrichInfo[i];
		if (enrichment) {
			vEnrichment[vEnrichment.length] = {
				sWord: enrichment.sWord,
				nPos: enrichment.nPos,
				nLength: enrichment.nLength,
				nLangType: enrichment.nLangType,
				s: enrichment.spelling,
				g: enrichment.grammar,
				t: enrichment.thesaurus,
				e: enrichment.enrichment,
				d: enrichment.dictionary
			};
		}
	}
	return vEnrichment;
}

/********************************************************************************
Class WSHTMLParser
********************************************************************************/
function WSHTMLParser() {}
WSHTMLParser.prototype.isWhiteSpace = function(ch) {
	return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
WSHTMLParser.prototype.isAlpha = function(ch) {
	return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));
}
WSHTMLParser.prototype.isDigit = function(ch) {
	return (ch >= '0' && ch <= '9');
}
WSHTMLParser.prototype.isAlnum = function(ch) {
	return (this.isAlpha(ch) || this.isDigit(ch));
}
WSHTMLParser.prototype.isPunct = function(ch) {
	return (ch == ',' || ch == '.' || ch == ':' || ch == ';' || ch == '!' || ch == '?');
}
WSHTMLParser.prototype.isMiddleChar = function(sText, nPos) {
	return (nPos + 1 < sText.length && nPos - 1 >= 0 && this.isAlnum(sText.charAt(nPos + 1)) && this.isAlnum(sText.charAt(nPos - 1)));
}
WSHTMLParser.prototype.isLegalChar = function(sText, nPos) {
	var ch = sText.charAt(nPos);
	return (this.isAlnum(ch) || ch == '_' || (ch == '\'' && this.isMiddleChar(sText, nPos)));
}
WSHTMLParser.prototype.isInternetAddress = function(sWord) {
	if (sWord.length == 0) return false;
	var i = 0;
	if (sWord.indexOf("www.") == 0) {
		return (sWord.length > 4);
	} else if (sWord.indexOf("http://") == 0) {
		return (sWord.length > 7);
	} else if ((i = sWord.indexOf('@')) > 0) {
		var nDotPos = sWord.indexOf('.', i);
		return (nDotPos >= 0);
	} else return false;
}
WSHTMLParser.prototype.isAbbreviation = function(sWord) {
	if (sWord.length < 2) return false;
	var bDotNow = false;
	for (var i = 0; i < sWord.length - 1; ++i) {
		if (bDotNow) {
			if (sWord.charAt(i) != '.') return false;
		} else {
			if (!this.isAlpha(sWord.charAt(i))) return false;
		}
		bDotNow = !bDotNow;
	}
	var last_char = sWord.charAt(sWord.length - 1);
	return (! (bDotNow && this.isAlpha(last_char)));
}
WSHTMLParser.prototype.isNumber = function(sWord) {
	if (sWord.length == 0) return false;
	var state_comma = false,
	state_dot = true,
	seen_dot = false;
	for (var i = 0; i < sWord.length; ++i) {
		var ch = sWord.charAt(i);
		switch (ch) {
		case '-':
		case '+':
		case '*':
		case '/':
		case '=':
			break;
		case '.':
			{
				if (!state_dot) return false;
				seen_dot = true;
				state_comma = false;
			}
			break;
		case ',':
			{
				if (!state_comma) return false;
				state_comma = false;
			}
			break;
		case 'x':
		case 'X':
			{
				if (i > 0 && sWord.charAt(i - 1) != '0') return false;
			}
			break;
		case 'E':
		case 'e':
			{
				if (i <= length - 2 && !(sWord.charAt(i + 1) == '+' || sWord.charAt(i + 1) == '-')) return false;
			}
			break;
		default:
			{
				if (!this.isDigit(ch)) return false;
				state_comma = true;
			}
		}
	}
	return true;
}
WSHTMLParser.prototype.isWholeWord = function(sWord) {
	return (this.isAbbreviation(sWord) || this.isNumber(sWord) || this.isInternetAddress(sWord));
}
WSHTMLParser.prototype.addToken = function(vTokens, sToken, nPos) {
	if (sToken.length > 0) {
		vTokens[vTokens.length] = {
			sWord: sToken,
			nPos: nPos,
			nLength: sToken.length
		};
	}
	return parseInt(nPos + sToken.length);
}
WSHTMLParser.prototype.extractWords = function(vTokens, sToken, nPos) {
	if (this.isAbbreviation(sToken) || this.isNumber(sToken) || this.isInternetAddress(sToken)) {
		var last_char = sToken.charAt(sToken.length - 1);
		if (this.isPunct(last_char)) {
			var w1 = sToken.substr(0, sToken.length - 1);
			var w2 = last_char;
			nPos = this.addToken(vTokens, w1, nPos);
			nPos = this.addToken(vTokens, w2, nPos);
		} else {
			nPos = this.addToken(vTokens, sToken, nPos);
		}
	} else {
		var sWord = "";
		for (var i = 0; i < sToken.length; ++i) {
			var ch = sToken.charAt(i);
			if (this.isLegalChar(sToken, i)) {
				sWord += ch;
			} else {
				nPos = this.addToken(vTokens, sWord, nPos);
				sWord = "";
				nPos = this.addToken(vTokens, ch, nPos);
				var sSuffix = sToken.substr(i + 1, sToken.length - i - 1);
				if (sSuffix.length > 0) {
					this.extractWords(vTokens, sSuffix, nPos);
				}
				return;
			}
		}
		nPos = this.addToken(vTokens, sWord, nPos);
	}
}
WSHTMLParser.prototype.skipWhitespaces = function(sText, nPos) {
	while (nPos < sText.length && this.isWhiteSpace(sText.charAt(nPos))) {++nPos;
	}
	return nPos;
}
WSHTMLParser.prototype.getToken = function(sText, nPos) {
	var sWord = "";
	for (var i = nPos; i < sText.length; ++i) {
		var ch = sText.charAt(i);
		if (ch == '\n' || ch == '\"') {
			if (sWord.length == 0) sWord += ch;
			else--i;
			break;
		} else if (this.isWhiteSpace(ch)) {
			if (sWord.length > 0) break;
		} else {
			sWord += ch;
		}
	}
	nPos = i + 1;
	nPos = this.skipWhitespaces(sText, nPos);
	return {
		sWord: sWord,
		nPos: nPos
	};
}
WSHTMLParser.prototype.tokenizeText = function(vTokens, sText, nOffset) {
	var nPos = 0;
	nPos = this.skipWhitespaces(sText, nPos);
	for (var oToken = this.getToken(sText, nPos); oToken.sWord.length > 0; oToken = this.getToken(sText, nPos)) {
		this.extractWords(vTokens, oToken.sWord, nPos + nOffset);
		nPos = oToken.nPos;
	}
}
WSHTMLParser.prototype.parseText = function(sText) {
	var vTokens = new Array();
	this.tokenizeText(vTokens, sText, 0);
	return vTokens;
}
WSHTMLParser.prototype.getStartTextPosition = function(strContent, nCurrentPos) {
	var nIndex = nCurrentPos;
	nIndex = this.skipWhitespaces(strContent, nCurrentPos);
	while (nIndex >= 0 && nIndex < strContent.length && strContent.charAt(nIndex) == '<') {
		if (strContent.substr(nIndex, 7) == "<script") {
			nIndex = strContent.indexOf("</script", nIndex);
		} else if (strContent.substr(nIndex, 4) == "<!--") {
			nIndex = strContent.indexOf("-->", nIndex);
		}
		if (nIndex >= 0) {
			nIndex = strContent.indexOf(">", nIndex);
			if (nIndex >= 0) {++nIndex;
			}
		}
		nIndex = this.skipWhitespaces(strContent, nIndex);
	}
	return nIndex;
}
WSHTMLParser.prototype.getStartTagPosition = function(strContent, nCurrentPos) {
	var nIndex = strContent.indexOf('<', nCurrentPos);
	return nIndex;
}
WSHTMLParser.prototype.getBodyPosition = function(strContent) {
	var nBodyIndex = strContent.indexOf("<body");
	if (nBodyIndex >= 0) {
		nBodyIndex = this.getStartTextPosition(strContent, nBodyIndex);
	} else {
		nBodyIndex = 0;
	}
	return nBodyIndex;
}
WSHTMLParser.prototype.findStartTokenIndex = function(strContent, nCurrentPos) {
	for (var nStart = nCurrentPos; nStart < strContent.length && (strContent.charAt(nStart) == '&' || strContent.charAt(nStart) == ' '); ++nStart) {}
	if (nStart == nCurrentPos || nStart >= strContent.length) {
		return nStart;
	} else if (strContent.charAt(nStart - 1) == '&') {
		var nFinish = this.findEndEscTokenIndex(strContent, nStart);
		if (nFinish >= 0 && nFinish < strContent.length) {++nFinish;
			nStart = this.findStartTokenIndex(strContent, nFinish);
		}
	}
	return nStart;
}
WSHTMLParser.prototype.findEndEscTokenIndex = function(strContent, nCurrentPos) {
	var nFinish = 0;
	for (nFinish = nCurrentPos; nFinish < strContent.length && nFinish < nCurrentPos + 5 && strContent.charAt(nFinish) != ';'; ++nFinish) {}
	if (nFinish >= 0 && nFinish < strContent.length && strContent.charAt(nFinish) != ';') {
		nFinish = -1;
	}
	return nFinish;
}
WSHTMLParser.prototype.findEndTokenIndex = function(strContent, nCurrentPos) {
	for (var nFinish = nCurrentPos; nFinish < strContent.length && strContent.charAt(nFinish) != ' ' && strContent.charAt(nFinish) != ';' && strContent.charAt(nFinish) != '&'; ++nFinish) {}
	if (nFinish >= 0 && nFinish < strContent.length && strContent.charAt(nFinish) != ';') {--nFinish;
	}
	return nFinish;
}
WSHTMLParser.prototype.getHTMLToken = function(strContent, nCurrentPos) {
	var nStart, nFinish;
	var sToken = "";
	for (; nCurrentPos < strContent.length; ++nCurrentPos) {
		nStart = this.findStartTokenIndex(strContent, nCurrentPos);
		if (nStart < 0 || nStart >= strContent.length) {
			nCurrentPos = strContent.length;
			break;
		}
		nFinish = this.findEndTokenIndex(strContent, nStart);
		if (nFinish < 0 || nFinish >= strContent.length) {
			nFinish = strContent.length - 1;
		}
		sToken = strContent.substr(nStart, nFinish - nStart + 1);
		nCurrentPos = nFinish;
		if (sToken.length > 0) {
			break;
		}
	}
	return {
		sWord: sToken,
		nStart: nStart,
		nFinish: nFinish
	};
}
WSHTMLParser.prototype.tokenizeHTML = function(vTokens, strSentence, nOffset) {
	var nPos = 0;
	for (var oToken = this.getHTMLToken(strSentence, nPos); oToken.sWord.length > 0; oToken = this.getHTMLToken(strSentence, nPos)) {
		this.tokenizeText(vTokens, oToken.sWord, nOffset + oToken.nStart);
		nPos = oToken.nFinish + 1;
	}
}
WSHTMLParser.prototype.parseHTML = function(strOriginalContent) {
	var strContent = strOriginalContent.toLowerCase();
	var nStartText = 0,
	nEndText = 0;
	var vTokens = new Array();
	nStartText = this.getBodyPosition(strContent);
	for (; nStartText >= 0 && nStartText < strContent.length; nStartText = nEndText) {
		nStartText = this.getStartTextPosition(strContent, nStartText);
		if (nStartText < 0) {
			break;
		}
		nEndText = this.getStartTagPosition(strContent, nStartText);
		if (nEndText < 0 || nEndText >= strContent.length) {
			nEndText = strContent.length;
		}
		var strSentence = strOriginalContent.substr(nStartText, nEndText - nStartText);
		this.tokenizeHTML(vTokens, strSentence, nStartText);
	}
	return vTokens;
}

/********************************************************************************
Class WSEnrichmentEngine
********************************************************************************/
var g_oWSEnrichmentsArray = [];
function WSEnrichmentEngine(settings, editorId, editorInstance) {
	this.config = {};
	if (settings) {
		for (val in settings) {
			//alert("WSEnrichmentDefaultConfig["+val+"] = " + WSEnrichmentDefaultConfig[val]);
			WSEnrichmentDefaultConfig[val] = settings[val];
		}
	}
	this.configure(WSEnrichmentDefaultConfig);
	//this.configure(settings);
	this.editorId = editorId;
	this.editorInstance = editorInstance;
	whitesmokePlugin.runningEngines[this.editorId] = this;
	g_oWSEnrichmentsArray.push(this);
	this.wsEditorNumber = g_oWSEnrichmentsArray.length - 1;

	//-------------------------------------------------------------------------------
	//fixme - move to config
	/*
	this.resultElementsInfo = {
		suggestions: {id: "wsSuggestionsResult", className: "wsSuggestionsResult"},
		dictionary:  {id: "wsDictionaryResult",  className: "wsDictionaryResult"},
		feedback:    {id: "wsFeedbackResult", className: "wsFeedbackResult"},		
		progress:    {id: "wsProgress", className: "wsProgress"}
	};

	var bTrial = null;
	this.bTrial = (bTrial ? bTrial: false);

	var sMode = "full"; //move to config
	this.sMode = (sMode && sMode.length > 0 ? sMode: "full"); //move to config
	var sFormat = "html"; //move to config
	this.sFormat = (sFormat && sFormat.length > 0 ? sFormat: "html"); //move to config
	this.sProfile = this.config.profile; //move to config
	this.nMaxSuggestions = (sMode == "min" ? 4 : 6);
	this.nMaxContentLength = 10000;
	
	this.sRSFrameId = "WSRSIFrame";
	this.stLangType = {
		unknown: 0,
		verb: 4,
		noun: 5,
		adjective: 6,
		adverb: 7
	};
	*/
	//-----------------------------------------------------------------------------
	//var sContent = "";
	//this.sContent = (sContent ? sContent: "");
	this.sContent = ""; //(this.config.sInitailContent ? this.config.sInitailContent: "");
	
	this.vTokens = new Array();
	this.vSentenceInfo = new Array();
	this.sOriginalContent = this.sContent;
	this.vEnrichment = new Array();
	this.vAddedWords = new Array();
	this.sParent = "parent";
	this.oParent = parent;
	this.nSelectedIndex = -1;
	this.state = "";
	this.timerID = null;
	this.vDictionaryCache = new Array();
	this.undoInfo = new Array();
	this.redoInfo = new Array();
	this.stLastPointedInfo = {
		e: null,
		oMousePos: null,
		sWord: "",
		nLangTYpe: 0,
		nIndex: 0,
		oElement: null,
		vDictionary: new Array()
	};
	this.stPrevElementStyle = {};
	this.vTemplatesMenu = new Array();
}
WSEnrichmentEngine.prototype = {
	configure: function(obj) {
		if (obj) {
			for (val in obj) {
				switch (val) {
				case "globalProgressWindow":
					WSEnrichmentDefaultConfig[val] = obj[val];
				default:
					this.config[val] = obj[val];
				}
			}
		}
	},
	initLoops: 100,
	initialize: function() {
		var doc = this.getEditorDocument();
		if (!doc || !doc.body) {
			if (this.initLoops--) {
				setTimeout("g_oWSEnrichmentsArray[" + this.wsEditorNumber + "].initialize();", 100);
				WSDebug.log("Postphoning initialize", "initialize");
			} else {
				WSDebug.log("Failed initialize", "initialize");
			}
			return;
		}
		//whitesmokePlugin.importCSS(this.getEditorDocument(), "css/content.css");
		//whitesmokePlugin.importCSS(this.getEditorDocument(), "whitesmoke_plugin/css/content.css");
		/*
		if (whitesmokePlugin.isMSIE) {
			var doc = this.getEditorDocument().body;
			var m = WSDomUtils.getStyle(doc, "margin-bottom");
			if (!parseInt(m)) doc.style.marginBottom = "1px";
		}
		*/
		//whitesmokePlugin.importCSS(this.getEditorDocument(), "css/content.css");
		//whitesmokePlugin.importCSS(this.getEditorDocument(), "whitesmoke_plugin/css/content.css");
		if (this.config.autoCreateToolBar) {
			this.createToolBar();
		}
		this.createUIElements();
		this.attachUIEvents();
		if (this.config.autoCheck.onInit) {
			this.updateContent();
		}
	},
	destroy: function() {},
	createToolBar: function() {
		var items = this.config.toolbar.items.split(",");
		var str = "";
		for (var i = 0; i < items.length; i++) {
			str += this.createToolBarItemHTML(items[i]);
		}
		var t = new WSTemplate(this.config.toolbar.snippet);
		var str = t.evaluate({
			menubaritems: str
		});
		var d = document.createElement("div");
		d.innerHTML = str;
		this.getEditorElement().parentNode.appendChild(d.firstChild);
	},
	createToolBarItemHTML: function(item) {
		var t = new WSTemplate(this.config.toolbar.itemSnippet);
		//alert(whitesmokePlugin.baseURL + "/images/" + item + ".gif");
		return t.evaluate({
			action: "void(whitesmokePlugin.doCommand('" + item + "','" + this.editorId + "'));",
			buttonUrl: whitesmokePlugin.baseURL + "/images/" + item + ".gif"
		});
	}
}
WSEnrichmentEngine.prototype.free = function() {
	this.removeEventHandler();
	this.sContent = null;
	this.vTokens = null;
	this.vEnrichment = null;
	this.vDictionaryCache = null;
	this.setEditorContent("");
}
WSEnrichmentEngine.prototype.getEditorElement = function() {
	return document.getElementById(this.editorId);
}
WSEnrichmentEngine.prototype.getElementById = function(sId) {
	return this.getEditorDocument().getElementById(sId);
}
WSEnrichmentEngine.prototype.getElementByIndex = function(nIndex) {
	var sId = "wsWord" + nIndex;
	return this.getElementById(sId);
}
WSEnrichmentEngine.prototype.openConfigWindow = function() {
	var width = 500;
	var height = 400;
	var options = "";
	var url = whitesmokePlugin.baseURL + "/config.htm";
	var win = "ConfigWindow";
	var leftPos = (screen.availWidth - width) / 2;
	var topPos = (screen.availHeight - height) / 2;
	options += 'width=' + width + ',height=' + height + ',top=' + topPos + ',left=' + leftPos;
	window.open(url, win, options);
}

/********************************************************************************
Class WSEE
********************************************************************************/
WSEE = {};
WSEE.ui = {
	getCustomId: function(id) {
		return new String(id + "_" + this.wsEditorNumber);
	},
	createUIElements: function() {
		WSDebug.log("Creating elements for popups...", "createWSElements");
		var editorElement = this.getEditorElement();
		for (var sTagID in this.config.resultElementsInfo) {
			var oElement = document.createElement('div');
			var oRes = this.config.resultElementsInfo[sTagID];
			var id = this.getCustomId(oRes.id);
			oElement.id = id;
			oElement.name = name;
			oElement.className = oRes.className;
			oElement.style.position = 'absolute';
			oElement.style.zIndex = '200';
			oElement.style.display = 'none';
			editorElement.parentNode.appendChild(oElement);
			//this.getEditorDocument().body.appendChild(oElement);
		}
		return true;
	},
	removeUIElements: function() {
		WSDebug.log("Removing elements for popups", "removeWSElements");
		for (var sTagID in this.config.resultElementsInfo) {
			var oRes = this.config.resultElementsInfo[sTagID];
			var id = this.getCustomId(oRes.id);
			var oElement = document.getElementById(id);
			//var oElement = this.getEditorElement().getElementById(id);
			if (oElement) {
				oElement.parentNode.removeChild(oElement);
				//this.getEditorElement().removeChild(oElement);
			}
		}
	},
	getSpecificResultElement: function(oResElement) {
		if (document) {
			var id = this.getCustomId(oResElement.id);
			return document.getElementById(id);
			//return this.getEditorDocument().getElementById(id);
		}
		return null;
	},
	getSuggestionsElement: function() {
		return this.getSpecificResultElement(this.config.resultElementsInfo.suggestions);
	},
	getDictionaryElement: function() {
		return this.getSpecificResultElement(this.config.resultElementsInfo.dictionary);
	},
	getFeedbackElement: function() {
		return this.getSpecificResultElement(this.config.resultElementsInfo.feedback);
	},
	getProgressElement: function() {
		return this.getSpecificResultElement(this.config.resultElementsInfo.progress);
	}
};
whitesmokePlugin.add(WSEnrichmentEngine, WSEE.ui);

/*************************************************************************************/
WSEnrichmentEngine.prototype.displayContent = function() {
	WSDebug.log("displayContent");
	var sContent = this.getEnrichedContent();
	//alert("displayContent: sContent = " + sContent);
	this.setEditorContent(sContent);
	if (this.warningMessage.length) { // Display the warning
		//alert("displayContent: this.warningMessage=\n" + this.warningMessage);
		//We have a message to display
		//var stripedMessage = this.warningMessage.substr(this.warningMessage.indexOf('"')+1, this.warningMessage.lastIndexOf('"')-this.warningMessage.indexOf('"')-1 );
		//alert(this.warningMessage.indexOf('"')+1);
		var start_idx = this.warningMessage.indexOf('"')+1;
		var end_idx  = this.warningMessage.lastIndexOf('"');
		var tempMsg = this.warningMessage.substr(start_idx, end_idx - start_idx);
		
		var tmp_elm = document.createElement('div');
		tmp_elm.innerHTML = tempMsg;
		var nodes = tmp_elm.getElementsByTagName('div');
		for (var i=0; i<nodes.length; ++i) {
			//alert("nodes["+i+"].className = " + nodes[i].className);
			if (nodes[i].className == "screenContent") { 
				//alert("screenContent\n" + nodes[i].innerHTML);
				tempMsg = nodes[i].innerHTML;
			}
		}
		delete tmp_elm;
		//tempMsg = tempMsg.replace(/ServerFeedbackXButton/, "");
		//alert("displayContent: tempMsg=\n" + tempMsg);
		var template = new WSTemplate(this.config.feedbackTemplate);
		var stripedMessage  = template.evaluate({
				sMessage: tempMsg
				//okButton: '<div id="ServerFeedbackOKButton"></div>',
				//downloadButton: '<div id="ServerFeedbackDownloadButton"></div>'
			});
		//var stripedMessage = '<div class="screenContainerMsg"><div id="innerContainer"><div id="bgTopMsg"><div class="left"></div><div class="center" style="color:#fff"><div id="questionMarkImg"></div><div id="ServerFeedbackXButton"></div></div><div class="right"></div></div><div id="screenContentMsg"><div class="insideScreenMsg" style="height:auto;"><div style="height:75px;">' + tempMsg + '</div><a href="http://www.whitesmoke.com/" target="_blank"></a></div></div><div id="bgBottomImg"></div></div></div>';
		//alert("displayContent: stripedMessage = " + stripedMessage);
		this.ShowServerFeedback(stripedMessage);
		this.warningMessage = "";
	}
	setTimeout(this.simpleClosure(this.setWordsEventHandler), 70);
	return true;
}

WSEnrichmentEngine.prototype.displaySummaryNotice = function() {
	var sum = g_summaryObj.enrichments + g_summaryObj.spelling + g_summaryObj.grammar;
	if (sum == 0) {
		return;
	}
	var pStr = "<table id='wsSummaryNotice' cellspacing=0 cellpadding=0 border=0>" +
			   "<th class='header_li'>WhiteSmoke found:</th>";
			   		
	if (g_summaryObj.grammar > 0) {
		pStr += "<tr><td class='grammar_li'>" + 
				g_summaryObj.grammar + " grammar mistake" + 
				(g_summaryObj.grammar==1 ? "" : "s") + "</td></tr>";
	}
	if (g_summaryObj.enrichments > 0) {
		pStr += "<tr><td class='style_li'>" + 
				g_summaryObj.enrichments + " style enhancement" + 
				(g_summaryObj.enrichments==1 ? "" : "s") + "</td></tr>";
	}
	if (g_summaryObj.spelling > 0) {
		pStr += "<tr><td class='spelling_li'>" + 
				g_summaryObj.spelling + " spelling mistake" + 
				(g_summaryObj.grammar==1 ? "" : "s") + "</td></tr>";
	}
	pStr += "</table>";

	var template = new WSTemplate(this.config.feedbackTemplate);
	var str  = template.evaluate({
					sMessage: pStr,
					okButton: '<HR><div id="ServerFeedbackOKButton"></div>'
					//okButton: '<div id="ServerFeedbackOKButton" style="float:right;"></div>'
					//downloadButton: '<div id="ServerFeedbackDownloadButton" style="float:left;"></div>'
				});
	this.ShowServerFeedback(str);
};


WSEnrichmentEngine.prototype.displaySummaryNotice2 = function() {
	var sum = g_summaryObj.enrichments + g_summaryObj.spelling + g_summaryObj.grammar;
	if (sum == 0) {
		return;
	}
	//<span id="ws_detect">WhiteSmoke found:</span>
	var pStr = "<div style='color:black; font-weight:bold;'>WhiteSmoke found:</div>";
	pStr += "<ul>";
	if (g_summaryObj.grammar > 0) {
		pStr += "<li class='grammar_li'>" + g_summaryObj.grammar + 
				" grammar mistake" + (g_summaryObj.grammar==1 ? "" : "s") + "</li>";
	}
	if (g_summaryObj.enrichments > 0) {
		pStr += "<li class='style_li'>" + g_summaryObj.enrichments + 
				" style enhancement" + (g_summaryObj.enrichments==1 ? "" : "s") + "</li>";
	}
	if (g_summaryObj.spelling > 0) {
		pStr += "<li class='spelling_li'>" + g_summaryObj.spelling + 
				" spelling mistake" + (g_summaryObj.grammar==1 ? "" : "s") + "</li>";
	}
	pStr += "</ul><div style=\"clear:both; height:0px; overflow:hidden;\"></div>";

	//var str = '<div class="screenContainerMsg" id="scrCont"><div id="innerContainer"><div id="bgTopMsg"><div class="left"></div><div class="center" style="color:#fff"><div id="questionMarkImg"></div><span id="ws_detect">WhiteSmoke found:</span><div id="ServerFeedbackXButton"></div></div><div class="right"></div></div><div id="screenContentMsg"><div class="insideScreenMsg" style="height:auto;"><div style="height:75px;">	' + pStr + '</div><div id="ServerFeedbackOKButton" style="float:right;"></div><div id="ServerFeedbackDownloadButton" style="float:left;"></div></div></div><div id="bgBottomImg"></div></div></div>';
	//var str = '<div class="screenContainerMsg" id="scrCont"><div id="innerContainer"><div id="bgTopMsg"><div class="left"></div><div class="center" style="color:#fff"><div id="questionMarkImg"></div><div id="ServerFeedbackXButton"></div></div><div class="right"></div></div><div id="screenContentMsg"><div class="insideScreenMsg" style="height:auto;"><div style="height:75px;">	' + pStr + '</div><div id="ServerFeedbackOKButton" style="float:right;"></div><div id="ServerFeedbackDownloadButton" style="float:left;"></div></div></div><div id="bgBottomImg"></div></div></div>';
	
	var template = new WSTemplate(this.config.feedbackTemplate);
	var str  = template.evaluate({
					sMessage: pStr,
					okButton: '<HR><div id="ServerFeedbackOKButton"></div>'
					//okButton: '<div id="ServerFeedbackOKButton" style="float:right;"></div>'
					//downloadButton: '<div id="ServerFeedbackDownloadButton" style="float:left;"></div>'
				});
	this.ShowServerFeedback(str);
};

WSEnrichmentEngine.prototype.SetFeedbackBehavior = function(oResElement) {
	var o3,o4,o5;
	if (o3 = document.getElementById("ServerFeedbackOKButton"))
	{
		WSDomUtils.addEvent(o3,'click',
			function () {
				WSUtils.hideElement(oResElement);
				//wsEngine.setFocus();
				//if (document.getElementById("WSEdit")) {
				//	tinyMCE.execCommand("mceFocus", true, "WSEdit");
				//}
			});
	}
	if (o4 = document.getElementById("ServerFeedbackXButton")) {
		WSDomUtils.addEvent(o4,'click',
			function() {
				WSUtils.hideElement(oResElement);
				//if (document.getElementById("WSEdit")) {
				//	tinyMCE.execCommand("mceFocus", true, "WSEdit");
				//}
			});
	}
	if (o5 = document.getElementById("ServerFeedbackDownloadButton")) {
		WSDomUtils.addEvent(o4,'click',
			function() {
				window.open("https://buy.whitesmoke.com/", "", "fullscreen=yes,menubar=yes,resizable=yes,scrollbars=yes,titlebar=yes,toolbar=yes");
			});
	}
};


WSEnrichmentEngine.prototype.ShowServerFeedback = function(html) {
	//alert(html);
	this.showFeedbackMessage(html);
	return;
	/*
	//alert("ShowServerFeedback");
	//alert("o  = " + document.getElementById("ScreenFeedback"));
	//alert("o2 = " + document.getElementById("ScreenFeedbackTd"));
	var o,o2;
	if ((o = document.getElementById("ScreenFeedback")) &&	//exist in the parent
		(o2 = document.getElementById("ScreenFeedbackTd")))
	{
		WSUtils.showElement(o);
		o2.innerHTML = html;
	}
	else {
		return;
	}
	this.SetFeedbackBehavior(o);
	*/
};
/*************************************************************************************/

g_summaryObj = new Object();
g_summaryObj.enrichments = 0;
g_summaryObj.spelling = 0;
g_summaryObj.grammar = 0;
function addToSummaryObject(nEnrichType) {
	var enrichTypeEnum = {
		MATCHING: 3,
		THESAURUS: 4,
		IDIOM: 5,
		SPELL: 6,
		GRAMMAR: 7,
		DICTIONARY: 8
	};
	switch (nEnrichType) {
	case enrichTypeEnum.THESAURUS:
		g_summaryObj.enrichments++;
		break;
	case enrichTypeEnum.SPELL:
		g_summaryObj.spelling++;
		break;
	case enrichTypeEnum.GRAMMAR:
		g_summaryObj.grammar++;
		break;
	}
}
WSEnrichmentEngine.prototype.simpleClosure = function(aMethod) {
	var obj = this;
	return function() {
		aMethod.apply(obj)
	};
}
WSEnrichmentEngine.prototype.eventClosure = function(aMethod) {
	var obj = this;
	return function(e) {
		aMethod.apply(obj, [e])
	};
}
WSEnrichmentEngine.prototype.isAlpha = function(sWord) {
	for (var i = 0; i < sWord.length; ++i) {
		var ch = sWord.charCodeAt(i);
		if ((ch < 65 || ch > 90) && (ch < 97 || ch > 122)) {
			return false;
		}
	}
	return true;
}
WSEnrichmentEngine.prototype.cleanWSContentById = whitesmokePlugin.cleanWSContentById;
WSEnrichmentEngine.prototype.cleanWSContent = whitesmokePlugin.cleanWSContent;
WSEnrichmentEngine.prototype.getSuggestionsLength = function(enrichment) {
	var nLength = 0;
	if (enrichment) {
		if (enrichment.s && enrichment.s.length > 0) {
			nLength = enrichment.s.length;
		} else if (enrichment.g && enrichment.g.length > 0) {
			nLength = enrichment.g.length;
		} else if ((enrichment.t && enrichment.t.length > 0) || (enrichment.e && enrichment.e.length > 0)) {
			nLength = Math.max(enrichment.t.length, enrichment.e.length);
		}
	}
	return Math.min(nLength, this.config.nMaxSuggestions);
}
WSEnrichmentEngine.prototype.getVectorWidth = function(vSuggestions) {
	var nMaxWidth = 0;
	if (vSuggestions) {
		for (var i = 0; i < vSuggestions.length; ++i) {
			if (nMaxWidth < vSuggestions[i].length) {
				nMaxWidth = vSuggestions[i].length;
			}
		}
	}
	return nMaxWidth;
}
WSEnrichmentEngine.prototype.getSuggestionsWidth = function(enrichment) {
	var nMaxWidth = 0;
	if (enrichment) {
		if (enrichment.s && enrichment.s.length > 0) {
			nMaxWidth = Math.max(8, this.getVectorWidth(enrichment.s)) + 3;
		} else if (enrichment.g && enrichment.g.length > 0) {
			nMaxWidth = Math.max(8, this.getVectorWidth(enrichment.g)) + 3;
		} else if ((enrichment.t && enrichment.t.length > 0) || (enrichment.e && enrichment.e.length > 0)) {
			var nMax1 = 0,
			nMax2 = 0;
			if (enrichment.t && enrichment.t.length > 0) {
				nMax1 = Math.max(10, this.getVectorWidth(enrichment.t)) + 3;
			}
			if (enrichment.e && enrichment.e.length > 0) {
				nMax2 = Math.max(10, this.getVectorWidth(enrichment.e)) + 3;
			}
			nMaxWidth = nMax1 + nMax2;
		}
	}
	var pxWidth = nMaxWidth * 8;
	return pxWidth;
}
WSEnrichmentEngine.prototype.g_nPrevKeyCode = null;
WSEnrichmentEngine.prototype.onHandleKeyUp = function(e) {
	var nKeyCode = e.keyCode;
	if (nKeyCode == 27) {
		this.hideSuggestions();
	}
	if (nKeyCode != this.g_nPrevKeyCode) {
		var sKeyValue = String.fromCharCode(nKeyCode);
		if (nKeyCode == 190 || nKeyCode == 46 || sKeyValue == ".") {
			this.g_nPrevKeyCode = nKeyCode;
			if (this.config.autoCheck.whileTyping) {
				this.updateContent();
			}
		}
		if ((nKeyCode >= 65 && nKeyCode <= 90) || (nKeyCode >= 97 && nKeyCode <= 122)) {
			this.g_nPrevKeyCode = nKeyCode;
		}
		return true;
	}
	return true;
}
WSEnrichmentEngine.prototype.onHandleMouseUp = function(e) {
	this.hideSuggestions();
	return false;
}
WSEnrichmentEngine.prototype.onMouseClickWord = function(e) {
	WSSharedEnrichmentEngine.stopDictionaryTimer();
	if (e) {
		var oElement = WSUtils.getEventElement(e);
		if (!oElement) {
			return false;
		}
		var sWord = oElement.innerHTML;
		var nSelectedIndex = WSUtils.getElementIndex(oElement);
		while (nSelectedIndex < 0 && oElement.parentNode) {
			oElement = oElement.parentNode;
			nSelectedIndex = WSUtils.getElementIndex(oElement);
		}
		var enrichment = this.vEnrichment[nSelectedIndex];
		if (enrichment) {
			if (enrichment.s && enrichment.s.length > 0) {
				this.showSpellingSuggestions(e, nSelectedIndex);
			}
			else if (enrichment.g && enrichment.g.length > 0) {
				this.showGrammarSuggestions(e, nSelectedIndex);
			}
			else if ((enrichment.t && enrichment.t.length > 0) || (enrichment.e && enrichment.e.length > 0)) {
				this.showEnrichmentSuggestions(e, nSelectedIndex);
			}
		}
	}
	if (WSUtils.isRightClick(e)) {
		if (document.all) {
			e.returnValue = false;
		}
		return false;
	}
	return false;
}
WSEnrichmentEngine.prototype.setWordsEventHandler = function() {
	if (!this.closures) this.closures = [];
	this.closures["onMouseClickWord"] = this.eventClosure(this.onMouseClickWord);
	for (var nIndex = 0; nIndex < this.vEnrichment.length; ++nIndex) {
		var oElement = this.getElementByIndex(nIndex);
		if (oElement) {
			//WSDomUtils.addEvent(oElement, "contextmenu",this.closures["onMouseClickWord"]);
			WSDomUtils.addEvent(oElement, "mouseup", this.closures["onMouseClickWord"]);
		}
	}
}
WSEnrichmentEngine.prototype.onHandleResize = function() {
	//alert(this.hideSuggestions);
	this.hideSuggestions();
	oFeedback = this.getFeedbackElement()
	//alert("oFeedback.style.display=" + oFeedback.style.display + ", oFeedback.style.visibility=" + oFeedback.style.visibility);
	if (WSUtils.isVisible(oFeedback)) {
		WSUtils.centerElement(oFeedback,window);
	}
	//this.hideProgressBar();

	/*
	var currheight;
	return function() {
		if (currheight != document.documentElement.clientHeight) {
			alert('resized');	
		}
		currheight = document.documentElement.clientHeight;
	}
	*/	
}
WSEnrichmentEngine.prototype.attachUIEvents = function() {
	var oEditorFrame = this.getEditorFrame();
	var oFeedback = this.getFeedbackElement();
	
	if (!this.closures) {
		this.closures = {};
		this.closures.onHandleKeyUp    = this.eventClosure(this.onHandleKeyUp);
		this.closures.onHandleMouseUp  = this.eventClosure(this.onHandleMouseUp);
		this.closures.onMouseClickWord = this.eventClosure(this.onMouseClickWord);
		this.closures.onHandleResize   = this.eventClosure(this.onHandleResize);
	}
	if (oEditorFrame) {
		WSDomUtils.addEvent(oEditorFrame.document, 'keyup',     this.closures.onHandleKeyUp);
		WSDomUtils.addEvent(oEditorFrame.document, 'mousedown', this.closures.onHandleMouseUp);
		WSDomUtils.addEvent(oEditorFrame.document, 'mousedown', this.closures.onMouseClickWord);
		WSDomUtils.addEvent(oEditorFrame.window,   'resize',    this.closures.onHandleResize);
		//WSDomUtils.addEvent(oEditorFrame.window,'resize',function(){self.onHandleResize();});
		//WSDomUtils.addEvent(oEditorFrame.window,'resize',this.eventClosure(this.onHandleResize));
		//WSDomUtils.addEvent(oEditorFrame.window,'resize',self.onResize());
	}
}
WSEnrichmentEngine.prototype.removeEventHandler = function() {
	WSDebug.warning("removing events...", "removeEventHandler");

	var oEditorFrame = this.getEditorFrame();
	if (!this.closures) return;
	if (oEditorFrame && this.closures) {
		WSDomUtils.removeEvent(oEditorFrame.window,   'resize',    this.closures.onHandleResize);
		WSDomUtils.removeEvent(oEditorFrame.document, 'keyup',     this.closures.onHandleKeyUp);
		WSDomUtils.removeEvent(oEditorFrame.document, 'mousedown', this.closures.onHandleMouseUp);
		WSDomUtils.removeEvent(oEditorFrame.document, 'mousedown', this.closures.onMouseClickWord);
	}
	for (var nIndex = 0; nIndex < this.vEnrichment.length; ++nIndex) {
		var oElement = this.getElementByIndex(nIndex);
		if (oElement) {
			WSDomUtils.removeEvent(oElement, "mouseup", this.closures["onMouseClickWord"]);
			WSDomUtils.removeEvent(oElement, "mouseover", this.closures["onMouseOverWord"]);
			WSDomUtils.removeEvent(oElement, "mouseout", this.closures["onMouseOutWord"]);
		}
	}
}
WSEnrichmentEngine.prototype.ContextMenuRequested = function(evnt) {
	return false;
}
WSEnrichmentEngine.prototype.getEnrichedContent = function() {
	var sContent = "";
	var nPrevPos = 0;
	var bBodyExist = false;
	var nBodyPos = this.sContent.indexOf("<body");
	if (nBodyPos < 0) {
		nBodyPos = this.sContent.indexOf("<BODY");
	}
	if (nBodyPos >= 0) {
		WSDebug.warning("Unexpected existance of body", "getEnrichedContent");
		bBodyExist = true;
		nBodyPos += 5;
		sContent += this.sContent.substr(0, nBodyPos);
		var nEndTagPos = this.sContent.indexOf(">", nBodyPos);
		if (nEndTagPos && nEndTagPos >= 0) {++nEndTagPos;
			sContent += this.sContent.substr(nBodyPos, nEndTagPos - nBodyPos);
			nPrevPos = nEndTagPos;
		}
	}
	for (var i = 0; i < this.vEnrichment.length; ++i) {
		var enrichment = this.vEnrichment[i];
		var nPos = enrichment.nPos;
		var nLength = enrichment.nLength;
		var nLangType = enrichment.nLangType;
		var sWord = this.sContent.substr(nPos, nLength);
		for (var j = 0; j < this.vAddedWords.length; ++j) {
			var nTmpPos = this.vAddedWords[j].nPos;
			var nTmpLength = this.vAddedWords[j].nLength;
			var nTmpIndex = this.vAddedWords[j].nIndex;
			var nTmpLangType = this.vAddedWords[j].nLangType;
			if (nTmpPos > nPrevPos && nTmpPos < nPos) {
				sContent += this.sContent.substr(nPrevPos, nTmpPos - nPrevPos);
				var sEnrichedWord = this.sContent.substr(nTmpPos, nTmpLength);
				sContent += this.getFormatedWord(sEnrichedWord, nTmpIndex, nTmpLangType);
				nPrevPos = nTmpPos + nTmpLength;
			}
		}
		sContent += this.sContent.substr(nPrevPos, nPos - nPrevPos);
		nPrevPos = nPos + nLength;
		sContent += this.getFormatedWord(sWord, i, nLangType);
		if (i == this.vEnrichment.length - 1 && this.sContent.indexOf("&nbsp;", nPrevPos) < 0) {
			sContent += "&nbsp;";
		}
	}
	sContent += this.sContent.substr(nPrevPos);
	WSDebug.log("created content: " + WSDomUtils.escapeHTML(sContent), "getEnrichedContent");
	return sContent;
}
WSEnrichmentEngine.prototype.getFormatedWord = function(sWord, nIndex, nLangType) {
	var sContent = "";
	var enrichment = this.vEnrichment[nIndex];
	if (enrichment) {
		var sClassName = this.getClassName(enrichment);
		if (sClassName.length > 0) {
			/*
			grammarWord = '<span id="#{anId}" class="#{}">#{word}</span>';
			enrichWord = '<span id="#{anId}" class="#{}">#{word}</span>';
			spellingWord = '<span id="#{anId}" class="#{}">#{word}</span>';
			*/
			sContent += '<span id="wsWordAdd' + nIndex.toString() + '"></span>';
			sContent += '<span id="wsWord' + nIndex.toString() + '" ';
			sContent += 'class="' + sClassName + '">';
			sContent += sWord;
			sContent += '</span>';
		} else if (sWord.length >= 2 && this.isAlpha(sWord)) {
			sContent += sWord;
		} else {
			sContent += sWord;
		}
	} else {
		sContent += sWord;
	}
	return sContent;
}
WSEnrichmentEngine.prototype.getClassName = function(enrichment) {
	var sClassName = "";
	if (enrichment) {
		if (enrichment.s && enrichment.s.length > 0) {
			sClassName = this.config.enrichSettings.spelling.className;
		} else if (enrichment.g && enrichment.g.length > 0) {
			sClassName = this.config.enrichSettings.grammar.className;
		} else if ((enrichment.t && enrichment.t.length > 0) || (enrichment.e && enrichment.e.length > 0)) {
			sClassName = this.config.enrichSettings.enrichment.className;
		}
	}
	return sClassName;
}
WSEnrichmentEngine.prototype.updateWordStyle = function(nIndex, enrichment) {
	var sClassName = this.getClassName(enrichment);
	var oElement = this.getElementByIndex(nIndex);
	oElement.className = sClassName;
}
WSEnrichmentEngine.prototype.setEnrichmentInfo = function(oEnrichmentInfo) {
	this.vEnrichment = oEnrichmentInfo;
	if (!this.vEnrichment) {
		this.vEnrichment = [];
		this.nSelectedIndex = -1;
		return;
	}
	if (this.vEnrichment.length == 0) {
		this.nSelectedIndex = -1;
	} else if (this.nSelectedIndex >= this.vEnrichment.length) {
		this.nSelectedIndex = 0;
	}
}
WSEnrichmentEngine.prototype.mergeEnrichmentInfo = function(oEnrichmentInfo) {
	if (oEnrichmentInfo && oEnrichmentInfo.length > 0) {
		oEnrichmentInfo[0].sWord = this.vEnrichment[this.nSelectedIndex].sWord;
		oEnrichmentInfo[0].nPos = this.vEnrichment[this.nSelectedIndex].nPos;
		oEnrichmentInfo[0].nLength = this.vEnrichment[this.nSelectedIndex].nLength;
		this.vEnrichment[this.nSelectedIndex] = oEnrichmentInfo[0];
		this.updateWordStyle(this.nSelectedIndex, oEnrichmentInfo[0]);
		this.nSelectedIndex = -1;
	}
}
WSEnrichmentEngine.prototype.setLastPointedInfo = function(e, oMousePos, sWord, nLastLangType, nLastPointedIndex, oLastPointedElement) {
	this.stLastPointedInfo.e = e;
	this.stLastPointedInfo.oMousePos = oMousePos;
	this.stLastPointedInfo.sWord = sWord;
	this.stLastPointedInfo.nLangType = nLastLangType;
	this.stLastPointedInfo.nIndex = nLastPointedIndex;
	this.stLastPointedInfo.oElement = oLastPointedElement;
	this.stLastPointedInfo.vDictionary = new Array();
}
WSEnrichmentEngine.prototype.setDictionaryInfo = function(oEnrichmentInfo) {
	this.stLastPointedInfo.vDictionary = new Array();
	if (oEnrichmentInfo && oEnrichmentInfo.length > 0) {
		this.stLastPointedInfo.vDictionary = oEnrichmentInfo[0].d;
	}
	var sWord = this.stLastPointedInfo.sWord;
	var nLangType = this.stLastPointedInfo.nLangType;
	if (!this.vDictionaryCache[nLangType]) {
		this.vDictionaryCache[nLangType] = new Array(sWord);
	}
	this.vDictionaryCache[nLangType][sWord] = this.stLastPointedInfo.vDictionary;
	return true;
}
WSEnrichmentEngine.prototype.getDictionaryInfo = function(sWord, nLangType) {
	this.stLastPointedInfo.vDictionary = new Array();
	if (this.vDictionaryCache[nLangType] && this.vDictionaryCache[nLangType][sWord]) {
		this.stLastPointedInfo.vDictionary = this.vDictionaryCache[nLangType][sWord];
		return this.stLastPointedInfo.vDictionary;
	}
	return null;
}
WSEnrichmentEngine.prototype.hideProgressBar = function() {
	var oElement = this.getProgressElement();
	if (oElement) {
		WSUtils.hideElement(oElement);
	}
}
WSEnrichmentEngine.prototype.hideDictionary = function() {
	WSSharedEnrichmentEngine.stopDictionaryTimer();
	var oElement = this.getDictionaryElement();
	if (oElement) {
		WSUtils.hideElement(oElement);
	}
}
WSEnrichmentEngine.prototype.hideSuggestions = function() {
	this.hideDictionary();
	var oElement = this.getSuggestionsElement();
	if (oElement) {
		WSUtils.hideElement(oElement);
	}
	this.markWord( - 1);
}
WSEnrichmentEngine.prototype.getMousePos = function(e) {
	var p;
	try {
		p = WSUtils.getMousePoint(e);
		return p; //{x: p.x, y: p.y};
	}
	catch(exception) {
		alert(exception);
		var oMousePos = this.stLastPointedInfo.oMousePos;
		var nMouseX = oMousePos.X;
		var nMouseY = oMousePos.Y;
		return {x: nMouseX,	y: nMouseY};
	}
}

WSEnrichmentEngine.prototype.getEditorViewport = function() {
	var oEditorFrame = this.getEditorFrame();
	return WSUtils.getWindowBounds(oEditorFrame);
}
WSEnrichmentEngine.prototype.calcSuggestionsPosition = function(e, nSelectedIndex, popupElement) {
	var finalPoint = WSUtils.zeroPoint();
	var mousePoint = WSUtils.getMousePoint(e);
	var elmBounds = WSUtils.getElementBounds(WSUtils.getEventElement(e),true);
	var editorBounds = WSUtils.getElementBounds(this.getEditorElement(),true);
	var editorViewport = this.getEditorViewport();

	//alert("mousePoint: x=" + mousePoint.x + ", y=" + mousePoint.y);
	//alert("editorBounds: x=" + editorBounds.x + ", y=" + editorBounds.y + ", h=" + editorBounds.h + ", w=" + editorBounds.w);
	//alert("elmBounds: x=" + elmBounds.x + ", y=" + elmBounds.y + ", h=" + elmBounds.h + ", w=" + elmBounds.w);
	//alert("editorViewport: x=" + editorViewport.x + ", y=" + editorViewport.y + ", h=" + editorViewport.h + ", w=" + editorViewport.w);

	elmBounds.x -= editorViewport.x;
	elmBounds.y -= editorViewport.y;
	//alert("elmBounds 2: x=" + elmBounds.x + ", y=" + elmBounds.y + ", h=" + elmBounds.h + ", w=" + elmBounds.w);
	//finalPoint.x = mousePoint.x + editorBounds.x;

	/*
	//IE consider the margin when calculating element bounds, others not	
	if (! whitesmokePlugin.isMSIE) {
		var margin = WSDomUtils.getElementMargin(this.getEditorDocument().body);	
		elmBounds.x += margin.left;	
		elmBounds.y += margin.top;
	}
	*/
	
	finalPoint.x = editorBounds.x + elmBounds.x;
	finalPoint.y = editorBounds.y + elmBounds.y + elmBounds.h + 3;
	
	//alert("editorViewport : x="+editorViewport.x+", y="+editorViewport.y+", h="+editorViewport.h+", w="+editorViewport.w);
	//alert("finalPoint: x="+finalPoint.x+", y="+finalPoint.y);
	//check if position is out of window bounds
	if (popupElement) {
		var windowBounds = WSUtils.getWindowBounds(window);
		var popupBounds  = WSUtils.getElementBounds(popupElement);
		//alert("popupBounds: x=" + popupBounds.x + ", y=" + popupBounds.y + ", h=" + popupBounds.h + ", w=" + popupBounds.w);
		//alert("windowBounds: x=" + windowBounds.x + ", y=" + windowBounds.y + ", h=" + windowBounds.h + ", w=" + windowBounds.w);
		
		//Check if the popup menu exceeded the buttom border
		if (finalPoint.y  > windowBounds.y + windowBounds.h - popupBounds.h) {
			finalPoint.y  = windowBounds.y + windowBounds.h - popupBounds.h;
			/*
			if (windowBounds.y < finalPoint.y - popupBounds.h - elmBounds.h) {
				finalPoint.y = editorBounds.y + elmBounds.y - popupBounds.h;
			}
			else {
				alert("not enough space to show flipped");
			}
			*/
		}
		//Check if the popup menu exceeded the right border
		if (finalPoint.x > windowBounds.x + windowBounds.w - popupBounds.w) {
			finalPoint.x = windowBounds.x + windowBounds.w - popupBounds.w;
			/*
			if (windowBounds.x < finalPoint.x - popupBounds.w) {
				finalPoint.x = windowBounds.x - popupBounds.w;
			}
			else {
				alert("not enough space to show flipped");
			}
			*/
		}
	}
	return finalPoint;
}
WSEnrichmentEngine.prototype.displaySuggestions = function(e, oDisplayElement, nSelectedIndex) {
	var targetPos = this.calcSuggestionsPosition(e, nSelectedIndex, oDisplayElement);
	//alert("displaySuggestions: nSelectedIndex=" + nSelectedIndex + ", targetPos.x="+targetPos.x+", targetPos.y="+targetPos.y);	
	oDisplayElement.style.left = (targetPos.x) + "px";
	oDisplayElement.style.top = (targetPos.y) + "px";
	WSUtils.showElement(oDisplayElement);
	return true;
}
WSEnrichmentEngine.prototype.displayMessage = function(oDisplayElement) {
	WSUtils.centerElement(oDisplayElement,window);
	WSUtils.showElement(oDisplayElement);
	return true;
}
WSEnrichmentEngine.prototype.showProgressDialog = function(bShow) {
	WSDebug.log((bShow) ? "Showing progressDialog": "Hiding progressDialog", "showProgressDialog");
	if (!this.config.enableProgressWindow) {
		return;
	}	
	var oElement;
	var showProgress = (bShow) ? 1 : -1;
	if (WSEnrichmentDefaultConfig.globalProgressWindow) {
		if (bShow) {
			WSGlobalProgressWindow.addOpenProgressWindows(this.editorId);
			if (WSGlobalProgressWindow.openProgessWindows.length < 2) {
				showProgress = 1;
				oElement = WSGlobalProgressWindow.globalProgessElement;
				if (!oElement) {
					oElement = this.getProgressElement();
				}
				WSGlobalProgressWindow.globalProgessElement = oElement;
			} else {
				showProgress = 0;
				return;
			}
		} else {
			WSGlobalProgressWindow.removeOpenProgressWindows(this.editorId);
			if (WSGlobalProgressWindow.openProgessWindows.length == 0) {
				oElement = WSGlobalProgressWindow.globalProgessElement;
				if (!oElement) {
					oElement = this.getProgressElement();
				}
				showProgress = -1;
				WSGlobalProgressWindow.globalProgessElement = null;
			} else {
				showProgress = 0;
				return;
			}
		}
	} else {
		oElement = this.getProgressElement();
	}
	WSDebug.log("Will show with element " + oElement.toString(), "showProgressDialog");
	if (oElement) {
		if (showProgress == 1) {
			WSDebug.log("Will open pregresssdialog..", "showProgressDialog");
			this.hideSuggestions();
			this.hideDictionary();
			var editorFrameRect = WSUtils.getElementBounds(this.getEditorElement());
			var dialogWidth = Math.max(196, editorFrameRect.w / 4);
			var dialogHeight = Math.max(58, editorFrameRect.h / 8);
			var t = (WSEnrichmentDefaultConfig.globalProgressWindow) ? new WSTemplate(WSEnrichmentDefaultConfig.progressWindow) : new WSTemplate(this.config.progressWindow);
			sContent = t.evaluate({
				w: dialogWidth,
				h: dialogHeight
			});
			WSUtils.showElement(oElement);
			oElement.innerHTML = sContent;
			var elBounds = WSUtils.getElementBounds(oElement);
			if (WSEnrichmentDefaultConfig.globalProgressWindow) {
				var wbounds = WSUtils.getWindowBounds(self);
				oElement.style.left = (wbounds.x + (wbounds.w - elBounds.w) / 2) + "px";
				oElement.style.top = (wbounds.y + (wbounds.h - elBounds.h) / 2) + "px";
			} else {
				oElement.style.left = (editorFrameRect.x + (editorFrameRect.w - elBounds.w) / 2) + "px";
				oElement.style.top = (editorFrameRect.y + (editorFrameRect.h - elBounds.h) / 2) + "px";
			}
		} else if (showProgress == -1) {
			oElement.innerHTML = "";
			WSUtils.hideElement(oElement);
		}
		return true;
	} else {
		return false;
	}
}
WSEnrichmentEngine.prototype.displayDictionary = function(oDisplayElement) {
	var el = this.getSuggestionsElement();
	if (! WSUtils.isVisible(el)) {
		return;
	}	
	var b = WSUtils.getElementBounds(el);
	oDisplayElement.style.left = b.x + b.w + "px";
	oDisplayElement.style.top = b.y + "px";
	oDisplayElement.className = "wsDictionaryOn";
	oDisplayElement.style.display = "inline";
	oDisplayElement.style.visibility = "visible";
	return true;
}
WSEnrichmentEngine.prototype.setSuggestionsEventHandler = function(oDisplayElement, sName, oFunction) {
	if (!this.closures) this.closures = [];
	if (!this.closures["onMouseOverSuggestion"]) {
		this.closures["onMouseOverSuggestion"] = this.eventClosure(this.onMouseOverSuggestion);
	}
	if (!this.closures["onMouseOutSuggestion"]) {
		this.closures["onMouseOutSuggestion"] = this.eventClosure(this.onMouseOutSuggestion);
	}
	var oElements = oDisplayElement.getElementsByTagName("td");
	for (var i = 0; i < oElements.length; ++i) {
		if (oElements[i].getAttribute('name') == sName) {
			WSDomUtils.addEvent(oElements[i], "mousedown", oFunction);
			WSDomUtils.addEvent(oElements[i], "mouseover", this.closures["onMouseOverSuggestion"]);
			WSDomUtils.addEvent(oElements[i], "mouseout", this.closures["onMouseOutSuggestion"]);
		}
	}
}
WSEnrichmentEngine.prototype.onMouseOverSuggestion = function(e) {
	WSSharedEnrichmentEngine.startDictionaryTimer(e, this);
	var oElement = WSUtils.getEventElement(e);
	if (oElement) {
		oElement.className = this.config.suggestionSettings.mouseOver.className;
	}
}
WSEnrichmentEngine.prototype.onMouseOutSuggestion = function(e) {
	WSSharedEnrichmentEngine.stopDictionaryTimer(this);
	var oElement = WSUtils.getEventElement(e);
	if (oElement) {
		oElement.className = this.config.suggestionSettings.single.className;
	}
}
WSEnrichmentEngine.prototype.getEnrichmentLangType = function(nLangType) {
	var nEnrichLangType = 0;
	if (nLangType == this.config.stLangType.noun) {
		nEnrichLangType = this.config.stLangType.adjective;
	} else if (nLangType == this.config.stLangType.verb) {
		nEnrichLangType = this.config.stLangType.adverb;
	}
	return nEnrichLangType;
}
WSEnrichmentEngine.prototype.getSuggestionsHTML = function(vSuggestions, enrichSettings, leftBorderHTML, rightBorderHTML) {
	var sContent = "";
	var size = vSuggestions.length;
	var template = new WSTemplate(this.config.suggestionHTML);
	for (var i = 0; i < size && i < this.config.nMaxSuggestions; ++i) {
	
		var sClassName = this.config.suggestionSettings.single.className;
		var sName = enrichSettings.name;
		if (vSuggestions[i].substr(0,7) == "remark:" ||
			vSuggestions[i].indexOf("(FORGOT A WORD?)") != -1)
		{
			sClassName = this.config.suggestionSettings.disabled.className;
			sName += "Disabled";
		}	

		sContent += template.evaluate({
			id: enrichSettings.name + i.toString(),
			name: sName,
			suggestion: vSuggestions[i],
			sColor: enrichSettings.color,
			className: sClassName,
			leftBorder: leftBorderHTML,
			rightBorder: rightBorderHTML
		});
	}
	return sContent;
}
WSEnrichmentEngine.prototype.getExtendedSuggestionsHTML = function(vSuggestions, spacesNum, enrichSettings, leftBorderHTML, rightBorderHTML) {
	var sContent = "";
	var size = vSuggestions.length;
	var template = new WSTemplate(this.config.suggestionHTML);
	for (var i = 0; i < size && i < this.config.nMaxSuggestions; ++i) {

		var sClassName = this.config.suggestionSettings.single.className;
		var sName = enrichSettings.name;
		
		if (vSuggestions[i].substr(0,7) == "remark:" ||
			vSuggestions[i].indexOf("(FORGOT A WORD?)") != -1)
		{
			sClassName = this.config.suggestionSettings.disabled.className;
			sName += "Disabled";
		}	
	
		sContent += template.evaluate({
			id: enrichSettings.name + i.toString(),
			name: sName,
			suggestion: vSuggestions[i],
			sColor: enrichSettings.color,
			className: sClassName,
			leftBorder: leftBorderHTML,
			rightBorder: rightBorderHTML
		});
	}
	for (i = 0; i < spacesNum && i+size < this.config.nMaxSuggestions; ++i) {
		sContent += template.evaluate({
			id: "0",
			name: "0",
			suggestion: "",
			sColor: enrichSettings.color,
			className: this.config.suggestionSettings.single.className,
			leftBorder: leftBorderHTML,
			rightBorder: rightBorderHTML
		});
	}
	return sContent;
}
WSEnrichmentEngine.prototype.showFeedbackMessage = function(html) {
	//this.getEditorInstance().tryDisableDesignMode();
	var self = this;
	var oFeedback = this.getFeedbackElement();
	if (! oFeedback) {
		return false;
	}
	oFeedback.innerHTML = html;
	//alert("oFeedback.innerHTML\n" + oFeedback.innerHTML);
	var nodes = oFeedback.getElementsByTagName("div");
	
	for (var i=0; i<nodes.length; ++i) {
		//if (nodes[i].id == "bgBottomImg") {
		if (nodes[i].id == "ServerFeedbackOKButton") {
		/*
			var new_div = document.createElement('div');
			new_div.innerHTML = "<BR><HR>";
			new_div = nodes[i].appendChild(new_div);
		*/			
			var new_btn = document.createElement('button');
			new_btn.id = 'feedback_ok_button';
			new_btn.name = 'feedback_ok_button';
			new_btn.innerHTML = "OK";
			WSDomUtils.addEvent(new_btn,'click',
						function() {
							WSUtils.hideElement(oFeedback);
							oFeedback.innerHTML = "";
							//WSDomUtils.removeEvent(new_btn,'click',arguments.callee);
							//WSDomUtils.removeEvent(window,'resize', dispMessageFunc);
			});
			//new_div.appendChild(new_btn);
			nodes[i].appendChild(new_btn);
			//alert("nodes["+i+"] =\n" + nodes[i].innerHTML);
			break;
		}
	}
	//WSDomUtils.addEvent(window,'resize',dispMessageFunc);
	this.displayMessage(oFeedback);
	return true;
}
WSEnrichmentEngine.prototype.showSuggestions = function(e, vSuggestions, enrichSettings) {
	var oDisplayElement = this.getSuggestionsElement();
	if (!oDisplayElement) {
		return false;
	}
	var sContent = '';
	if (vSuggestions.length > 0) {
		var tLeft = new WSTemplate(this.config.leftBorderHTML);
		var leftBorder = tLeft.evaluate({
			sColor: enrichSettings.color
		});
		var tRight = new WSTemplate(this.config.rightBorderHTML);
		var rightBorder = tRight.evaluate({
			sColor: enrichSettings.color
		});
		var t = new WSTemplate(this.config.suggestionBox);
		sContent = t.evaluate({
			sColor: enrichSettings.color,
			sHeader: enrichSettings.title,
			suggestion: this.getSuggestionsHTML(vSuggestions, enrichSettings, leftBorder, rightBorder)
		});
	}
	oDisplayElement.innerHTML = sContent;
	this.setSuggestionsEventHandler(oDisplayElement, enrichSettings.name, this.eventClosure(this.onReplaceWord));
	this.displaySuggestions(e, oDisplayElement, this.nSelectedIndex);
	return true;
}
WSEnrichmentEngine.prototype.show2Suggestions = function(e, vESuggestions, vTSuggestions) {
	if (vESuggestions.length == 0 && vTSuggestions.length == 0) {
		return false;
	}
	var oDisplayElement = this.getSuggestionsElement();
	if (!oDisplayElement) {
		return false;
	}
	var enrichmentSuggestion = '';
	var leftovers = Math.min(vTSuggestions.length, this.config.nMaxSuggestions) - Math.min(vESuggestions.length, this.config.nMaxSuggestions);
	if (vESuggestions.length > 0) {
		var tLeft = new WSTemplate(this.config.leftBorderHTML);
		var leftBorder = tLeft.evaluate({
			sColor: "blue"
		});
		var tRight = new WSTemplate(this.config.rightBorderHTML);
		var rightBorder = tRight.evaluate({
			sColor: "lightBlue"
		});
		var t = new WSTemplate(this.config.enrichmentSuggestionHTML);
		//var t = new WSTemplate(this.config.extendedSuggestionHTML);
		enrichmentSuggestion = t.evaluate({
			sColor:  this.config.enrichSettings.enrichment.color,
			sHeader: this.config.enrichSettings.enrichment.title,
			suggestion: this.getExtendedSuggestionsHTML(vESuggestions, leftovers, this.config.enrichSettings.enrichment, leftBorder, rightBorder)
		});
	}
	var thesaurusSuggestion = '';
	leftovers *= -1;
	//leftovers = (-1) * leftovers;
	if (vTSuggestions.length > 0) {
		var tRight = new WSTemplate(this.config.rightBorderHTML);
		var rightBorder = tRight.evaluate({
			sColor: this.config.enrichSettings.thesaurus.color //"blue"
		});
		//var t2 = new WSTemplate(this.config.thesaurusSuggestionHTML);
		var t2 = new WSTemplate(this.config.extendedSuggestionHTML);
		thesaurusSuggestion = t2.evaluate({
			sColor:  this.config.enrichSettings.thesaurus.color,
			sHeader: this.config.enrichSettings.thesaurus.title,
			suggestion: this.getExtendedSuggestionsHTML(vTSuggestions, leftovers, this.config.enrichSettings.thesaurus, "", rightBorder)
		});
	}
	var sContent = '';
	var t3 = new WSTemplate(this.config.extendedSuggestionBox);
	sContent = t3.evaluate({
		enrichmentSuggestion: enrichmentSuggestion,
		thesaurusSuggestion: thesaurusSuggestion
	});
	oDisplayElement.innerHTML = sContent;
	var sEnrichmentName = this.config.enrichSettings.enrichment.name;
	var sThesaurusName  = this.config.enrichSettings.thesaurus.name;
	this.setSuggestionsEventHandler(oDisplayElement, sEnrichmentName, this.eventClosure(this.onAddWord));
	this.setSuggestionsEventHandler(oDisplayElement, sThesaurusName, this.eventClosure(this.onReplaceWord));
	this.displaySuggestions(e, oDisplayElement, this.nSelectedIndex);
	return true;
}
WSEnrichmentEngine.prototype.onAddWord = function(e) {
	var oElement = WSUtils.getEventElement(e);
	if (oElement) {
		var sNewWord = oElement.innerHTML;
		var nLangType = this.vEnrichment[this.nSelectedIndex].nLangType;
		var nEnrichmentLangType = this.getEnrichmentLangType(nLangType);
		this.addWord(sNewWord, nEnrichmentLangType);
	}
}
WSEnrichmentEngine.prototype.showTrialMessage = function(e) {
	alert("This version available to the full version only.\nTo register go to -> https://buy.whitesmoke.com/buynew/");
	return true;
}
WSEnrichmentEngine.prototype.showDictionaryResult = function() {
	var bSuggestionsPresent = true;
	var oDisplayElement = this.getDictionaryElement();
	if (!oDisplayElement) {
		bSuggestionsPresent = false;
		oDisplayElement = this.getSuggestionsElement();
		if (!oDisplayElement) {
			return false;
		}
	}
	var vSuggestions = this.stLastPointedInfo.vDictionary;
	if (vSuggestions.length <= 0) {}
	var sWord = this.stLastPointedInfo.sWord;
	var sContent = "";
	sContent += '<table class="wsDictionary">';
	sContent += '<tr>';
	sContent += '<td valign="top">';
	sContent += '<table>';
	sContent += '<th align="left" class="wsDictionaryHeader">Dictionary</th>';
	sContent += '<tr><td align="left" class="wsDictionaryValue">' + sWord + ':</td></tr>';
	if (vSuggestions.length <= 0) {
		sContent += '<tr><td style="font-style:italic;" class="wsDictionaryValue">no suggestions</td></tr>';
	}
	for (var i = 0; i < vSuggestions.length; ++i) {
		sContent += '<tr>';
		sContent += '<td class="wsDictionarySuggestion">';
		sContent += vSuggestions[i];
		sContent += '</td>';
		sContent += '</tr>';
	}
	sContent += '</table></td></tr></table>';
	oDisplayElement.innerHTML = sContent;
	var e = this.stLastPointedInfo.e;
	if (bSuggestionsPresent == false) {
		this.displaySuggestions(e, oDisplayElement, this.stLastPointedInfo.nIndex);
	} else {
		this.displayDictionary(oDisplayElement);
	}
	return true;
}
WSEnrichmentEngine.prototype.markWord = function(nNewIndex) {
	var nPrevIndex = this.nSelectedIndex;
	if (nPrevIndex >= 0) {
		var oPrevElement = this.getElementByIndex(nPrevIndex);
		if (oPrevElement && oPrevElement.style) {
			if (this.stPrevElementStyle != null) {
				oPrevElement.style.textDecoration = this.stPrevElementStyle.textDecoration;
				oPrevElement.style.borderBottomStyle = this.stPrevElementStyle.borderBottomStyle;
			}
		}
	}
	if (nNewIndex >= 0) {
		var oElement = this.getElementByIndex(nNewIndex);
		if (oElement && oElement.style) {
			this.stPrevElementStyle.textDecoration = oElement.style.textDecoration;
			this.stPrevElementStyle.borderBottomStyle = oElement.style.borderBottomStyle;
			//oElement.style.borderBottomStyle = "solid";
		} 
	}
}
WSEnrichmentEngine.prototype.showSpellingSuggestions = function(e, nIndex) {
	this.markWord(nIndex);
	this.state = "s";
	this.nSelectedIndex = nIndex;
	var enrichment = this.vEnrichment[this.nSelectedIndex]; {
		return this.showSuggestions(e, enrichment.s, this.config.enrichSettings.spelling);
	}
}
WSEnrichmentEngine.prototype.showGrammarSuggestions = function(e, nIndex) {
	if (false) {
		this.showTrialMessage();
	} else {
		this.markWord(nIndex);
		this.state = "g";
		this.nSelectedIndex = nIndex;
		var enrichment = this.vEnrichment[this.nSelectedIndex];
		return this.showSuggestions(e, enrichment.g, this.config.enrichSettings.grammar);
	}
}
WSEnrichmentEngine.prototype.showEnrichmentSuggestions = function(e, nIndex) {
	this.markWord(nIndex);
	this.state = "e";
	this.nSelectedIndex = nIndex;
	var enrichment = this.vEnrichment[this.nSelectedIndex];
	return this.show2Suggestions(e, enrichment.e, enrichment.t);//,this.config.enrichSettings.enrichment,this.config.enrichSettings.thesaurus);
}
WSEnrichmentEngine.prototype.addUndoInfo = function(sFuncName, vArgs) {
	this.undoInfo[this.undoInfo.length] = {
		nIndex: this.nSelectedIndex,
		sFuncName: sFuncName,
		vArgs: vArgs
	};
}
WSEnrichmentEngine.prototype.getFilteredEditorContent = function() {
	var sContent = this.getEditorContent();
	WSDebug.notice("There is a htmlMode and nonHTML mode. Why?", "getFilteredEditorContent");
	var sNewContent = this.cleanWSContent(sContent);
	WSDebug.notice("Removing trailing nbsp.. Why?", "getFilteredEditorContent");
	if (sNewContent.substr(sNewContent.length - 6, 6) == "&nbsp;") {
		sNewContent = sNewContent.substr(0, sNewContent.length - 6);
	}
	return sNewContent;
}
WSEnrichmentEngine.prototype.getFilteredTemplateContent = function() {
	var sContent = "";
	var oEditorFrame = this.getEditorFrame();
	if (oEditorFrame) {
		var oContentFrame = oEditorFrame.frames['content'];
		sContent = oContentFrame.document.body.innerHTML;
	}
	return sContent;
}
WSEnrichmentEngine.prototype.updateContent = function() {
	var sNewContent = this.getFilteredEditorContent();
	WSDebug.log("collected content: " + sNewContent, "updateContent");
	this.querySentence(sNewContent);
	return sNewContent;
}
WSEnrichmentEngine.prototype.onReplaceWord = function(e) {
	var oElement = WSUtils.getEventElement(e);
	if (oElement) {
		var sNewWord = oElement.innerHTML;
		var nLangType = this.vEnrichment[this.nSelectedIndex].nLangType;
		this.replaceWord(sNewWord, nLangType);
	}
	return true;
}
WSEnrichmentEngine.prototype.replaceWord = function(newWord, nLangType) {
	this.hideSuggestions();
	var enrichment = this.vEnrichment[this.nSelectedIndex];
	var oldWord = enrichment.sWord;
	enrichment.sWord = newWord;
	var oElement = this.getElementByIndex(this.nSelectedIndex);
	oElement.className = this.config.enrichSettings.general.className;
	this.replaceElementText(oElement, oldWord, newWord);
	var bIsThesaurus = false;
	for (var i = 0; i < enrichment.t.length; ++i) {
		if (enrichment.t[i] == newWord) {
			//enrichment.t[i] = oldWord;
			bIsThesaurus = true;
		}
	}
	this.addUndoInfo("replaceWord", ["\"" + oldWord + "\"", nLangType]);
	if (!bIsThesaurus && this.config.autoCheck.onChange) {
		this.queryWord(newWord, nLangType);
	}
}
WSEnrichmentEngine.prototype.replaceElementText = function(oElement, oldText, newText) {
	var sContent = oElement.innerHTML;
	var sNewContent = sContent.replace(oldText, newText);
	oElement.innerHTML = sNewContent;
}
WSEnrichmentEngine.prototype.addWord = function(newWord, nLangType) {
	this.hideSuggestions();
	var sId = "wsWordAdd" + this.nSelectedIndex;
	var oElement = this.getElementById(sId);
	oElement.innerHTML += newWord + " ";
	this.addUndoInfo("deleteWord", ["\"" + sId + "\"", "\"" + newWord + "\"", nLangType]);
	return true;
}
WSEnrichmentEngine.prototype.deleteWord = function(sElementId, sWord, nLangType) {
	this.hideSuggestions();
	var oElement = this.getElementById(sElementId);
	this.replaceElementText(oElement, sWord, "");
	this.addUndoInfo("addWord", ["\"" + sWord + "\"", nLangType]);
}
WSEnrichmentEngine.prototype.undo = function() {
	if (this.undoInfo.length > 0) {
		var stUndo = this.undoInfo.pop();
		if (stUndo) {
			var sUndoCode = "this." + stUndo.sFuncName + "(";
			for (var i = 0; i < stUndo.vArgs.length; ++i) {
				if (i >= 1) {
					sUndoCode += ",";
				}
				sUndoCode += stUndo.vArgs[i];
			}
			sUndoCode += ");";
			this.nSelectedIndex = stUndo.nIndex;
			eval(sUndoCode);
			this.redoInfo[this.redoInfo.length] = this.undoInfo.pop();
		}
	}
}
WSEnrichmentEngine.prototype.redo = function() {
	if (this.redoInfo.length > 0) {
		var stRedo = this.redoInfo.pop();
		if (stRedo) {
			var sRedoCode = "this." + stRedo.sFuncName + "(";
			for (var i = 0; i < stRedo.vArgs.length; ++i) {
				if (i >= 1) {
					sRedoCode += ",";
				}
				sRedoCode += stRedo.vArgs[i];
			}
			sRedoCode += ");";
			this.nSelectedIndex = stRedo.nIndex;
			eval(sRedoCode);
		}
	}
}
WSEnrichmentEngine.prototype.testQuerySentence = function() {
	var sSentence = "He is patatoes.";
	WSDebug.log("on querySentence, sentence = [\n" + sSentence + "\n]", "querySentence");
	this.sContent = sSentence;
	var vTokens = this.tokenize(sSentence);
	var sMessage = "";
	for (var i = 0; i < vTokens.length; ++i) {
		sMessage += vTokens[i].sWord;
		sMessage += " ";
	}
	this.showProgressDialog(true);
	sMessage += this.queryLoginInfo();
	sMessage += "v\n";
	sMessage += "998\n";
	sMessage = this.queryLoginInfo();
	sMessage += "l\n";
	return this.sendToServer("handleTestQuerySentenceResponse", "", 0, sMessage);
}
WSEnrichmentEngine.prototype.queryLoginInfo = function() {
	var mess = "3\n";
	mess += "0\n";
	mess += this.config.userId + "\n";
	return mess;
}
WSEnrichmentEngine.prototype.handleTestQuerySentenceResponse = function(sResponseData) {
	WSDebug.log("Receiving response from server..", "handleQuerySentenceResponse");
	this.showProgressDialog(false);
	if (sResponseData) {
		var oEnrichmentInfo = wsGetEnrichmentInfo(sResponseData, this.vTokens, this);
		this.setEnrichmentInfo(oEnrichmentInfo);
	}
	this.displayContent();
}
WSEnrichmentEngine.prototype.onHandleError = function(sResponseData) {
	WSDebug.warning("Not a valid member (" + sResponseData + ")");
	var template = new WSTemplate(this.config.feedbackTemplate);
	var msg  = template.evaluate({
			sMessage: sResponseData,
			okButton: '<HR><div id="ServerFeedbackOKButton"></div>'
			//okButton: '<div id="ServerFeedbackOKButton"></div>',
			//downloadButton: '<div id="ServerFeedbackDownloadButton"></div>'
		});
	this.ShowServerFeedback(msg);
}
/*
var onHandleError = function(sResponseData) {
	WSDebug.warning("Not a valid member (" + sResponseData + ")");
	alert(sResponseData);
}
*/
WSEnrichmentEngine.prototype.sendToServer = function(sCallBack, sQueryType, nLangType, sMessage) {
	WSDebug.log("{callback: " + sCallBack + ",querytype: " + sQueryType + ", langtype: " + nLangType + "message: " + sMessage + "}", "sendToServer");
	return this.sendToServerAJAXExtended(sCallBack, sQueryType, nLangType, sMessage);
}

function IFrameRequest() 
{
  var reqCount = 0;
  this.readyState = 0;
  this.status = 0;
  this.responseText = "";    
  reqCount++;
  this.req_id = reqCount;
}

IFrameRequest.prototype = {
	open: function(protocol, url, async) {
		this.protocol = protocol;
		this.url = url;
	},

	onreadystatechange: function() { },

	send: function(postBody) {
		var self = this;
		if (this.protocol.toUpperCase()=='POST') {
			this.url = this.url + "&" + postBody;
		}
		var IFrameDoc = document.createElement('iframe');
		var frame_id = 'req'+this.req_id;
		IFrameDoc.id  = frame_id;
		IFrameDoc.name = frame_id;
		IFrameDoc.style.width = "0";
		IFrameDoc.style.height = "0";
		IFrameDoc.style.border = "0";
		IFrameDoc = document.body.appendChild(IFrameDoc);

		try {
			IFrameDoc.src = this.url;
		}
		catch(e) {
			return false;
		}
		this.readyState = 1;
		this.onreadystatechange();
		//WSDomUtils.addEvent(IFrameDoc,'load', function() {self.IFht(4,frame_id);});
		setTimeout(function(){self.IFht(4,frame_id);}, 4);
	},

	overrideMimeType: function() { },
	getResponseHeader: function (name) { return ''; },
	setRequestHeader: function (name, data) { },

	IFht: function (d, frame_id) {
		var self = this;
		var iframe = document.getElementById(frame_id);
		var finished_load = false;
		
		if (d<15 && (typeof iframe.readyState)!="undefined") { //For IE
			finished_load = (iframe.readyState=='complete');
		}
		else {
			finished_load =
				iframe && iframe.contentWindow &&
				iframe.contentWindow.document &&
				iframe.contentWindow.document.body &&
				iframe.contentWindow.document.body.innerHTML;
		}

		if (finished_load) {
			//alert(iframe.contentWindow.document.getElementsByTagName('head')[0].innerHTML);
			//alert(iframe.contentWindow.document.body.innerHTML);
			//self.responseText = iframe.contentWindow.document.body.innerHTML;
			self.responseText = iframe.contentWindow.document.getElementsByTagName('head')[0].innerHTML;
			//self.responseText = self.responseText.replace(/[\n\r]+/ig, "");
			iframe.parentNode.removeChild(iframe);
			self.status = 200;
			self.readyState = 4;
			self.onreadystatechange();
		}
		else {
			d*=1.5;
			setTimeout(function() {self.IFht(d,frame_id);},d);
		}
	}
}

WSEnrichmentEngine.prototype.sendToServerAJAXExtended = function(sCallBack, sQueryType, nLangType, sMessage) {
	if (sMessage.length <= this.config.nMaxContentLength) {
		var xmlhttp = null;
		var self = this;
		//var xmlhttp = new XMLHttpRequest();
		//var xmlhttp = new XMLHTTP(this.config.sAJAXExURL);
		//var htmlhttp = new HTMLHttpRequest("","");
		//var xmlhttp = htmlhttp.xmlhttp;
		//alert("htmlhttp = " + htmlhttp + ", xmlhttp =  " + xmlhttp);
		//var xmlhttp = new getHTTPObjectUser();
		//var sendMethod = this.config.sendMethod;

		var call_prefix = "";
		if (self.config.sendMethod.iframe == 1) {
			xmlhttp = new IFrameRequest();
			call_prefix = "parent.g_oWSEnrichmentsArray[" + this.wsEditorNumber + "].";
		}
		else if (self.config.sendMethod.ajax_ex == 1)	{
			var call_prefix = "self.";
			xmlhttp = new XMLHTTP(self.config.sAJAXExURL);
		}
		else { //if (self.config.sendMethod.ajax == 1)
			var call_prefix = "self.";
			xmlhttp = new XMLHttpRequest();
		}
		sCallBack = call_prefix + sCallBack;
		var sErrorCallBack = call_prefix + "onHandleError";
		
		
		xmlhttp.onerror = function(description) {
			alert("description\n"+description);
			this.onHandleError(description);
		}
		xmlhttp.onreadystatechange = function() {			
			if (xmlhttp.readyState == 4) {
				//alert("xmlhttp.responseText:\n" + xmlhttp.responseText);
				//var sCode = xmlhttp.responseText;
				//alert(sCode);
				//sCode = sCode.replace(/\\\\/g,"\\");
				//var eval_str = sCallBack + "(" + sCode + ");";
				//var eval_str = sCode;
				//alert(xmlhttp.responseText);
				//eval(xmlhttp.responseText); //self.eval(sCode);
				//if (self.config.sendMethod.iframe != 1)
				{
					var upperResponseText = xmlhttp.responseText.toUpperCase();
					var nStart = upperResponseText.indexOf("<S" + "CRIPT");
					if (nStart >= 0) {
						nStart = upperResponseText.indexOf(">", nStart) + 1;
						var nFinish = upperResponseText.indexOf("</SC" + "RIPT>", nStart);
						if (nFinish >= 0) {
							var sCode = xmlhttp.responseText.substr(nStart, nFinish - nStart);
							//alert("sCode = " + sCode);
							//sCode = sCode.replace(/\\\\/g,"\\");
							//alert(sCallBack + "." + sCode);
							//eval(sCallBack + "." + sCode); //self.eval(sCode);
							//alert("sCode = " + sCode);
							//eval(sCode); //self.eval(sCode);
						}
					}
				}
				self.displaySummaryNotice();
			}
		}
		var sProfile = this.config.profile;
		var sURL = "";
		if (sQueryType) {
			sURL = this.config.sServerURL + "?user_id=" + this.config.userId + "&callback=" + sCallBack + "&error_callback="+sErrorCallBack+"&profile=" + sProfile + "&lang_type=" + nLangType + "&query_type=" + sQueryType + "&message=" + encodeURI(sMessage);
		} else {
			sURL = this.config.sServerURL + "?user_id=" + this.config.userId + "&callback=" + sCallBack + "&error_callback="+sErrorCallBack+"&profile=" + sProfile + "&lang_type=" + nLangType + "&message=" + encodeURI(sMessage);
		}
		//alert(sURL);
		xmlhttp.open('POST', sURL);
		xmlhttp.send(null);
	} else {
		var template = new WSTemplate(this.config.feedbackTemplate);
		var stripedMessage  = template.evaluate({
				sMessage: this.config.getLengthExceededMsg(),
				okButton: '<HR><div id="ServerFeedbackOKButton"></div>'
				//okButton: '<div id="ServerFeedbackOKButton"></div>',
				//downloadButton: '<div id="ServerFeedbackDownloadButton"></div>'
			});
	
		this.ShowServerFeedback(stripedMessage);
		return false;
	}
	return true;
}
WSEnrichmentEngine.prototype.querySentence = function(sSentence) {
	WSDebug.log("on querySentence, sentence = [\n" + sSentence + "\n]", "querySentence");
	this.sContent = sSentence;
	var vTokens = this.tokenize(sSentence);
	var sMessage = "";
	for (var i = 0; i < vTokens.length; ++i) {
		sMessage += vTokens[i].sWord;
		sMessage += " ";
	}
	this.showProgressDialog(true);
	return this.sendToServer("handleQuerySentenceResponse", "sentence", 0, sMessage);
}
WSEnrichmentEngine.prototype.handleQuerySentenceResponse = function(sResponseData) {
	WSDebug.log("Receiving response from server: " + sResponseData, "handleQuerySentenceResponse");
	this.showProgressDialog(false);
	if (sResponseData) {
		var oEnrichmentInfo = wsGetEnrichmentInfo(sResponseData, this.vTokens, this);
		this.setEnrichmentInfo(oEnrichmentInfo);
	}
	this.displayContent();
}
WSEnrichmentEngine.prototype.queryWord = function(sWord, nLangType) {
	this.showProgressDialog(true);
	return this.sendToServer("handleQueryWordResponse", "word", nLangType, sWord);
}
WSEnrichmentEngine.prototype.handleQueryWordResponse = function(sResponseData) {
	this.showProgressDialog(false);
	if (sResponseData) {
		var oEnrichmentInfo = wsGetEnrichmentInfo(sResponseData, this.vTokens);
		this.mergeEnrichmentInfo(oEnrichmentInfo);
	}
}
WSEnrichmentEngine.prototype.queryDictionary = function(sWord, nLangType) {
	if (this.getDictionaryInfo(sWord, nLangType) != null) {
		this.showDictionaryResult();
		return true;
	} else {
		return this.sendToServer("handleQueryDictionaryResponse", "dictionary", nLangType, sWord);
	}
}
WSEnrichmentEngine.prototype.handleQueryDictionaryResponse = function(sResponseData) {
	if (sResponseData) {
		var oEnrichmentInfo = wsGetEnrichmentInfo(sResponseData, this.vTokens);
		this.setDictionaryInfo(oEnrichmentInfo);
	}
	this.showDictionaryResult();
}
WSEnrichmentEngine.prototype.queryTemplateList = function() {
	return this.sendToServer("handleQueryTemplateListResponse", "templateList", 0, "");
}
WSEnrichmentEngine.prototype.displayTemplateContent = function(sResponseData) {
	var oEditorFrame = this.getEditorFrame();
	if (oEditorFrame) {
		var oContentFrame = oEditorFrame.frames['content'];
		oContentFrame.document.open();
		oContentFrame.document.write(sResponseData);
		oContentFrame.document.close();
		return true;
	}
	return false;
}
WSEnrichmentEngine.prototype.queryTemplate = function(sName) {
	return this.sendToServer("handleQueryTemplateResponse", "template", 0, sName);
}
WSEnrichmentEngine.prototype.tokenize = function(sContent) {
	var htmlParser = new WSHTMLParser();
	this.vTokens = htmlParser.parseHTML(sContent);
	return this.vTokens;
}
WSEnrichmentEngine.prototype.reTokenize = function(sContent) {
	var htmlParser = new WSHTMLParser();
	var vTokens = new Array();
	if (this.isHTMLMode()) {
		vTokens = htmlParser.parseHTML(sContent);
	} else {
		vTokens = htmlParser.parseText(sContent);
	}
	return this.vTokens;
}
WSEnrichmentEngine.prototype.getEditorContent = function() {
	WSDebug.needOverrideWarning("getEditorContent", "WSEnrichmentEngine");
}
WSEnrichmentEngine.prototype.setEditorContent = function(sContent) {
	WSDebug.needOverrideWarning("setEditorContent", "WSEnrichmentEngine");
}
WSEnrichmentEngine.prototype.getEditorFrame = function() {
	WSDebug.needOverrideWarning("getEditorFrame", "WSEnrichmentEngine");
}
WSEnrichmentEngine.prototype.getEditorDocument = function() {
	WSDebug.needOverrideWarning("getEditorDocument", "WSEnrichmentEngine");
}
var WSDebug = {
	messages: [],
	windowname: "WhiteSmokeDebugWindow",
	logwindow: null,
	logwindowReady: false,
	log: function(mess, sender, level) {
		if (!level) level = 1;
		if (!sender) sender = "unknown";
		this.appendToLog(this.createMessage(sender, mess, level));
	},
	notice: function(mess, sender, level) {
		if (!level) level = 0;
		if (!sender) sender = "unknown";
		this.appendToLog(this.createMessage(sender, mess, level));
	},
	warning: function(mess, sender, level) {
		if (!level) level = 0;
		if (!sender) sender = "unknown";
		this.appendToLog(this.createMessage(sender, '<span class="warning">' + mess + '</span>', level));
	},
	createMessage: function(sender, message, level) {
		var d = new Date();
		var t = this.lpad(d.getHours(), "0", 2) + ":" + this.lpad(d.getMinutes(), "0", 2) + ":" + this.lpad(d.getSeconds(), "0", 2) + "." + this.lpad(d.getMilliseconds(), "0", 2);
		return {
			id: this.messages.length,
			sender: sender,
			message: message,
			level: level,
			timestamp: t
		};
	},
	lpad: function(str, chr, length) {
		str = str.toString();
		while (str.length < length) str = chr + str;
		return str;
	},
	openDebugWindow: function() {
		if (!this.logwindow) {
			var width = 500;
			var height = 400;
			var options = "";
			var url = whitesmokePlugin.baseURL + "/debug.htm";
			//var url = "";
			var win = this.windowname;
			var leftPos = (screen.availWidth - width) / 2;
			var topPos = (screen.availHeight - height) / 2;
			options += 'width=' + width + ',height=' + height + ',top=' + topPos + ',left=' + leftPos;
			this.logwindow = window.open(url, win, options);

			this.logwindow.document.body.innerHTML = "";
			
			var oTable = this.logwindow.document.createElement('table');
			oTable.id = 'debug_table';
			oTable.name  = 'debug_table';
			oTable.border = 1;
			/*
			oTable.setAttribute('id', "debug_table");
			oTable.setAttribute('name', "debug_table");
			oTable.setAttribute('border', "1");
			*/
			oTable.innerHTML = "<tr><th>id</th><th>sender</th><th>message</th><th>level</th><th>timestamp</th></tr>";
			this.logwindow.document.body.appendChild(oTable);
			
		} else {
			this.logwindow.focus();
		}
	},
	needOverrideWarning: function(m, s) {
		this.appendToLog(this.createMessage(s, "Method '" + m + "' of '" + s + "' needs to be overridden!!", 5));
		this.openDebugWindow();
	},
	appendToLog: function(m) {
		this.messages.push(m);
		this.showMessage(m);
		//if (this.logwindow && m.level > 0) this.logwindow.debug.showMessage(m);
	},
	showMessage: function(m) {
		//alert("this.messages.length " + this.messages.length + ", m.level = " + m.level + ", m.id = " + m.id);
		if (this.logwindow && m.level >= 0) { 
			var oElement = this.logwindow.document.createElement('tr');
			oElement.innerHTML = "<td>" + m.id + "</td><td>" + m.sender + "</td><td>" + m.message + "</td><td>" + m.level + "</td><td>" + m.timestamp + "</td>";
			var oTable = this.logwindow.document.getElementById('debug_table');
			var oTableBody = oTable.getElementsByTagName("tbody");
			if (oTableBody) {
				oTable = oTableBody[0];
			}
			//oTable.appendChild(oElement);
			oTable.innerHTML += "<td>" + m.id + "</td><td>" + m.sender + "</td><td>" + m.message + "</td><td>" + m.level + "</td><td>" + m.timestamp + "</td>";
			/*
			var oElement = this.logwindow.document.createElement('div');
			oElement.innerHTML = "id: " + m.id + ", sender: " + m.sender + ", message: " + m.message + ", level: " + m.level + ", timestamp: " + m.timestamp;
			oElement.innerHTML += "<HR>";
			this.logwindow.document.body.appendChild(oElement);
			//this.logwindow.document.getElementsByTagName('body')[0].appendChild(oElement);
			//alert(this.logwindow.document.getElementsByTagName('body')[0].innerHTML);
			*/
		}
	}
}
XMLHTTP = function(sAPIURL) {
	var _maximumRequestLength = 1500;
	var _apiURL = sAPIURL;
	this.status = null;
	this.statusText = null;
	this.responseText = null;
	this.responseXML = null;
	this.synchronous = false;
	this.readyState = 0;
	this.onreadystatechange = function() {}
	this.onerror = function() {}
	this.onload = function() {}
	this.abort = function() {
		_stop = true
		_destroyScripts();
	}
	this.getAllResponseHeaders = function() {
		var result = '';
		for (property in _responseHeaders)
			result += property + ': ' + _responseHeaders[property] + '\r\n';
		return result
	}
	this.getResponseHeader = function(name) {
		for (property in _responseHeaders) {
			if (property.toLowerCase() == name.toLowerCase())
				return _responseHeaders[property];
		}
		return null;
	}
	this.overrideMimeType = function(type) {
		_overrideMime = type;
	}
	this.open = function(method, url, sync, userName, password) {
		//alert("At xmlhttp.js:\nmethod=" + method + "\nurl=" + url);
		if (!_checkParameters(method, url))
			return _method = (method) ? method: '';
		_url = (url) ? url: '';
		_userName = (userName) ? userName: '';
		_password = (password) ? password: '';
		_setReadyState(1);
	}
	this.openRequest = function(method, url, sync, userName, password) {
		return this.open(method, url, sync, userName, password);
	}
	this.send = function(data) {
		if (_stop) 
			return;
		var src = _createQuery(data);
		alert("src = " + src);
		var script = _createScript(src);
	}
	this.setRequestHeader = function(name, value) {
		if (_stop) 
			return;
		for (property in _requestHeaders) {
			if (property.toLowerCase() == name.toLowerCase()) {
				_requestHeaders[property] = value;
				return;
			}
		}
		_requestHeaders[name] = value;
	}
	var _method = '';
	var _url = '';
	var _userName = '';
	var _password = '';
	var _requestHeaders = {
		"HTTP-Referer": escape(document.location),
		"Content-Type": "application/x-www-form-urlencoded"
	}
	var _responseHeaders = {};
	var _overrideMime = "";
	var self = this;
	var _id = '';
	var _scripts = [];
	var _stop = false;
	var _throwError = function(description) {
		alert("XMLHTTP::_throwError: " + description);
		self.onerror(description);
		self.abort();
		return false;
	}
	var _createQuery = function(data) {
		if (!data) 
			data = '';
		var headers = '';
		for (property in _requestHeaders)
			headers += property + '=' + _requestHeaders[property] + '&';
		var originalsrc = _method + '$' + _id + '$' + _userName + "$" + _password + "$" + headers + '$' + _escape(data) + '$' + _url;
		var src = originalsrc;
		var max = _maximumRequestLength, request = [];
		var total = Math.floor(src.length / max), current = 0;
		while (src.length > 0) {
			var query = _apiURL + '?' + 'multipart' + '$' + _id + '$' + current+++'$' + total + '$' + src.substr(0, max);
			request.push(query);
			src = src.substr(max);
		}
		if (request.length == 1)
			src = _apiURL + '?' + originalsrc;
		else 
			src = request;
		return src;
	}
	var _checkParameters = function(method, url) {
		if (!method)
			return _throwError('Please, specify the query method (GET, POST or HEAD)');
		if (!url)
			return _throwError('Please, specify the URL');
		if (method.toLowerCase() != 'get' && method.toLowerCase() != 'post' && method.toLowerCase() != 'head')
			return _throwError('Please, specify either a GET, POST or a HEAD method');
		if (url.toLowerCase().substr(0, 7) != 'http://') 
			return _throwError('Only HTTP protocol is supported (http://)');
		return true;
	}
	var _createScript = function(src) {
		if ('object' == typeof src) {
			for (var i = 0; i < src.length; i++) 
				_createScript(src[i]);
			return;
		}
		var script = document.createElement('s' + 'cript');
		script.src = src;
		script.type = 'text/javascript';
		if (navigator.userAgent.indexOf('Safari'))
			script.charset = 'utf-8';
		script = document.getElementsByTagName('head')[0].appendChild(script);
		_scripts.push(script);
		return script;
	}
	var _escape = function(string) {
		string = escape(string);
		string = string.replace('+', '%2B');
		return string;
	}
	var _destroyScripts = function() {
		for (var i = 0; i < _scripts.length; i++)
			if (_scripts[i].parentNode)
				_scripts[i].parentNode.removeChild(_scripts[i]);
	}
	var _registerCallback = function() {
		_id = 'v' + Math.random().toString().substr(2);
		window[_id] = self;
	}
	var _setReadyState = function(number) {
		self.readyState = number;
		self.onreadystatechange();
		if (number == 4)
			self.onload();
	}
	var _parseXML = function() {
		var type = self.getResponseHeader('Content-type') + _overrideMime;
		if (! (type.indexOf('html') > -1 || type.indexOf('xml') > -1))
			return;
		if (document.implementation && document.implementation.createDocument && navigator.userAgent.indexOf('Opera') == -1) {
			var parser = new DOMParser();
			var xml = parser.parseFromString(self.responseText, "text/xml");
			self.responseXML = xml;
		}
		else if (window.ActiveXObject) {
			var xml = new ActiveXObject('MSXML2.DOMDocument.3.0');
			if (xml.loadXML(self.responseText))
				self.responseXML = xml;
		}
		else {
			var xml = document.body.appendChild(document.createElement('div'));
			xml.style.display = 'none';
			xml.innerHTML = self.responseText;
			_cleanWhitespace(xml, true);
			self.responseXML = xml.childNodes[0];
			document.body.removeChild(xml);
		}
	}
	var _cleanWhitespace = function(element, deep) {
		var i = element.childNodes.length;
		if (i == 0) return;
		do {
			var node = element.childNodes[--i];
			if (node.nodeType == 3 && !_cleanEmptySymbols(node.nodeValue))
				element.removeChild(node);
			if (node.nodeType == 1 && deep)
				_cleanWhitespace(node, true);
		} while ( i > 0 );
	}
	var _cleanEmptySymbols = function(string) {
		string = string.replace('\r', '');
		string = string.replace('\n', '');
		string = string.replace(' ', '');
		return (string.length == 0) ? false : true;
	}
	this._parse = function(object) {
		if (_stop) 
			return;
		if (object.multipart) 
			return;
		if (!object.success)
			return;
		_throwError(object.description);
		_responseHeaders = object.responseHeaders;
		this.status = object.status;
		this.statusText = object.statusText;
		this.responseText = object.responseText;
		_parseXML();
		_destroyScripts();
		_setReadyState(4);
	}
	_registerCallback();
}

