/* * ***** BEGIN LICENSE BLOCK ***** * Version: ZAPL 1.1 * * The contents of this file are subject to the Zimbra AJAX Public * License Version 1.1 ("License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.zimbra.com/license * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is: Zimbra AJAX Toolkit. * * The Initial Developer of the Original Code is Zimbra, Inc. * Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc. * All Rights Reserved. * * Contributor(s): * * ***** END LICENSE BLOCK ***** */ /** * Html Editor * * @author Ross Dargahi */ function DwtHtmlEditor(parent, className, posStyle, content, mode, blankIframeSrc) { if (arguments.length == 0) return; this.setBlankIframeSrc(blankIframeSrc); className = className || "DwtHtmlEditor"; DwtComposite.call(this, parent, className, posStyle); this._mode = mode == DwtHtmlEditor.HTML && this.isHtmlEditingSupported() ? mode : DwtHtmlEditor.TEXT; if (!content) content = (this._mode == DwtHtmlEditor.HTML) ? DwtHtmlEditor._INITIAL_HTML : ""; this._pendingContent = content; this._initialize(); } DwtHtmlEditor.prototype = new DwtComposite(); DwtHtmlEditor.prototype.constructor = DwtHtmlEditor; // Modes DwtHtmlEditor.HTML = 1; DwtHtmlEditor.TEXT = 2; // Styles DwtHtmlEditor.H1 = 1; DwtHtmlEditor.H2 = 2; DwtHtmlEditor.H3 = 3; DwtHtmlEditor.H4 = 4; DwtHtmlEditor.H5 = 5; DwtHtmlEditor.H6 = 6; DwtHtmlEditor.PARAGRAPH = 7; DwtHtmlEditor.ADDRESS = 8; DwtHtmlEditor.PREFORMATTED = 9; DwtHtmlEditor._STYLES = ["", "

", "

", "

", "

", "

", "
", "

", "

", "
"];

// Font Family
DwtHtmlEditor.ARIAL = 1;
DwtHtmlEditor.COURIER = 2;
DwtHtmlEditor.TIMES = 3;
DwtHtmlEditor.VERDANA = 4;

// Font Styles
DwtHtmlEditor.BOLD_STYLE = "bold";
DwtHtmlEditor.ITALIC_STYLE = "italic";
DwtHtmlEditor.UNDERLINE_STYLE = "underline";
DwtHtmlEditor.STRIKETHRU_STYLE = "strikethrough";
DwtHtmlEditor.SUBSCRIPT_STYLE = "subscript";
DwtHtmlEditor.SUPERSCRIPT_STYLE = "superscript";

// Justification
DwtHtmlEditor.JUSTIFY_LEFT = "justifyleft";
DwtHtmlEditor.JUSTIFY_CENTER = "justifycenter";
DwtHtmlEditor.JUSTIFY_RIGHT = "justifyright";
DwtHtmlEditor.JUSTIFY_FULL = "justifyfull";

// Indent
DwtHtmlEditor.OUTDENT = "outdent";
DwtHtmlEditor.INDENT = "indent";

// Elements
DwtHtmlEditor.HORIZ_RULE = "inserthorizontalrule";
DwtHtmlEditor.ORDERED_LIST = "insertorderedlist";
DwtHtmlEditor.UNORDERED_LIST = "insertunorderedlist";

// Direction
DwtHtmlEditor.DIRECTION_R2L;
DwtHtmlEditor.DIRECTION_L2R;

// PRIVATE Class Attributes

// Font Family Definitions & RegExs
DwtHtmlEditor._ARIAL = "Arial, Helvetica, sans-serif";
DwtHtmlEditor._COURIER = "Courier New, Courier, mono";
DwtHtmlEditor._TIMES = "Times New Roman, Times, serif";
DwtHtmlEditor._VERDANA = "Verdana, Arial, Helvetica, sans-serif";

DwtHtmlEditor._ARIAL_RE = /arial|helvetica|sans-serif/;
DwtHtmlEditor._TIMES_RE = /times|serif/;
DwtHtmlEditor._VERDANA_RE = /verdana/;
DwtHtmlEditor._COURIER_RE = /courier|mono/;


DwtHtmlEditor._H1_RE = /Heading 1|h1/;
DwtHtmlEditor._H2_RE = /Heading 2|h2/;
DwtHtmlEditor._H3_RE = /Heading 2|h3/;
DwtHtmlEditor._H4_RE = /Heading 2|h4/;
DwtHtmlEditor._H5_RE = /Heading 2|h5/;
DwtHtmlEditor._H6_RE = /Heading 2|h6/;
DwtHtmlEditor._PARAGRAPH_RE = /Normal|p/;
DwtHtmlEditor._ADDRESS_RE = /Address|address/;
DwtHtmlEditor._PREFORMATTED_RE = /Formatted|pre/;

DwtHtmlEditor._FONT_NAME = "fontname";
DwtHtmlEditor._FONT_SIZE = "fontsize";
DwtHtmlEditor._FONT_COLOR = "forecolor";
DwtHtmlEditor._FONT_HILITE = "hilitecolor";
DwtHtmlEditor._FONT_HILITE_IE = "backcolor";
DwtHtmlEditor._FORMAT_BLOCK = "formatblock";

/*cut
copy
paste
undo
redo
*/

DwtHtmlEditor._BLOCK_ELEMENTS = {
	address:1, body:1, div:1, dl:1, fieldset:1, form:1, h1:1, h2:1, h3:1, h4:1, h5:1, h6:1, 
	iframe:1, li:1, ol:1, p:1, pre:1, quote:1, table:1, tbody:1, td:1, textarea:1, tfoot: 1, 
	thead:1, tr:1, ul:1
};

DwtHtmlEditor._KEY2CMDS = {
	"b":DwtHtmlEditor.BOLD_STYLE, "i":DwtHtmlEditor.ITALIC_STYLE, "u":DwtHtmlEditor.UNDERLINE_STYLE, 
	"s":DwtHtmlEditor.STRIKETHRU_STYLE, "l":DwtHtmlEditor.JUSTIFY_LEFT, "e":DwtHtmlEditor.JUSTIFY_CENTER, 
	"r":DwtHtmlEditor.JUSTIFY_RIGHT, "j":DwtHtmlEditor.JUSTIFY_FULL, "1":DwtHtmlEditor._STYLES[1],
	"2":DwtHtmlEditor._STYLES[1], "3":DwtHtmlEditor._STYLES[3], "4":DwtHtmlEditor._STYLES[4], 
	"5":DwtHtmlEditor._STYLES[5], "6":DwtHtmlEditor._STYLES[6], "0":"DUMP"
};

DwtHtmlEditor._INITIAL_HTML = "";

DwtHtmlEditor.prototype.focus =
function() {
	this._getIframeWin().focus();
	// Hack to fix IE focusing bug
	if (AjxEnv.isIE) {
		if (this._currInsPt) {
			if (this._currInsPt.text.length <= 1)
				this._currInsPt.collapse(false);
			this._currInsPt.select();
		}
	}
}

DwtHtmlEditor.prototype.addStateChangeListener = 
function(listener) {
	this.addListener(DwtEvent.STATE_CHANGE, listener);
}

DwtHtmlEditor.prototype.removeStateChangeListener = 
function(listener) { 
	this.removeListener(DwtEvent.STATE_CHANGE, listener);
}

DwtHtmlEditor.prototype.clear =
function() {
	this.setContent((this._mode == DwtHtmlEditor.HTML) ? DwtHtmlEditor._INITIAL_HTML : "");
}

DwtHtmlEditor.prototype.enable =
function(enable) {
	var doc = this.getDocument();
	if (this._textAreaId != null)
		Dwt.getDomObj(doc, this._textAreaId).disabled = !enable;
	if (this._iFrameId != null)
		Dwt.getDomObj(doc, this._iframeId).disabled = !enable;
}

DwtHtmlEditor.prototype.setBlankIframeSrc = function (src) {
	this._blankIframeSrc = src;
};

DwtHtmlEditor.prototype.isHtmlEditingSupported =
function() {
	return (!AjxEnv.isGeckoBased && !AjxEnv.isIE) ? false : true;
}

/**
 * Get the content
*/
DwtHtmlEditor.prototype.getContent =
function() {
	if (this._mode == DwtHtmlEditor.HTML) {
		var iframeDoc = this._getIframeDoc();
		return iframeDoc.body ? this._getIframeDoc().body.innerHTML : "";
	} else {
		return Dwt.getDomObj(this.getDocument(), this._textAreaId).value;
	}
}

/**
 * Set the content to be displayed. This can be HTML
*/
DwtHtmlEditor.prototype.setContent =
function(content) {
	if (this._mode == DwtHtmlEditor.HTML) {
		// iframe's document isnt done loading :(
		this._pendingContent = content;
		this._setContentOnTimer();
	} else {
		Dwt.getDomObj(this.getDocument(), this._textAreaId).value = content;
	}
}

DwtHtmlEditor.prototype.insertElement =
function(element) {
	this._execCommand(element);
}

/**
* Changes the editor mode.
*
* @param mode	The new mode
* @param convert	If new mode -> HTML and convert, then the content of the widget is escaped. If
*		mode -> Text and convert, then text is stripped out of content
*/
DwtHtmlEditor.prototype.setMode =
function(mode, convert) {
	if (mode == this._mode || (mode != DwtHtmlEditor.HTML && mode != DwtHtmlEditor.TEXT))
		return;
	
	this._mode = mode;
	var doc = this.getDocument();
	if (mode == DwtHtmlEditor.HTML) {
		var textArea = Dwt.getDomObj(doc, this._textAreaId);
		var iFrame;
		if (this._iFrameId != null) {
			this._getIframeDoc().body.innerHTML = (convert) ? AjxStringUtil.htmlEncodeSpace(textArea.value) : textArea.value;
			iFrame = Dwt.getDomObj(doc, this._iFrameId);
		} else {
			iFrame = this._initHtmlMode((convert) ? AjxStringUtil.htmlEncodeSpace(textArea.value) : textArea.value);
		}
		Dwt.setVisible(textArea, false);
		Dwt.setVisible(iFrame, true);
		// XXX: mozilla hack
		if (AjxEnv.isGeckoBased)
			this._enableDesignMode([this._getIframeDoc()]);
	} else {
		var textArea = this._textAreaId != null
			? Dwt.getDomObj(doc, this._textAreaId)
			: this._initTextMode(true);
		
		// If we have pending content, then an iFrame is being created. This can happen
		// if the widget is instantiated and immediate setMode is called w/o getting out
		// to the event loop where _finishHtmlMode is triggered
		var content = (!this._pendingContent) ? this._getIframeDoc().innerHTML : this._pendingContent;
		textArea.value = (convert) ? this._convertHtml2Text() : this._getIframeDoc().innerHTML;;

		Dwt.setVisible(Dwt.getDomObj(doc, this._iFrameId), false);
		Dwt.setVisible(textArea, true);	
	}
}

DwtHtmlEditor.prototype.setTextDirection =
function(direction) {
	if (this._mode != DwtHtmlEditor.HTML)
		return;
		
	var dir = (direction == DwtHtmlEditor.DIRECTION_R2L) ? "rtl" : "ltr";
	var el = this._getParentElement();
	
	DBG.println("EL: " + el.tagName.toLowerCase() + " - " + DwtHtmlEditor._BLOCK_ELEMENTS[el.tagName.toLowerCase()]);

	while (el && !DwtHtmlEditor._BLOCK_ELEMENTS[el.tagName.toLowerCase()])
		el = el.parentNode;
		
	if (el)
		el.style.direction = el.style.direction == dir ? "" : dir;
}

// Font sizes should be 1-7
DwtHtmlEditor.prototype.setFont =
function(family, style, size, color, hiliteColor) {
	if (family) {
		switch (family) {
			case DwtHtmlEditor.ARIAL:
				this._execCommand(DwtHtmlEditor._FONT_NAME, DwtHtmlEditor._ARIAL);
				break;			
			case DwtHtmlEditor.COURIER:
				this._execCommand(DwtHtmlEditor._FONT_NAME, DwtHtmlEditor._COURIER);
				break;			
			case DwtHtmlEditor.TIMES:
				this._execCommand(DwtHtmlEditor._FONT_NAME, DwtHtmlEditor._TIMES);
				break;	
			case DwtHtmlEditor.VERDANA:
				this._execCommand(DwtHtmlEditor._FONT_NAME, DwtHtmlEditor._VERDANA);
				break;	
		}
	}
	
	if (style)
		this._execCommand(style);
		
	if (size && size > 0 && size < 8)
		this._execCommand(DwtHtmlEditor._FONT_SIZE, size);
		
	if (color)
		this._execCommand(DwtHtmlEditor._FONT_COLOR, color);

	if (hiliteColor)
		this._execCommand((AjxEnv.isIE) ? DwtHtmlEditor._FONT_HILITE_IE : DwtHtmlEditor._FONT_HILITE, hiliteColor);
}

DwtHtmlEditor.prototype.setJustification =
function(justification) {
	this._execCommand(justification);
}

DwtHtmlEditor.prototype.setIndent =
function(indent) {
	this._execCommand(indent);
}

DwtHtmlEditor.prototype.setStyle =
function(style) {
	this._execCommand(DwtHtmlEditor._FORMAT_BLOCK, DwtHtmlEditor._STYLES[style]);
}

DwtHtmlEditor.prototype.setSize =
function(width, height) {
	DwtComposite.prototype.setSize.call(this, width, height);
	var doc = this.getDocument();
	var htmlEl = this.getHtmlElement();
	
	if (this._iFrameId != null) {
		var iFrame = Dwt.getDomObj(this.getDocument(), this._iFrameId);
		iFrame.width = htmlEl.style.width;
		iFrame.height = htmlEl.style.height;
	} else {
		var textArea = Dwt.getDomObj(this.getDocument(), this._textAreaId);
		textArea.style.width = htmlEl.style.width;
		textArea.style.height = htmlEl.style.height;
	}
}

DwtHtmlEditor.prototype.getIframe = 
function() {
	return Dwt.getDomObj(this.getDocument(), this._iFrameId);
}

DwtHtmlEditor.prototype._initialize = 
function() {
	if (this._mode == DwtHtmlEditor.HTML) 
		this._initHtmlMode(this._pendingContent);
	else
		this._initTextMode();
}

DwtHtmlEditor.prototype._initTextMode =
function(ignorePendingContent) {
	var doc = this.getDocument();
	var htmlEl = this.getHtmlElement();
	this._textAreaId = "textarea_" + Dwt.getNextId();
	var textArea = doc.createElement("textarea");
	textArea.className = "DwtHtmlEditorTextArea";
	textArea.id = this._textAreaId;
	htmlEl.appendChild(textArea);
	
	// We will ignore pending content if we are called from setMode. See setMode for
	// documentation
	if (!ignorePendingContent) {
		textArea.value = this._pendingContent;
		this._pendingContent = null;
	}
	
	return textArea;
}

DwtHtmlEditor.prototype._initHtmlMode =
function(content) {
	var iFrame = this._createIFrameEl();
	
	this._keyEvent = new DwtKeyEvent();
	this._stateEvent = new DwtHtmlEditorStateEvent();
	this._stateEvent.dwtObj = this;
	
	this._updateStateAction = new AjxTimedAction();
	this._updateStateAction.obj = this;
	this._updateStateAction.method = DwtHtmlEditor.prototype._updateState;
	
	this._pendingContent = content || "";
	
	// IE can sometimes race ahead and execute script before the underlying component is created
	var timedAction = new AjxTimedAction();
	timedAction.obj = this;
	timedAction.method = DwtHtmlEditor.prototype._finishHtmlModeInit;
	AjxTimedAction.scheduleAction(timedAction, 100);
	
	return iFrame;
}

DwtHtmlEditor.prototype._createIFrameEl = 
function() {
	var doc = this.getDocument();
	var htmlEl = this.getHtmlElement();
	this._iFrameId  = "iframe_" + Dwt.getNextId();
	var iFrame = doc.createElement("iframe");
	iFrame.id = this._iFrameId;
	iFrame.className = "DwtHtmlEditorIFrame";
	if (AjxEnv.isIE && location.protocol == "https:") {
		iFrame.src = (this._blankIframeSrc != null)? this._blankIframeSrc: "";
	}
	htmlEl.appendChild(iFrame);
	
	return iFrame;
}

DwtHtmlEditor.prototype._finishHtmlModeInit =
function(params) {
	var doc = this._getIframeDoc();	
	try {
		doc.open();
		doc.write(this._pendingContent);
		doc.close(); 
	} catch (ex) {
		// TODO Replace
		alert("Error loading content");
		return;
	}
	
	var p = [doc];
	this._enableDesignMode(p);
	this._registerEditorEventHandlers(Dwt.getDomObj(this.getDocument(), this._iFrameId), doc);
	this.focus();
	this._updateState();
}

DwtHtmlEditor.prototype._getIframeDoc =
function() {
	return Dwt.getIframeDoc(Dwt.getDomObj(this.getDocument(), this._iFrameId));
}

DwtHtmlEditor.prototype._getIframeWin =
function() {
	return Dwt.getIframeWindow(Dwt.getDomObj(this.getDocument(), this._iFrameId));
}

DwtHtmlEditor.prototype._getParentElement = 
function() {
	if (AjxEnv.isIE) {
		var iFrameDoc = this._getIframeDoc();
		var selection = iFrameDoc.selection;
		var range = selection.createRange();
		if (selection.type == "None" || selection.type == "Text")
			// If selection is None still have a parent element
			return selection.createRange().parentElement();
		else if (selection.type == "Control")
			return selection.createRange().item(0);
		else
			return iFrameDoc.body;
	} else {
		try {
			var range = this._getRange();
			var p = range.commonAncestorContainer;
			if (!range.collapsed && range.startContainer == range.endContainer 
				&& range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes())
				p = range.startContainer.childNodes[range.startOffset];
DBG.println("P: " + p);
			while (p.nodeType == 3)
				p = p.parentNode;
			return p;
		} catch (e) {
			return null;
		}
	}
}

DwtHtmlEditor.prototype._getRange =
function() {
	var iFrameDoc = this._getIframeDoc();
	if (AjxEnv.isIE) {
		return iFrameDoc.selection;
	} else {
		this.focus();
		var selection = this._getIframeWin().getSelection();
		if (selection != null) {
			try {
				return selection.getRangeAt(0);
			} catch(e) {
				return tiFrameDoc.createRange();
			}
		} else {
			return iFrameDoc.createRange();
		}
	}
}

DwtHtmlEditor.prototype._registerEditorEventHandlers =
function(iFrame, iFrameDoc) {
	var events = ["mouseup", "keydown", "keypress", "drag", "mousedown"];
	var me = this;
	// TODO - Hopefully this closure doesn't cause a memory leak!!!!
	var func = function (evt) {return me._handleEditorEvent(AjxEnv.isIE ? iFrame.contentWindow.event : evt);};
	
	if (AjxEnv.isIE) {
		for (var i in events)
			iFrameDoc.attachEvent("on" + events[i], func);
		
	} else {
		for (var i in events)
			iFrameDoc.addEventListener(events[i],  func, true);
	}
}

DwtHtmlEditor.prototype._handleEditorEvent =
function(ev) {
	var retVal = true;
	
	// If we have a mousedown event, then let DwtMenu know. This is a nasty hack that we have to do since
	// the iFrame is in a different document etc
	if (ev.type == "mousedown") {
		DwtMenu._outsideMouseDownListener(null);
		return true;
	}
	
	if (DwtKeyEvent.isKeyPressEvent(ev)) {
		var ke = this._keyEvent;
		ke.setFromDhtmlEvent(ev);
		if (ke.ctrlKey) {
			var key = String.fromCharCode(ke.charCode).toLowerCase();
			var cmd = null;
			var value = null;	

			switch (key) {
			    case '1':
			    case '2':
			    case '3':
			    case '4':
			    case '5':
			    case '6':
					cmd = DwtHtmlEditor._FORMAT_BLOCK;
					value = DwtHtmlEditor._KEY2CMDS[key];
					break;
					
				case '0':
					try {
						this.setMode((this._mode == DwtHtmlEditor.HTML) ? DwtHtmlEditor.TEXT : DwtHtmlEditor.HTML, true);
					} catch (e) {
						DBG.println("EXCEPTION!: " + e);
					}
					ke._stopPropagation = true;
					ke._returnValue = false;
					ke.setToDhtmlEvent(ev);
					return false;
					
				default:
					// IE Has full on keyboard shortcuts
					//if (!AjxEnv.isIE)
						cmd = DwtHtmlEditor._KEY2CMDS[key];
					break;
			}
			DBG.println("CMD: " + cmd);			
			if (cmd) {
				this._execCommand(cmd, value);
				DBG.println("AFTER EXEC");
				ke._stopPropagation = true;
				ke._returnValue = false;
				ke.setToDhtmlEvent(ev);
				retVal = false;
			}
		}
	}
	
	// TODO notification for any updates etc
	// Set's up the a range for the current ins point or selection. This is IE only because the iFrame can
	// easily lose focus (e.g. by clicking on a button in the toolbar) and we need to be able to get back
	// to the correct insertion point/selection.
	if (AjxEnv.isIE) {
		var iFrameDoc = this._getIframeDoc();
		this._currInsPt = iFrameDoc.selection.createRange();
		// If just at the insertion point, then collapse so that we don't get
		// a range selection on a call to DwtHtmlEditor.focus()
		if (iFrameDoc.selection.type == "None") {
			this._currInsPt.collapse(false);
			this._currInsPt.text = "";
		}
	}
	
	if (this._stateUpdateActionId != null) 
		AjxTimedAction.cancelAction(this._stateUpdateActionId);
	
	this._stateUpdateActionId = AjxTimedAction.scheduleAction(this._updateStateAction, 100);

	return retVal;
}

DwtHtmlEditor.prototype._updateState =
function() {
	if (this._mode != DwtHtmlEditor.HTML)
		return;
		
	this._stateUpdateActionId = null;
	var ev = this._stateEvent;
	ev.reset();
	
	var iFrameDoc = this._getIframeDoc();
	ev.isBold = iFrameDoc.queryCommandState(DwtHtmlEditor.BOLD_STYLE);
	ev.isItalic = iFrameDoc.queryCommandState(DwtHtmlEditor.ITALIC_STYLE);
	ev.isUnderline = iFrameDoc.queryCommandState(DwtHtmlEditor.UNDERLINE_STYLE);
	ev.isStrikeThru = iFrameDoc.queryCommandState(DwtHtmlEditor.STRIKETHRU_STYLE);
	ev.isSuperscript = iFrameDoc.queryCommandState(DwtHtmlEditor.SUPERSCRIPT_STYLE);
	ev.isSubscript = iFrameDoc.queryCommandState(DwtHtmlEditor.SUBSCRIPT_STYLE);
	ev.isOrderedList = iFrameDoc.queryCommandState(DwtHtmlEditor.ORDERED_LIST);
	ev.isUnorderedList = iFrameDoc.queryCommandState(DwtHtmlEditor.UNORDERED_LIST);
	
	// Don't futz with the order of the if statements below. They are important due to the 
	// nature of the RegExs
	var family = iFrameDoc.queryCommandValue(DwtHtmlEditor._FONT_NAME);
	if (family) {
		family = family.toLowerCase();
		if (family.search(DwtHtmlEditor._VERDANA_RE) != -1)
			ev.fontFamily = DwtHtmlEditor.VERDANA;
		else if (family.search(DwtHtmlEditor._ARIAL_RE) != -1)
			ev.fontFamily = DwtHtmlEditor.ARIAL;		
		else if (family.search(DwtHtmlEditor._TIMES_RE) != -1)
			ev.fontFamily = DwtHtmlEditor.TIMES;
		else if (family.search(DwtHtmlEditor._COURIER_RE) != -1)
			ev.fontFamily = DwtHtmlEditor.COURIER;
	}
	
	ev.fontSize = iFrameDoc.queryCommandValue(DwtHtmlEditor._FONT_SIZE);
	ev.backgroundColor = iFrameDoc.queryCommandValue((AjxEnv.isIE) ? "backcolor" : "hilitecolor");
	ev.color = iFrameDoc.queryCommandValue("forecolor");
	ev.justification = null;
	ev.direction = null;
	
	var style = iFrameDoc.queryCommandValue(DwtHtmlEditor._FORMAT_BLOCK);
	if (style) {
		if (style.search(DwtHtmlEditor._H1_RE) != -1)
			ev.style = DwtHtmlEditor.H1;
		else if (style.search(DwtHtmlEditor._H2_RE) != -1)
			ev.style = DwtHtmlEditor.H2;
		else if (style.search(DwtHtmlEditor._H3_RE) != -1)
			ev.style = DwtHtmlEditor.H3;
		else if (style.search(DwtHtmlEditor._H4_RE) != -1)
			ev.style = DwtHtmlEditor.H4;
		else if (style.search(DwtHtmlEditor._H5_RE) != -1)
			ev.style = DwtHtmlEditor.H5;
		else if (style.search(DwtHtmlEditor._H6_RE) != -1)
			ev.style = DwtHtmlEditor.H6;
		else if (style.search(DwtHtmlEditor._PARAGRAPH_RE) != -1)
			ev.style = DwtHtmlEditor.PARAGRAPH;
		else if (style.search(DwtHtmlEditor._ADDRESS_RE) != -1)
			ev.style = DwtHtmlEditor.ADDRESS;
		else if (style.search(DwtHtmlEditor._PREFORMATTED_RE) != -1)
			ev.style = DwtHtmlEditor.PREFORMATTED;
	}
	
	if (iFrameDoc.queryCommandState(DwtHtmlEditor.JUSTIFY_LEFT))
		ev.justification = DwtHtmlEditor.JUSTIFY_LEFT;
	else if (iFrameDoc.queryCommandState(DwtHtmlEditor.JUSTIFY_CENTER))
		ev.justification = DwtHtmlEditor.JUSTIFY_CENTER;
	else if (iFrameDoc.queryCommandState(DwtHtmlEditor.JUSTIFY_RIGHT))
		ev.justification = DwtHtmlEditor.JUSTIFY_RIGHT;
	else if (iFrameDoc.queryCommandState(DwtHtmlEditor.JUSTIFY_FULL))
		ev.justification = DwtHtmlEditor.JUSTIFY_FULL;


	// Notify any listeners
	if (this.isListenerRegistered(DwtEvent.STATE_CHANGE))
		this.notifyListeners(DwtEvent.STATE_CHANGE, ev);
}

DwtHtmlEditor.prototype._enableDesignMode =
function(params) {
	if (!params) return;
	
	var iFrameDoc = params[0];
	try {
		//doc.body.contentEditable = true; <= IE
		iFrameDoc.designMode = "on";
	} catch (ex) {
		//Gecko may take some time to enable design mode..
		if (AjxEnv.isGeckoBased) {
			var ta = new AjxTimedAction();
			ta.obj = this;
			ta.method = this._enableDesignMode;
			ta.params.add(iFrameDoc);
			AjxTimedAction.scheduleAction(ta, 10);
			return true;
		} else {
			// TODO Should perhaps throw an exception?
			return false;
		}
	}
}

DwtHtmlEditor.prototype._setContentOnTimer = 
function() {
	var iframeDoc = this._getIframeDoc();
	try {
		iframeDoc.body.innerHTML = this._pendingContent;
		//this._pendingContent = null;
		// XXX: mozilla hack
		if (AjxEnv.isGeckoBased)
			this._enableDesignMode([iframeDoc]);
	} catch (ex) {
		var ta = new AjxTimedAction();
		ta.obj = this;
		ta.method = this._setContentOnTimer;
		AjxTimedAction.scheduleAction(ta, 10);
		return true;
	}
}

DwtHtmlEditor.prototype._execCommand =
function(command, option) {
	if (this._mode != DwtHtmlEditor.HTML)
		return;
		
// DBG.println("CMD: " + command + " - Option: " + option);

	try {
		this.focus();
		this._getIframeDoc().execCommand(command, false, option);	
	} catch (e) {
		// TODO remove
		alert(e);
		// perhaps retry the command?
	}
	this._updateState();
}

DwtHtmlEditor.prototype._convertHtml2Text =
function() {
	var iFrameDoc = this._getIframeDoc();
	return AjxStringUtil.convertHtml2Text(iFrameDoc.body);
}