/********************************************************************
 * openWYSIWYG v1.46c Copyright (c) 2006 openWebWare.com 
 * Contact us at devs@openwebware.com
 * This copyright notice MUST stay intact for use.
 *
 * $Id: wysiwyg.js,v 1.10 2006/12/25 09:17:07 xhaggi Exp $
 *
 * An open source WYSIWYG editor for use in web based applications.
 * For full source code and docs, visit http://www.openwebware.com
 *
 * This library is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU Lesser General Public License as published 
 * by the Free Software Foundation; either version 2.1 of the License, or 
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along 
 * with this library; if not, write to the Free Software Foundation, Inc., 59 
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA  
 ********************************************************************/
var WYSIWYG = {

	/**
	 * Settings class, holds all customizeable properties
	 */
	Settings: function() {
	
		// Images Directory
		this.ImagesDir = "editor/images/";
		
		// Popups Directory
		this.PopupsDir = "editor/popups/";
		
		// CSS Directory File
		this.CSSFile = "editor/styles/wysiwyg.css";		
		
		// Default WYSIWYG width and height (use px or %)
		this.Width = "500px";
		this.Height = "200px";
		
		// Default stylesheet of the WYSIWYG editor window
		this.DefaultStyle = "font-family: Arial; font-size: 12px; background-color: #FFFFFF";
		
		// Stylesheet if editor is disabled
		this.DisabledStyle = "font-family: Arial; font-size: 12px; background-color: #EEEEEE";
				
		// Width + Height of the preview window
		this.PreviewWidth = 500;
		this.PreviewHeight = 400;
		
		// Confirmation message if you strip any HTML added by word
		this.RemoveFormatConfMessage = "Clean HTML inserted by MS Word ?";
		
		// Nofication if browser is not supported by openWYSIWYG, leave it blank for no message output.
		this.NoValidBrowserMessage = "openWYSIWYG does not support your browser.";
				
		// Anchor path to strip, leave it blank to ignore
		// or define auto to strip the path where the editor is placed 
		// (only IE)
		this.AnchorPathToStrip = "auto";
		
		// Image path to strip, leave it blank to ignore
		// or define auto to strip the path where the editor is placed 
		// (only IE)
		this.ImagePathToStrip = "auto";
		
		// Enable / Disable the custom context menu
		this.ContextMenu = true;
		
		// Enabled the status bar update. Within the status bar 
		// node tree of the actually selected element will build
		this.StatusBarEnabled = true;
		
		// If enabled than the capability of the IE inserting line breaks will be inverted.
		// Normal: ENTER = <p> , SHIFT + ENTER = <br>
		// Inverted: ENTER = <br>, SHIFT + ENTER = <p>
		this.InvertIELineBreaks = false;
		
		// Replace line breaks with <br> tags
		this.ReplaceLineBreaks = false;
		
		// Insert image implementation
		this.ImagePopupFile = "";
		this.ImagePopupWidth = 0;
		this.ImagePopupHeight = 0;
		
		// Holds the available buttons displayed 
		// on the toolbar of the editor
		this.Toolbar = new Array();
		this.Toolbar[0] = new Array(
			"font", 
			"fontsize",	
			"bold", 
			"italic", 
			"underline", 
			"strikethrough",
			"seperator", 
			"forecolor", 
			"backcolor", 
			"seperator", 
			"justifyleft", 
			"justifycenter", 
			"justifyright", 
			"seperator", 
			"unorderedlist", 
			"orderedlist",
			"outdent", 
			"indent"
		);
		this.Toolbar[1] = new Array(
			"subscript", 
			"superscript", 
			"seperator", 
			"cut", 
			"copy", 
			"paste",
			"removeformat",
			"seperator", 
			"undo", 
			"redo", 
			"seperator", 
			"inserttable", 
			"insertimage", 
			"createlink", 
			"seperator",  
			"preview", 
			"print",
			"seperator", 
			"viewSource", 
			"seperator", 
			"help"
		);
			
		// List of available font types
	    this.Fonts = new Array(
			"Arial", 
			"Sans Serif", 
			"Tahoma", 
			"Verdana", 
			"Courier New", 
			"Georgia", 
			"Times New Roman", 
			"Impact", 
			"Comic Sans MS"
		);
		
		// List of available font sizes 
	    this.Fontsizes = new Array(
			"1", 
			"2", 
			"3", 
			"4", 
			"5", 
			"6", 
			"7"
		);
				
		// Add the given element to the defined toolbar
		// on the defined position
		this.addToolbarElement = function(element, toolbar, position) {
			if(element != "seperator") {this.removeToolbarElement(element);}
			if(this.Toolbar[toolbar-1] == null) {
				this.Toolbar[toolbar-1] = new Array();
			}
			this.Toolbar[toolbar-1].splice(position+1, 1, element);			
		};
		
		// Remove an element from the toolbar
		this.removeToolbarElement = function(element) {
			if(element == "seperator") {return;} // do not remove seperators
			for(var i=0;i<this.Toolbar.length;i++) {
				if(this.Toolbar[i]) {
					var toolbar = this.Toolbar[i];
					for(var j=0;j<toolbar.length;j++) {
						if(toolbar[j] != null && toolbar[j] == element) {
							this.Toolbar[i].splice(j,1);
						}
					}
				}
			}
		};
		
		// clear all or a given toolbar
		this.clearToolbar = function(toolbar) {
			if(typeof toolbar == "undefined") {
				this.Toolbar = new Array();
			}
			else {
				this.Toolbar[toolbar+1] = new Array();
			}
		};
		
	},
	
		
	/* ---------------------------------------------------------------------- *\
		!! Do not change something below or you know what you are doning !!
	\* ---------------------------------------------------------------------- */	

	// List of available block formats (not in use)
	//BlockFormats: new Array("Address", "Bulleted List", "Definition", "Definition Term", "Directory List", "Formatted", "Heading 1", "Heading 2", "Heading 3", "Heading 4", "Heading 5", "Heading 6", "Menu List", "Normal", "Numbered List"),

	// List of available actions and their respective ID and images
	ToolbarList: {
	//Name              buttonID               buttonTitle           	buttonImage               buttonImageRollover
	"insertpdf":      ['InsertPDF',            'InsertPDF',         	'insert_pdf.gif',         'insert_pdf_on.gif'],
	"bold":           ['Bold',                 'Bold',               	'bold.gif',               'bold_on.gif'],
	"italic":         ['Italic',               'Italic',             	'italics.gif',            'italics_on.gif'],
	"underline":      ['Underline',            'Underline',          	'underline.gif',          'underline_on.gif'],
	"strikethrough":  ['Strikethrough',        'Strikethrough',      	'strikethrough.gif',      'strikethrough_on.gif'],
	"seperator":      ['',                     '',                   	'seperator.gif',          'seperator.gif'],
	"subscript":      ['Subscript',            'Subscript',          	'subscript.gif',          'subscript_on.gif'],
	"superscript":    ['Superscript',          'Superscript',        	'superscript.gif',        'superscript_on.gif'],
	"justifyleft":    ['Justifyleft',          'Justifyleft',        	'justify_left.gif',       'justify_left_on.gif'],
	"justifycenter":  ['Justifycenter',        'Justifycenter',      	'justify_center.gif',     'justify_center_on.gif'],
	"justifyright":   ['Justifyright',         'Justifyright',       	'justify_right.gif',      'justify_right_on.gif'],
	"unorderedlist":  ['InsertUnorderedList',  'Insert Unordered List',	'list_unordered.gif',     'list_unordered_on.gif'],
	"orderedlist":    ['InsertOrderedList',    'Insert Ordered List',  	'list_ordered.gif',       'list_ordered_on.gif'],
	"outdent":        ['Outdent',              'Outdent',            	'indent_left.gif',        'indent_left_on.gif'],
	"indent":         ['Indent',               'Indent',             	'indent_right.gif',       'indent_right_on.gif'],
	"cut":            ['Cut',                  'Cut',                	'cut.gif',                'cut_on.gif'],
	"copy":           ['Copy',                 'Copy',               	'copy.gif',               'copy_on.gif'],
	"paste":          ['Paste',                'Paste',              	'paste.gif',              'paste_on.gif'],
	"forecolor":      ['ForeColor',            'Fore Color',          	'forecolor.gif',          'forecolor_on.gif'],
	"backcolor":      ['BackColor',            'Back Color',          	'backcolor.gif',          'backcolor_on.gif'],
	"undo":           ['Undo',                 'Undo',               	'undo.gif',               'undo_on.gif'],
	"redo":           ['Redo',                 'Redo',               	'redo.gif',               'redo_on.gif'],
	"inserttable":    ['InsertTable',          'Insert Table',        	'insert_table.gif',       'insert_table_on.gif'],
	"insertimage":    ['InsertImage',          'Insert Image',        	'insert_picture.gif',     'insert_picture_on.gif'],
	"createlink":     ['CreateLink',           'Create Link',         	'insert_hyperlink.gif',   'insert_hyperlink_on.gif'],
	"viewSource":     ['ViewSource',           'View Source',         	'view_source.gif',        'view_source_on.gif'],
	"viewText":       ['ViewText',             'View Text',           	'view_text.gif',          'view_text_on.gif'],
	"help":           ['Help',                 'Help',               	'help.gif',               'help_on.gif'],
	"selectfont":     ['SelectFont',           'Select Font',        	'select_font.gif',        'select_font_on.gif'],
	"selectsize":     ['SelectSize',           'Select Size',        	'select_size.gif',        'select_size_on.gif'],
	"preview":		  ['Preview', 			   'Preview',       	 	'preview.gif',			   'preview_on.gif'],
	"print":		  ['Print', 			   'Print',       	 	 	'print.gif',			   'print_on.gif'],
	"removeformat":   ['RemoveFormat',         'Strip Word HTML',    	'remove_format.gif',      'remove_format_on.gif'],
	"delete":         ['Delete',               'Delete',             	'delete.gif',     		   'delete_on.gif']
	},
	
	// stores the different settings for each textarea
	// the textarea identifier is used to store the settings object
	config: new Array(),
	// Create viewTextMode global variable and set to 0
	// enabling all toolbar commands while in HTML mode
	viewTextMode: new Array(),	
	
	/**
	 * Get the range of the given selection
	 *
	 * @param {Selection} sel Selection object
	 * @return {Range} Range object
	 */
	getRange: function(sel) {
		return sel.createRange ? sel.createRange() : sel.getRangeAt(0);
	},
	
	/**
	 * Get the iframe object of the WYSIWYG editor
	 * 
	 * @param {String} n Editor identifier 
	 * @return {HtmlIframeObject} Iframe object
	 */
	getEditor: function(n) {
		return $("wysiwyg" + n);
	},
	
	/**
	 * Get editors window element
	 *
	 * @param {String} n Editor identifier 
	 * @return {Object} Html window object
	 */
	getEditorWindow: function(n) {
		return this.getEditor(n).contentWindow;
	},
	
	/**
	 * Attach the WYSIWYG editor to the given textarea element
	 *
	 * @param {String} id Textarea identifier (all = all textareas)
	 * @param {Settings} settings the settings which will be applied to the textarea
	 */
	attach: function(id, settings) {	
		if(id != "all") {	
			this.setSettings(id, settings);
			WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
			WYSIWYG_Core.addEvent(window, "load", function generateEditor() {WYSIWYG._generate(id, settings);});
		}
		else {
			WYSIWYG_Core.addEvent(window, "load", function generateEditor() {WYSIWYG.attachAll(settings);});
		}
	},
	
	/**
	 * Attach the WYSIWYG editor to all textarea elements
	 *
	 * @param {Settings} settings Settings to customize the look and feel
	 */
	attachAll: function(settings) {
		var areas = document.getElementsByTagName("textarea");
		for(var i=0;i<areas.length;i++) {
			var id = areas[i].getAttribute("id");
			if(id == null || id == "") continue;
			this.setSettings(id, settings);
			WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
			WYSIWYG._generate(id, settings);
		}
	},
	
	/**
	 * Display an iframe instead of the textarea. 
	 * It's used as textarea replacement to display HTML.
	 *
	 * @param id Textarea identifier (all = all textareas)
	 * @param settings the settings which will be applied to the textarea
	 */
	display: function(id, settings) {	
		if(id != "all") {	
			this.setSettings(id, settings);
			WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
			WYSIWYG_Core.addEvent(window, "load", function displayIframe() {WYSIWYG._display(id, settings);});
		}
		else {
			WYSIWYG_Core.addEvent(window, "load", function displayIframe() {WYSIWYG.displayAll(settings);});
		}
	},
	
	/**
	 * Display an iframe instead of the textarea. 
	 * It's apply the iframe to all textareas found in the current document.
	 *
	 * @param settings Settings to customize the look and feel
	 */
	displayAll: function(settings) {
		var areas = document.getElementsByTagName("textarea");
		for(var i=0;i<areas.length;i++) {
			var id = areas[i].getAttribute("id");
			if(id == null || id == "") continue;
			this.setSettings(id, settings);
			WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
			WYSIWYG._display(id, settings);
		}
	},
		
	/**
	 * Set settings in config array, use the textarea id as identifier
	 * 
	 * @param n Textarea identifier (all = all textareas)
	 * @param settings the settings which will be applied to the textarea
	 */
	setSettings: function(n, settings) {
		if(typeof(settings) != "object") {
			this.config[n] = new this.Settings();
		}
		else {
			this.config[n] = settings;
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : insertImage()
	  Description : insert an image into WYSIWYG in rich text
	  Usage       : WYSIWYG.insertImage("test.jpg", 500, 200, "center", 0, "Picture Alternativ", 5, 5, "textareaID")
	  Arguments   : src - Source of the image
	  				width - Width
					height - Height
					align - Alignment of the image
					border - border 
					alt - Alternativ Text
					hspace - Horizontal Space
					vspace - Vertical Space
	                n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	insertImage: function(src,style, width, height, align, border, alt, hspace, vspace, n) {
	
		// get editor
		var doc = this.getEditorWindow(n).document;
		// get selection and range
		var sel = this.getSelection(n);
		var range = this.getRange(sel);
		
		// the current tag of range
		var img = this.findParentTag("img", range);
		
		// element is not a link
		var update = (img == null) ? false : true;
		if(!update) {
			img = doc.createElement("img");
		}
		
		// set the attributes
		WYSIWYG_Core.setAttribute(img, "src", src);
		WYSIWYG_Core.setAttribute(img, "style", "width:" + width + ";height:" + height);
		if(align != "") { WYSIWYG_Core.setAttribute(img, "align", align); } else { img.removeAttribute("align"); }
		WYSIWYG_Core.setAttribute(img, "border", border);
		WYSIWYG_Core.setAttribute(img, "alt", alt);
		WYSIWYG_Core.setAttribute(img, "hspace", hspace);
		WYSIWYG_Core.setAttribute(img, "vspace", vspace);
		WYSIWYG_Core.setAttribute(img, "class", style);
		img.removeAttribute("width");
		img.removeAttribute("height");
		
		// on update exit here
		if(update) { return; }   
		
		// Check if IE or Mozilla (other)
		if (WYSIWYG_Core.isMSIE) {
			range.pasteHTML(img.outerHTML);   
		}
		else {
			this.insertNodeAtSelection(img, n);
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : insertLink()
	  Description : insert a link into WYSIWYG in rich text
	  Usage       : WYSIWYG.insertLink("http://www.google.de", "_blank", "", "", "", "textareaID")
	  Arguments   : href - The link url
	  				target - Target of the link
					style - Stylesheet of the link
					styleClass - Stylesheet class of the link
					name - Name attribute of the link
	                n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	insertLink: function(href, target, style, styleClass, name, n) {
	
		// get editor
		var doc = this.getEditorWindow(n).document;
		// get selection and range
		var sel = this.getSelection(n);
		var range = this.getRange(sel);
		var lin = null;
		
		// get element from selection
		if(WYSIWYG_Core.isMSIE) {
			if(sel.type == "Control" && range.length == 1) {	
				range = this.getTextRange(range(0));
				range.select();
			}
		}

		// find a as parent element
		lin = this.findParentTag("a", range);
				
		// check if parent is found
		var update = (lin == null) ? false : true;
		if(!update) {
			lin = doc.createElement("a");
		}
		
		// set the attributes
		WYSIWYG_Core.setAttribute(lin, "href", href);
		WYSIWYG_Core.setAttribute(lin, "class", styleClass);
		WYSIWYG_Core.setAttribute(lin, "className", styleClass);
		WYSIWYG_Core.setAttribute(lin, "target", target);
		WYSIWYG_Core.setAttribute(lin, "name", name);
		WYSIWYG_Core.setAttribute(lin, "style", style);
		
		// on update exit here
		if(update) { return; }
	
		// Check if IE or Mozilla (other)
		if (WYSIWYG_Core.isMSIE) {	
			range.select();
			lin.innerHTML = range.htmlText;
			range.pasteHTML(lin.outerHTML);   
		} 
		else {			
			var node = range.startContainer;	
			var pos = range.startOffset;
			if(node.nodeType != 3) { node = node.childNodes[pos]; }
			if(node.tagName)
				lin.appendChild(node);
			else
				lin.innerHTML = sel;
			this.insertNodeAtSelection(lin, n);
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : removeFormat()
	  Description : Strip any HTML added by word
	  Usage       : removeFormat(n)
	  Arguments   : n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	removeFormat: function(n) {
		
		if ( !confirm(this.config[n].RemoveFormatConfMessage) ) { return; }
		var doc = this.getEditorWindow(n).document;
		var str = doc.body.innerHTML;
		
		str = str.replace(/<span([^>])*>(&nbsp;)*\s*<\/span>/gi, '');
	    str = str.replace(/<span[^>]*>/gi, '');
	    str = str.replace(/<\/span[^>]*>/gi, '');
	    str = str.replace(/<p([^>])*>(&nbsp;)*\s*<\/p>/gi, '');
	    str = str.replace(/<p[^>]*>/gi, '');
	    str = str.replace(/<\/p[^>]*>/gi, '');
	    str = str.replace(/<h([^>])[0-9]>(&nbsp;)*\s*<\/h>/gi, '');
	    str = str.replace(/<h[^>][0-9]>/gi, '');
	    str = str.replace(/<\/h[^>][0-9]>/gi, ''); 
		str = str.replace (/<B [^>]*>/ig, '<b>');
		
		// var repl_i1 = /<I[^>]*>/ig;
		// str = str.replace (repl_i1, '<i>');
		
		str = str.replace (/<DIV[^>]*>/ig, '');
		str = str.replace (/<\/DIV>/gi, '');
		str = str.replace (/<[\/\w?]+:[^>]*>/ig, '');
		str = str.replace (/(&nbsp;){2,}/ig, '&nbsp;');
		str = str.replace (/<STRONG>/ig, '');
		str = str.replace (/<\/STRONG>/ig, '');
		str = str.replace (/<TT>/ig, '');
		str = str.replace (/<\/TT>/ig, '');
		str = str.replace (/<FONT [^>]*>/ig, '');
		str = str.replace (/<\/FONT>/ig, '');
		str = str.replace (/STYLE=\"[^\"]*\"/ig, '');
		str = str.replace(/<([\w]+) class=([^ |>]*)([^>]*)/gi, '<$1$3');
  	    str = str.replace(/<([\w]+) style="([^"]*)"([^>]*)/gi, '<$1$3'); 
		str = str.replace(/width=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/classname=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/align=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/valign=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/<\\?\??xml[^>]>/gi, '');
	    str = str.replace(/<\/?\w+:[^>]*>/gi, '');
	    str = str.replace(/<st1:.*?>/gi, '');
	    str = str.replace(/o:/gi, ''); 
	    
	    str = str.replace(/<!--([^>])*>(&nbsp;)*\s*<\/-->/gi, '');
   		str = str.replace(/<!--[^>]*>/gi, '');
   		str = str.replace(/<\/--[^>]*>/gi, '');
		
		doc.body.innerHTML = str;
	},
	
	/**
	 * Display an iframe instead of the textarea.
	 * 
	 * @param n - ID of textarea to replace
	 * @param settings - object which holds the settings
	 */
	_display: function(n, settings) {
			
		// Get the textarea element
		var textarea = $(n);
		
		// Validate if textarea exists
		if(textarea == null) {
			alert("No textarea found with the given identifier (ID: " + n + ").");
			return;
		}
		
		// Validate browser compatiblity
		if(!WYSIWYG_Core.isBrowserCompatible()) {
			if(this.config[n].NoValidBrowserMessage != "") { alert(this.config[n].NoValidBrowserMessage); }
			return;
		}
		
	    // Load settings in config array, use the textarea id as identifier
		if(typeof(settings) != "object") {
			this.config[n] = new this.Settings();
		}
		else {
			this.config[n] = settings;
		}
		
		// Hide the textarea 
		textarea.style.display = "none";
		
		// Override the width and height of the editor with the 
		// size given by the style attributes width and height
		if(textarea.style.width) {
			this.config[n].Width = textarea.style.width;
		}
		if(textarea.style.height) {
			this.config[n].Height = textarea.style.height
		} 
			
	    // determine the width + height
		var currentWidth = this.config[n].Width;
		var currentHeight = this.config[n].Height;
	 
		// Calculate the width + height of the editor 
		var ifrmWidth = "100%";
		var	ifrmHeight = "100%";
		if(currentWidth.search(/%/) == -1) {
			ifrmWidth = currentWidth;
			ifrmHeight = currentHeight;
		}
				
		// Create iframe which will be used for rich text editing
		var iframe = '<table cellpadding="0" cellspacing="0" border="0" style="width:' + currentWidth + '; height:' + currentHeight + ';" class="tableTextareaEditor"><tr><td valign="top">\n'
	    + '<iframe frameborder="0" id="wysiwyg' + n + '" class="iframeText" style="width:' + ifrmWidth + ';height:' + ifrmHeight + ';"></iframe>\n'
	    + '</td></tr></table>\n';
	
	    // Insert after the textArea both toolbar one and two
		textarea.insertAdjacentHTML("afterEnd", iframe);
						
		// Pass the textarea's existing text over to the content variable
	    var content = textarea.value;
		var doc = this.getEditorWindow(n).document;
		
		// Replace all \n with <br> 
		if(this.config[n].ReplaceLineBreaks) {
			content = content.replace(/(\r\n)|(\n)/ig, "<br>");
		}
			
		// Write the textarea's content into the iframe
	    doc.open();
	    doc.write(content);
	    doc.close();
	    
	    // Set default style of the editor window
		WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
	},
	
	/**
	 * Replace the given textarea with wysiwyg editor
	 * 
	 * @param n - ID of textarea to replace
	 * @param settings - object which holds the settings
	 */
	_generate: function(n, settings) {
			    
		 // Get the textarea element
		var textarea = $(n);
		// Validate if textarea exists
		if(textarea == null) {
			alert("No textarea found with the given identifier (ID: " + n + ").");
			return;
		}	    
		
		// Validate browser compatiblity
		if(!WYSIWYG_Core.isBrowserCompatible()) {
			if(this.config[n].NoValidBrowserMessage != "") { alert(this.config[n].NoValidBrowserMessage); }
			return;
		}
								
		// Hide the textarea 
		textarea.style.display = 'none'; 
		
		// Override the width and height of the editor with the 
		// size given by the style attributes width and height
		if(textarea.style.width) {
			this.config[n].Width = textarea.style.width;
		}
		if(textarea.style.height) {
			this.config[n].Height = textarea.style.height
		}
			
	    // determine the width + height
		var currentWidth = this.config[n].Width;
		var currentHeight = this.config[n].Height;
	 
		// Calculate the width + height of the editor 
		var toolbarWidth = currentWidth;
		var ifrmWidth = "100%";
		var	ifrmHeight = "100%";
		if(currentWidth.search(/%/) == -1) {
			toolbarWidth = currentWidth.replace(/px/gi, "");
			toolbarWidth = (parseFloat(toolbarWidth) + 2) + "px";
			ifrmWidth = currentWidth;
			ifrmHeight = currentHeight;
		}
		
				
	    // Generate the WYSIWYG Table
	    // This table holds the toolbars and the iframe as the editor
	    var editor = "";
	    editor += '<table border="0" cellpadding="0" cellspacing="0" class="tableTextareaEditor" id="wysiwyg_table_' + n + '" style="width:' + currentWidth  + '; height:' + currentHeight + ';">';
	    editor += '<tr><td>';
	    	  
		// Output all command buttons that belong to toolbar one
		for (var j = 0; j < this.config[n].Toolbar.length;j++) { 
			if(this.config[n].Toolbar[j] && this.config[n].Toolbar[j].length > 0) {
				var toolbar = this.config[n].Toolbar[j];
				
				// Generate WYSIWYG toolbar one
			    editor += '<table border="0" cellpadding="0" cellspacing="0" class="toolbar1" style="width:100%;" id="toolbar' + j + '_' + n + '">';
	    		editor += '<tr><td style="width: 6px;"><img src="' + this.config[n].ImagesDir + 'seperator2.gif" alt="" hspace="3"></td>';
				
				for (var i = 0; i < toolbar.length;i++) { 
				    if (toolbar[i]) {
				    	// Font selection
						if (toolbar[i] == "font"){
							editor += '<td style="width: 90px;"><span id="FontSelect' + n + '"></span></td>';
						}
						// Font size selection
						else if (toolbar[i] == "fontsize"){
							editor += '<td style="width: 60px;"><span id="FontSizes'  + n + '"></span></td>';
						}
						// Button print out
						else {
							// Get the values of the Button from the global ToolbarList object
							var buttonObj = this.ToolbarList[toolbar[i]];
							var buttonID = buttonObj[0];
							var buttonTitle = buttonObj[1];
							var buttonImage = this.config[n].ImagesDir + buttonObj[2];
							var buttonImageRollover  = this.config[n].ImagesDir + buttonObj[3];
							    
							if (toolbar[i] == "seperator") {
								editor += '<td style="width: 12px;" align="center">';
								editor += '<img src="' + buttonImage + '" border=0 unselectable="on" width="2" height="18" hspace="2" unselectable="on">';
								editor += '</td>';
							}
							// View Source button
							else if (toolbar[i] == "viewSource"){
							    editor += '<td style="width: 22px;">';
								editor += '<span id="HTMLMode' + n + '"><img src="'  + buttonImage +  '" border="0" unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + buttonImageRollover + '\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + buttonImage + '\';" onclick="WYSIWYG.formatText(\'' + buttonID + '\',\'' + n + '\');" unselectable="on" width="20" height="20"></span>';
								editor += '<span id="textMode' + n + '"><img src="' + this.config[n].ImagesDir + 'view_text.gif" border="0" unselectable="on" title="viewText" id="ViewText" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + this.config[n].ImagesDir + 'view_text_on.gif\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + this.config[n].ImagesDir + 'view_text.gif\';" onclick="WYSIWYG.formatText(\'ViewText\',\'' + n + '\');" unselectable="on"  width="20" height="20"></span>';
						        editor += '</td>';
					        }
							else {
								editor += '<td style="width: 22px;">';
								editor += '<img src="' + buttonImage + '" border=0 unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + buttonImageRollover + '\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + buttonImage + '\';" onclick="WYSIWYG.formatText(\'' + buttonID + '\',\'' + n + '\');" unselectable="on" width="20" height="20">';
								editor += '</td>';
							}
						}
			  		}
			  	}
			  	editor += '<td>&nbsp;</td></tr></table>';
			}
		}
		
	 	editor += '</td></tr><tr><td valign="top">\n';
		// Create iframe which will be used for rich text editing
		editor += '<iframe frameborder="0" id="wysiwyg' + n + '" class="iframeText" style="width:100%;height:' + currentHeight + ';"></iframe>\n'
	    + '</td></tr>';
	    // Status bar HTML code
	    if(this.config[n].StatusBarEnabled) {
		    editor += '<tr><td class="wysiwyg-statusbar" style="height:10px;" id="wysiwyg_statusbar_' + n + '">&nbsp;</td></tr>';    
		}
	    editor += '</table>\n';
	    
	    // Insert the editor after the textarea	    
	    textarea.insertAdjacentHTML("afterEnd", editor);
	    		
	    // Insert the Font Type and Size drop downs into the toolbar
	    // Hide the dynamic drop down lists for the Font Types and Sizes
    	this.outputFontSelect(n);
	  	this.outputFontSizes(n);
	  	this.hideFonts(n);
		this.hideFontSizes(n);
			
		// Hide the "Text Mode" button
		// Validate if textMode Elements are prensent
		if($("textMode" + n)) {
			$("textMode" + n).style.display = 'none'; 
		}
						
		// Pass the textarea's existing text over to the content variable
	    var content = textarea.value;
		var doc = this.getEditorWindow(n).document;		
		

		// Replace all \n with <br> 
		if(this.config[n].ReplaceLineBreaks) {
			content = content.replace(/\n\r|\n/ig, "<br>");
		}
				
		// Write the textarea's content into the iframe
	    doc.open();
	    doc.write(content);
	    doc.close();
	    		
		// Make the iframe editable in both Mozilla and IE
		// Improve compatiblity for IE + Mozilla
		if (doc.body.contentEditable) {
			doc.body.contentEditable = true;
		}
		else {
			doc.designMode = "on";	
		}
	
		// Set default font style
		WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
	    
	    // Event Handling
	    // Update the textarea with content in WYSIWYG when user submits form
	    for (var idx=0; idx < document.forms.length; idx++) {
	    	WYSIWYG_Core.addEvent(document.forms[idx], "submit", function xxx_aa() { WYSIWYG.updateTextArea(n); });
	    }
	    
	    // close font selection if mouse moves over the editor window
	    WYSIWYG_Core.addEvent(doc, "mouseover", function xxx_bb() { WYSIWYG.hideFonts(n); WYSIWYG.hideFontSizes(n); });
	    
	    // If it's true invert the line break capability of IE
		if(this.config[n].InvertIELineBreaks) {
			WYSIWYG_Core.addEvent(doc, "keypress", function xxx_cc() { WYSIWYG.invertIELineBreakCapability(n); });
		}
					
		// status bar update
		if(this.config[n].StatusBarEnabled) {
			WYSIWYG_Core.addEvent(doc, "mouseup", function xxx_dd() { WYSIWYG.updateStatusBar(n); });
		}
	    	    
    	// custom context menu
		if(this.config[n].ContextMenu) {	
			WYSIWYG_ContextMenu.init(n);		
		}
								
		// init viewTextMode var
	    this.viewTextMode[n] = false;			
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : disable()
	  Description : Disable the given WYSIWYG Editor Box
	  Usage       : WYSIWYG.disable(textareaID)
	  Arguments   : textareaID - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	disable: function(textareaID) {
		// set n to textareaID
		var n = textareaID;
		
		// get the editor window
		var editor = this.getEditorWindow(n);
	
		// Validate if editor exists
		if(editor == null) {
			alert("No editor found with the given identifier (ID: " + n + ").");
			return;
		}
		
		if(editor) {
			// disable design mode or content editable feature
			if(editor.document.body.contentEditable) {
				editor.document.body.contentEditable = false;
			}
			else {
				editor.document.designMode = "Off";		
			}
				
			// change the style of the body
			WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DisabledStyle);
			
			// hide the status bar
			this.hideStatusBar(n);
							
			// hide all toolbars
			this.hideToolbars(n);
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : enable()
	  Description : Enables the given WYSIWYG Editor Box
	  Usage       : WYSIWYG.enable(textareaID)
	  Arguments   : textareaID - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	enable: function(textareaID) {
		// set n to textareaID
		var n = textareaID;
		
		// get the editor window
		var editor = this.getEditorWindow(n);
	
		// Validate if editor exists
		if(editor == null) {
			alert("No editor found with the given identifier (ID: " + n + ").");
			return;
		}
		
		if(editor) {
			// disable design mode or content editable feature
			if(editor.document.body.contentEditable){
				editor.document.body.contentEditable = true;
			}
			else {
				editor.document.designMode = "On";		
			}
				
			// change the style of the body
			WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DefaultStyle);
			
			// hide the status bar
			this.showStatusBar(n);
							
			// hide all toolbars
			this.showToolbars(n);
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : getNodeTree()
	  Description : Returns the node structure of the current selection as array
	  Usage       : WYSIWYG.getNodeTree(n);
	  Arguments   : n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	getNodeTree: function(n) {
		
		var sel = this.getSelection(n);
		var range = this.getRange(sel);	
			
		// get element of range
		var tag = this.getTag(range);
		if(tag == null) { return; }
		// get parent of element
		var node = this.getParent(tag);
		// init the tree as array with the current selected element
		var nodeTree = new Array(tag);
		// get all parent nodes
		var ii = 1;
		
		while(node != null && node.nodeName != "#document") {
			nodeTree[ii] = node;
			node = this.getParent(node);			
			ii++;
		}
		
		return nodeTree;
	},
	
	/**
	 * Removes the current node of the selection
	 *
	 * @param {String} n The editor identifier (the textarea's ID)
	 */
	removeNode: function(n) {
		// get selection and range
		var sel = this.getSelection(n);
		var range = this.getRange(sel);
		// the current tag of range
		var tag = this.getTag(range);
		var parent = tag.parentNode;
		if(tag == null || parent == null) { return; }
		if(tag.nodeName == "HTML" || tag.nodeName == "BODY") { return; }

		// copy child elements of the node to the parent element before remove the node
		//var childNodes = new Array();
		//for(var i=0; i < tag.childNodes.length;i++)
		//	childNodes[i] = tag.childNodes[i];	
		//for(var i=0; i < childNodes.length;i++)
		//	parent.insertBefore(childNodes[i], tag);	
		
		// remove node
		parent.removeChild(tag);
		// validate if parent is a link and the node is only 
		// surrounded by the link, then remove the link too
		if(parent.nodeName == "A" && !parent.hasChildNodes()) {
			if(parent.parentNode) { parent.parentNode.removeChild(parent); }
		}
		// update the status bar
		this.updateStatusBar(n);
	},
	
	/**
	 * Get the selection of the given editor
	 * 
	 * @param n The editor identifier (the textarea's ID)
	 */
	getSelection: function(n) {
		var ifrm = this.getEditorWindow(n);
		var doc = ifrm.document;
		var sel = null;
		if(ifrm.getSelection){
			sel = ifrm.getSelection();
		}
		else if (doc.getSelection) {
			sel = doc.getSelection();
		}
		else if (doc.selection) {
			sel = doc.selection;
		}
		return sel;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : updateStatusBar()
	  Description : Updates the status bar with the current node tree
	  Usage       : WYSIWYG.updateStatusBar(n);
	  Arguments   : n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	updateStatusBar: function(n) {
		
		// get the node structure
		var nodeTree = this.getNodeTree(n);
		if(nodeTree == null) { return; }
		// format the output
		var outputTree = "";
		var max = nodeTree.length - 1;
		for(var i=max;i>=0;i--) {
			if(nodeTree[i].nodeName != "HTML" && nodeTree[i].nodeName != "BODY") {
				outputTree += '<a class="wysiwyg-statusbar" href="javascript:WYSIWYG.selectNode(\'' + n + '\',' + i + ');">' + nodeTree[i].nodeName + '</a>';	
			}
			else {
				outputTree += nodeTree[i].nodeName;
			}
			if(i > 0) { outputTree += " > "; }
		}
			
		// update the status bar 	
		var statusbar = $("wysiwyg_statusbar_" + n);
		if(statusbar){ 
			statusbar.innerHTML = outputTree; 
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : disableDesignMode()
	  Description : Disable the design mode if right mouse button is pressed.
	  				It's needed for custom context menus on mozilla (firefox),
	  				because if design mode is on then you can`t diabled the browser
	  				context menu.
	  Usage       : WYSIWYG.disableDesignMode(e, n);
	  Arguments   : event - browser event (like which button pressed)
	  				n  - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	disableDesignMode: function(event, n) {
		var doc = this.getEditorWindow(n).document;
		if(event.which == 3) {
			doc.designMode = "off";
			return false;	
		}
		else if(event.which != 3 && doc.designMode == "off") {
			doc.designMode = "on";
			return true;
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : formatText() (changed)
	  Description : Format the content within the WYSIWYG Editor
	  Usage       : WYSIWYG.formatText(id, n, selected);
	  Arguments   : cmd - The execCommand (e.g. Bold)
	                n  - The editor identifier that the command affects (the textarea's ID)
	                selected - The selected value when applicable (e.g. Arial)
	\* ---------------------------------------------------------------------- */
	formatText: function(cmd, n, selected) {
			
		// When user clicks toolbar button make sure it always targets its respective WYSIWYG
		this.getEditorWindow(n).focus();
		
		// When in Text Mode these execCommands are disabled
		var formatIDs = new Array("FontSize","FontName","Bold","Italic","Underline","Subscript","Superscript","Strikethrough","Justifyleft","Justifyright","Justifycenter","InsertUnorderedList","InsertOrderedList","Indent","Outdent","ForeColor","BackColor","InsertImage","InsertTable","CreateLink", "Preview", "RemoveFormat");
	  
		// Check if button clicked is in disabled list
		for (var i = 0; i < formatIDs.length; i++) {
			if (formatIDs[i] == cmd) {
				 var disabled_id = 1; 
			}
		}
		
		// rbg to hex convertion implementation dependents on browser
		var toHexColor = WYSIWYG_Core.isMSIE ? WYSIWYG_Core._dec_to_rgb : WYSIWYG_Core.toHexColor;
		
		// popup screen positions
		var popupPosition = {left: parseInt(window.screen.availWidth / 3), top: parseInt(window.screen.availHeight / 3)};		
		
		// Check if in Text Mode and disabled button was clicked
		if (this.viewTextMode[n] == true && disabled_id == 1) {
		  alert("You are in TEXT Mode. This feature has been disabled.");
		  return;
		}
		
		// Check the insert image popup implementation
		var imagePopupFile = this.config[n].PopupsDir + 'insert_image.html';
		var imagePopupWidth = 400;
		var imagePopupHeight = 210;
		if(typeof this.config[n].ImagePopupFile != "undefined" && this.config[n].ImagePopupFile != "") {
			imagePopupFile = this.config[n].ImagePopupFile;
		}
		if(typeof this.config[n].ImagePopupWidth && this.config[n].ImagePopupWidth > 0) {
			imagePopupWidth = this.config[n].ImagePopupWidth;
		}
		if(typeof this.config[n].ImagePopupHeight && this.config[n].ImagePopupHeight > 0) {
			imagePopupHeight = this.config[n].ImagePopupHeight;
		}
		
		// switch which action have to do
		switch(cmd) {
			// Font size
			case "FontSize":
				this.getEditorWindow(n).document.execCommand("FontSize", false, selected);
			break;
			
			// FontName
			case "FontName": 
				this.getEditorWindow(n).document.execCommand("FontName", false, selected);
			break;
			
			// ForeColor and 
			case "ForeColor":
				var rgb = this.getEditorWindow(n).document.queryCommandValue(cmd);
		      	var currentColor = rgb != '' ? toHexColor(this.getEditorWindow(n).document.queryCommandValue(cmd)) : "000000";
			  	window.open(this.config[n].PopupsDir + 'select_color.html?color=' + currentColor + '&command=' + cmd + '&wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,width=210,height=165,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// BackColor
			case "BackColor":
				var currentColor = toHexColor(this.getEditorWindow(n).document.queryCommandValue(cmd));
			  	window.open(this.config[n].PopupsDir + 'select_color.html?color=' + currentColor + '&command=' + cmd + '&wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,width=210,height=165,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// InsertImage
			case "InsertImage": 
				window.open(imagePopupFile + 'insert_image.aspx?wysiwyg=' + n,'popup','location=0,status=0,scrollbars=1,resizable=0,width=800,height=600');
				//window.open(imagePopupFile + '?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=' + imagePopupWidth + ',height=' + imagePopupHeight + ',top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// Remove Image
			case "RemoveImage": 
				this.removeImage(n);
			break;
			
			// Remove Link
			case "RemoveLink": 
				this.removeLink(n);
			break;
			
			// Remove a Node
			case "RemoveNode": 
				this.removeNode(n);
			break;
			
			// Create Link
			case "CreateLink": 
				window.open(this.config[n].PopupsDir + 'insert_hyperlink.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=350,height=160,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// InsertTable
			case "InsertTable": 
				window.open(this.config[n].PopupsDir + 'create_table.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=350,height=200,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// InsertPdf
			case "InsertPDF": 
				window.open(imagePopupFile + 'insert_pdf.aspx?wysiwyg=' + n,'popup','location=0,status=0,scrollbars=0,resizable=0,width=400,height=190');
			break;
			
			// ViewSource
			case "ViewSource": 
				this.viewSource(n);
			break;
			
			// ViewText
			case "ViewText": 
				this.viewText(n);
			break;
			
			// Help
			case "Help":
				window.open(this.config[n].PopupsDir + 'about.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=400,height=350,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// Strip any HTML added by word
			case "RemoveFormat":
				this.removeFormat(n);	
			break;
			
			// Preview thx to Korvo
			case "Preview":
				window.open('preview.html?wysiwyg=' + n,'popup', 'location=0,status=0,scrollbars=1,resizable=1,width=' + this.config[n].PreviewWidth + ',height=' + this.config[n].PreviewHeight + ',top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// Print
			case "Print":
				this.print(n);
			break;
						
			default: 
				WYSIWYG_Core.execCommand(n, cmd);
				
		}
	 		
		// hide node the font + font size selection
		this.hideFonts(n);
		this.hideFontSizes(n);		
	},	
		
	/* ---------------------------------------------------------------------- *\
	  Function    : insertHTML()
	  Description : Insert HTML into WYSIWYG in rich text
	  Usage       : WYSIWYG.insertHTML("<b>hello</b>", "textareaID")
	  Arguments   : html - The HTML being inserted (e.g. <b>hello</b>)
	                n  - The editor identifier that the HTML 
						 will be inserted into (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	insertHTML: function(html, n) {	
		if (WYSIWYG_Core.isMSIE) {	  
			this.getEditorWindow(n).document.selection.createRange().pasteHTML(html);   
		} 
		else {
			var span = this.getEditorWindow(n).document.createElement("span");
			span.innerHTML = html;
			this.insertNodeAtSelection(span, n);		
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : insertNodeAtSelection()
	  Description : insert HTML into WYSIWYG in rich text (mozilla)
	  Usage       : WYSIWYG.insertNodeAtSelection(insertNode, n)
	  Arguments   : insertNode - The HTML being inserted (must be innerHTML inserted within a div element)
	                n          - The editor identifier that the HTML will be inserted into (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	insertNodeAtSelection: function(insertNode, n) {
	
		// get editor document
		var doc = this.getEditorWindow(n).document;
		// get current selection
		var sel = this.getSelection(n);
		
		// get the first range of the selection
		// (there's almost always only one range)
		var range = sel.getRangeAt(0);
		
		// deselect everything
		sel.removeAllRanges();
		
		// remove content of current selection from document
		range.deleteContents();
		
		// get location of current selection
		var container = range.startContainer;
		var pos = range.startOffset;
		
		// make a new range for the new selection
		range = doc.createRange();
		
		if (container.nodeType==3 && insertNode.nodeType==3) {					
			// if we insert text in a textnode, do optimized insertion
			container.insertData(pos, insertNode.data);
			// put cursor after inserted text
			range.setEnd(container, pos+insertNode.length);
			range.setStart(container, pos+insertNode.length);		
		} 	
		else {
		
			var afterNode;	
			var beforeNode;
			if (container.nodeType==3) {
				// when inserting into a textnode
				// we create 2 new textnodes
				// and put the insertNode in between
				var textNode = container;
				container = textNode.parentNode;
				var text = textNode.nodeValue;
				
				// text before the split
				var textBefore = text.substr(0,pos);
				// text after the split
				var textAfter = text.substr(pos);
				
				beforeNode = document.createTextNode(textBefore);
				afterNode = document.createTextNode(textAfter);
				
				// insert the 3 new nodes before the old one
				container.insertBefore(afterNode, textNode);
				container.insertBefore(insertNode, afterNode);
				container.insertBefore(beforeNode, insertNode);
				
				// remove the old node
				container.removeChild(textNode);
			} 
			else {
				// else simply insert the node
				afterNode = container.childNodes[pos];
				container.insertBefore(insertNode, afterNode);
			}
			
			range.setEnd(afterNode, 0);
			range.setStart(afterNode, 0);
		}
		
		sel.addRange(range);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : print()
	  Description : Print out the content of the WYSIWYG editor area
	  Usage       : WYSIWYG.print(n)
	  Arguments   : n - The editor identifier (textarea ID)
	\* ---------------------------------------------------------------------- */
	print: function(n) {
		if(document.all && navigator.appVersion.substring(22,23)==4) {
			var doc = this.getEditorWindow(n).document;
			doc.focus();
			var OLECMDID_PRINT = 6;
			var OLECMDEXECOPT_DONTPROMPTUSER = 2;
			var OLECMDEXECOPT_PROMPTUSER = 1;
			var WebBrowser = '<object id="WebBrowser1" width="0" height="0" classid="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2"></object>';
			doc.body.insertAdjacentHTML('beforeEnd',WebBrowser);
			WebBrowser.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER);
			WebBrowser.outerHTML = '';
		} else {
			this.getEditorWindow(n).print();
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : outputFontSelect()
	  Description : creates the Font Select drop down and inserts it into the toolbar
	  Usage       : WYSIWYG.outputFontSelect(n)
	  Arguments   : n - The editor identifier that the Font Select will update
		                when making font changes (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	outputFontSelect: function(n) {	
		 
		var fontDiv = $('FontSelect' + n);
		if(fontDiv == null) { return; }
		
		var fonts = this.config[n].Fonts;
		var FontSelectObj = this.ToolbarList['selectfont'];
		var FontSelect = this.config[n].ImagesDir  + FontSelectObj[2];
		var FontSelectOn  = this.config[n].ImagesDir + FontSelectObj[3];
		fonts.sort();
		
		var FontSelectDropDown = new Array;
		FontSelectDropDown[n] = '<table border="0" cellpadding="0" cellspacing="0"><tr><td onMouseOver="$(\'selectFont' + n + '\').src=\'' + FontSelectOn + '\';" onMouseOut="$(\'selectFont' + n + '\').src=\'' + FontSelect + '\';"><img src="' + FontSelect + '" id="selectFont' + n + '" width="85" height="20" onClick="WYSIWYG.showFonts(\'' + n + '\');" unselectable="on" border="0"><br>';
		FontSelectDropDown[n] += '<span id="Fonts' + n + '" class="dropdown" style="width: 145px;">';
	
		for (var i = 0; i < fonts.length;i++) {
		  	if (fonts[i]) {
		  		FontSelectDropDown[n] += '<button type="button" onClick="WYSIWYG.formatText(\'FontName\',\'' + n + '\',\'' + fonts[i] + '\')\;" onMouseOver="this.className=\'mouseOver\'" onMouseOut="this.className=\'mouseOut\'" class="mouseOut" style="width: 120px;"><table cellpadding="0" cellspacing="0" border="0"><tr><td align="left" style="font-family:' + fonts[i] + '; font-size: 12px;">' + fonts[i] + '</td></tr></table></button><br>';	
		  	}
	  	}
		
		FontSelectDropDown[n] += '</span></td></tr></table>';
		fontDiv.insertAdjacentHTML("afterBegin", FontSelectDropDown[n]);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : outputFontSizes()
	  Description : creates the Font Sizes drop down and inserts it into the toolbar
	  Usage       : WYSIWYG.outputFontSelect(n)
	  Arguments   : n   - The editor identifier that the Font Sizes will update
		                    when making font changes (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	outputFontSizes: function(n) {	
	
		var fontSizeDiv = $('FontSizes' + n);
		if(fontSizeDiv == null) { return; }
		
		var fontSize = this.config[n].Fontsizes;
		var FontSizeObj = this.ToolbarList['selectsize'];
		var FontSize = this.config[n].ImagesDir + FontSizeObj[2];
		var FontSizeOn = this.config[n].ImagesDir + FontSizeObj[3];
	
		fontSize.sort();
		var FontSizesDropDown = new Array;
		FontSizesDropDown[n] = '<table border="0" cellpadding="0" cellspacing="0"><tr><td onMouseOver="$(\'selectSize' + n + '\').src=\'' + FontSizeOn + '\';" onMouseOut="$(\'selectSize' + n + '\').src=\'' + FontSize + '\';"><img src="' + FontSize + '" id="selectSize' + n + '" width="49" height="20" onClick="WYSIWYG.showFontSizes(\'' + n + '\');" unselectable="on" border="0"><br>';
	  	FontSizesDropDown[n] += '<span id="Sizes' + n + '" class="dropdown" style="width: 170px;">';
	
		for (var i = 0; i < fontSize.length;i++) {
		  if (fontSize[i]) {
	      	FontSizesDropDown[n] += '<button type="button" onClick="WYSIWYG.formatText(\'FontSize\',\'' + n + '\',\'' + fontSize[i] + '\')\;" onMouseOver="this.className=\'mouseOver\'" onMouseOut="this.className=\'mouseOut\'" class="mouseOut" style="width: 145px;"><table cellpadding="0" cellspacing="0" border="0"><tr><td align="left" style="font-family: arial, verdana, helvetica;"><font size="' + fontSize[i] + '">size ' + fontSize[i] + '</font></td></tr></table></button><br>';	
	      }
	  	}
		
		FontSizesDropDown[n] += '</span></td></tr></table>';
		fontSizeDiv.insertAdjacentHTML("afterBegin", FontSizesDropDown[n]);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : hideFonts()
	  Description : Hides the list of font names in the font select drop down
	  Usage       : WYSIWYG.hideFonts(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	hideFonts: function(n) {
		if($('Fonts' + n)) { $('Fonts' + n).style.display = 'none'; }
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : hideFontSizes()
	  Description : Hides the list of font sizes in the font sizes drop down
	  Usage       : WYSIWYG.hideFontSizes(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	hideFontSizes: function(n) {
		if($('Sizes' + n)) { $('Sizes' + n).style.display = 'none'; }
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : showFonts()
	  Description : Shows the list of font names in the font select drop down
	  Usage       : WYSIWYG.showFonts(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	showFonts: function(n) { 
		if($('Fonts' + n) == null) { return; }
		if ($('Fonts' + n).style.display == 'block') {
			$('Fonts' + n).style.display = 'none';
		}
		else {
			$('Fonts' + n).style.display = 'block'; 
			$('Fonts' + n).style.position = 'absolute';		
		}
		
		// hide font size selection
		this.hideFontSizes(n);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : showFontSizes()
	  Description : Shows the list of font sizes in the font sizes drop down
	  Usage       : WYSIWYG.showFonts(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	showFontSizes: function(n) { 
		if($('Sizes' + n) == null) { return; }
		if ($('Sizes' + n).style.display == 'block') {
			$('Sizes' + n).style.display = 'none';
		}
		else {
			$('Sizes' + n).style.display = 'block'; 
			$('Sizes' + n).style.position = 'absolute';		
		}
		
		// hide font size selection
		this.hideFonts(n);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : viewSource()
	  Description : Shows the HTML source code generated by the WYSIWYG editor
	  Usage       : WYSIWYG.showFonts(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	viewSource: function(n) {
		
		// document
		var doc = this.getEditorWindow(n).document;
			
		// View Source for IE 	 
		if (WYSIWYG_Core.isMSIE) {
			var iHTML = doc.body.innerHTML;
			// strip off the absolute urls
			iHTML = this.stripURLPath(n, iHTML);
			// replace all decimal color strings with hex decimal color strings
			iHTML = WYSIWYG_Core.replaceRGBWithHexColor(iHTML);
			doc.body.innerText = iHTML;
		}
	  	// View Source for Mozilla/Netscape
	  	else {
	  		// replace all decimal color strings with hex decimal color strings
			var html = WYSIWYG_Core.replaceRGBWithHexColor(doc.body.innerHTML);
	    	html = document.createTextNode(html);
	    	doc.body.innerHTML = "";
	    	doc.body.appendChild(html);
	  	}
	  
		// Hide the HTML Mode button and show the Text Mode button
		// Validate if Elements are present
		if($('HTMLMode' + n)) {
		    $('HTMLMode' + n).style.display = 'none'; 
		}
	    if($('textMode' + n)) {
		    $('textMode' + n).style.display = 'block';
		}
		
		// set the font values for displaying HTML source
		doc.body.style.fontSize = "12px";
		doc.body.style.fontFamily = "Courier New"; 
		
	  	this.viewTextMode[n] = true;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : viewSource()
	  Description : Shows the HTML source code generated by the WYSIWYG editor
	  Usage       : WYSIWYG.showFonts(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	viewText: function(n) { 
		
		// get document
		var doc = this.getEditorWindow(n).document;
		
		// View Text for IE 	  	 
		if (WYSIWYG_Core.isMSIE) {
	    	var iText = doc.body.innerText;
	    	// strip off the absolute urls
			iText = this.stripURLPath(n, iText);
			// replace all decimal color strings with hex decimal color strings
			iText = WYSIWYG_Core.replaceRGBWithHexColor(iText);
	    	doc.body.innerHTML = iText;
		}
	  
		// View Text for Mozilla/Netscape
	  	else {
	    	var html = doc.body.ownerDocument.createRange();
	    	html.selectNodeContents(doc.body);
	    	// replace all decimal color strings with hex decimal color strings
			html = WYSIWYG_Core.replaceRGBWithHexColor(html.toString());
	    	doc.body.innerHTML = html;
		}
					  
		// Hide the Text Mode button and show the HTML Mode button
		// Validate if Elements are present
		if($('textMode' + n)) {
			$('textMode' + n).style.display = 'none'; 
		}
		if($('HTMLMode' + n)) {
			$('HTMLMode' + n).style.display = 'block';
		}
		
		// reset the font values (changed)
		WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
		
		this.viewTextMode[n] = false;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : getDocumentPath()
	  Description : Get the path of the given document
	  Usage       : WYSIWYG.getDocumentPath(doc)
	  Arguments   : doc  - Document of which you get the the path
	\* ---------------------------------------------------------------------- */
	getDocumentPathOfUrl: function(url) {
		var path = null;
		
		// if local file system, convert local url into web url
		url = url.replace(/file:\/\//gi, "file:///");
		url = url.replace(/\\/gi, "\/");
		var pos = url.lastIndexOf("/");
		if(pos != -1) {
			path = url.substring(0, pos + 1);
		}
		return path;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : getDocumentUrl()
	  Description : Get the documents url, convert local urls to web urls
	  Usage       : WYSIWYG.getDocumentUrl(doc)
	  Arguments   : doc  - Document of which you get the the path
	\* ---------------------------------------------------------------------- */
	getDocumentUrl: function(doc) {
		// if local file system, convert local url into web url
		var url = doc.URL;
		url = url.replace(/file:\/\//gi, "file:///");
		url = url.replace(/\\/gi, "\/");
		return url;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : stripURLPath()
	  Description : Strips off the defined image and the anchor urls of the given content.
	  				It also can strip the document URL automatically if you define auto.
	  Usage       : WYSIWYG.stripURLPath(content)
	  Arguments   : content  - Content on which the stripping applies
	\* ---------------------------------------------------------------------- */
	stripURLPath: function(n, content, exact) {
	
		// parameter exact is optional
		if(typeof exact == "undefined") {
			exact = true;
		}
	
		var stripImgageUrl = null;
		var stripAnchorUrl = null;
		
		// add url to strip of anchors to array
		if(this.config[n].AnchorPathToStrip == "auto") {
			stripAnchorUrl = this.getDocumentUrl(document);
		}
		else if(this.config[n].AnchorPathToStrip != "") {
			stripAnchorUrl = this.config[n].AnchorPathToStrip;
		}
		
		// add strip url of images to array
		if(this.config[n].ImagePathToStrip == "auto") {
			stripImgageUrl = this.getDocumentUrl(document);
		}
		else if(this.config[n].ImagePathToStrip != "") {
			stripImgageUrl = this.config[n].ImagePathToStrip;
		}
		
		var url;
		var regex;
		var result;
		// strip url of image path
		if(stripImgageUrl) {
			// escape reserved characters to be a valid regex	
			url = WYSIWYG_Core.stringToRegex(this.getDocumentPathOfUrl(stripImgageUrl));	
			
			// exact replacing of url. regex: src="<url>"
			if(exact) {
				regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
				content = content.replace(regex, "$1$3");	
			}
			// not exect replacing of url. regex: <url>
			else {
				regex = eval("/(" + url + ")(.+)/gi");
				content = content.replace(regex, "$2");	
			}
			
			// strip absolute urls without a heading slash ("images/print.gif")	
			result = this.getDocumentPathOfUrl(stripImgageUrl).match(/.+[\/]{2,3}[^\/]*/,"");
			if(result) {
				url = WYSIWYG_Core.stringToRegex(result[0]);
				
				// exact replacing of url. regex: src="<url>"
				if(exact) {
					regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
					content = content.replace(regex, "$1$3");
				}
				// not exect replacing of url. regex: <url>
				else {
					regex = eval("/(" + url + ")(.+)/gi");
					content = content.replace(regex, "$2");	
				}
			}	
		}
		
		// strip url of image path
		if(stripAnchorUrl) {						
			// escape reserved characters to be a valid regex		
			url = WYSIWYG_Core.stringToRegex(this.getDocumentPathOfUrl(stripAnchorUrl));
			
			// strip absolute urls with a heading slash ("/product/index.html")
			// exact replacing of url. regex: src="<url>"
			if(exact) {
				regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
				content = content.replace(regex, "$1$3");	
			}
			// not exect replacing of url. regex: <url>
			else {
				regex = eval("/(" + url + ")(.+)/gi");
				content = content.replace(regex, "$2");	
			}
			
			// strip absolute urls without a heading slash ("product/index.html")	
			result = this.getDocumentPathOfUrl(stripAnchorUrl).match(/.+[\/]{2,3}[^\/]*/,"");
			if(result) {
				url = WYSIWYG_Core.stringToRegex(result[0]);
				// exact replacing of url. regex: src="<url>"
				if(exact) {
					regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
					content = content.replace(regex, "$1$3");	
				}
				// not exect replacing of url. regex: <url>
				else {
					regex = eval("/(" + url + ")(.+)/gi");
					content = content.replace(regex, "$2");	
				}
				
			}
			
			// stip off anchor links with #name			
			url = WYSIWYG_Core.stringToRegex(stripAnchorUrl);
			// exact replacing of url. regex: src="<url>"
			if(exact) {
				regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
				content = content.replace(regex, "$1$3");
			}
			// not exect replacing of url. regex: <url>
			else {
				regex = eval("/(" + url + ")(.+)/gi");
				content = content.replace(regex, "$2");	
			}
			
			
			// stip off anchor links with #name (only for local system)
			url = this.getDocumentUrl(document);
			var pos = url.lastIndexOf("/");
			if(pos != -1) {
				url = url.substring(pos + 1, url.length);
				url = WYSIWYG_Core.stringToRegex(url);
				// exact replacing of url. regex: src="<url>"
				if(exact) {
					regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
					content = content.replace(regex, "$1$3");
				}
				// not exect replacing of url. regex: <url>
				else {
					regex = eval("/(" + url + ")(.+)/gi");
					content = content.replace(regex, "$2");	
				}
			}
		}
		
		return content;
	},	
		
	/* ---------------------------------------------------------------------- *\
	  Function    : updateTextArea()
	  Description : Updates the text area value with the HTML source of the WYSIWYG
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	updateTextArea: function(n) {	
		// on update switch editor back to html mode
		if(this.viewTextMode[n]) { this.viewText(n); }
		// get inner HTML
		var content = this.getEditorWindow(n).document.body.innerHTML;
		// strip off defined URLs on IE
		content = this.stripURLPath(n, content);
		// replace all decimal color strings with hex color strings
		content = WYSIWYG_Core.replaceRGBWithHexColor(content);
		// remove line breaks before content will be updated
		if(this.config[n].ReplaceLineBreaks) { content = content.replace(/(\r\n)|(\n)/ig, ""); }
		// set content back in textarea
		$(n).value = content;
	},
		
	/* ---------------------------------------------------------------------- *\
	  Function    : hideToolbars()
	  Description : Hide all toolbars
	  Usage       : WYSIWYG.hideToolbars(n)
	  Arguments   : n - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	hideToolbars: function(n) {
		for(var i=0;i<this.config[n].Toolbar.length;i++) {
			var toolbar = $("toolbar" + i + "_" + n);
			if(toolbar) { toolbar.style.display = "none"; }
		}	
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : showToolbars()
	  Description : Display all toolbars
	  Usage       : WYSIWYG.showToolbars(n)
	  Arguments   : n - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	showToolbars: function(n) {
		for(var i=0;i<this.config[n].Toolbar.length;i++) {
			var toolbar = $("toolbar" + i + "_" + n);
			if(toolbar) { toolbar.style.display = ""; }
		}	
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : hideStatusBar()
	  Description : Hide the status bar
	  Usage       : WYSIWYG.hideStatusBar(n)
	  Arguments   : n - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	hideStatusBar: function(n) {
		var statusbar = $('wysiwyg_statusbar_' + n);
		if(statusbar) {	statusbar.style.display = "none"; }
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : showStatusBar()
	  Description : Display the status bar
	  Usage       : WYSIWYG.showStatusBar(n)
	  Arguments   : n - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	showStatusBar: function(n) {
		var statusbar = $('wysiwyg_statusbar_' + n);
		if(statusbar) { statusbar.style.display = ""; }
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : findParentTag()
	  Description : Get the given parent tag of a range
	  Usage       : WYSIWYG.findParentTag(parentTagName, range)
	  Arguments   : parentTagName - Parent tag to find
	  				range - Range 
	\* ---------------------------------------------------------------------- */
	findParentTag: function(parentTagName, range){
		parentTagName = parentTagName.toUpperCase();
		var rangeWorking;
		var elmWorking = null;
		try {
			if(!WYSIWYG_Core.isMSIE) {
				var node = range.startContainer;	
				var pos = range.startOffset;
				if(node.nodeType != 3) { node = node.childNodes[pos]; }
				elmWorking = node;
				while (elmWorking.tagName != "HTML") {
			  		if (elmWorking.tagName == parentTagName){
			  			return elmWorking;
			  		} 
			  		elmWorking = elmWorking.parentNode;
			 	}
			 	return null;
			}
			else {
				if(range.length > 0) {
					elmWorking = range.item(0);
				}
				else {
					elmWorking = range.parentElement();	
				}
				while (elmWorking.tagName != "HTML") {
			  		if (elmWorking.tagName == parentTagName){
			   			return elmWorking;
			  		} else {
			   			elmWorking = elmWorking.parentElement;
			  		}
			 	}
				rangeWorking = range.duplicate();
				rangeWorking.collapse(true);
				rangeWorking.moveEnd("character", 1);
				if (rangeWorking.text.length>0) {
					while (rangeWorking.compareEndPoints("EndToEnd", range) < 0){
			  			rangeWorking.move("Character");
			  			if (null != this.findParentTag(parentTagName, rangeWorking)){
			   				return this.findParentTag(parentTagName, rangeWorking);
			  			}
			 		}
			 	}
			 	return null;
			}
		}
		catch(e) {
			return null;
		}
	},
		
	/* ---------------------------------------------------------------------- *\
	  Function    : getTag()
	  Description : Get the acutally tag of the given range
	  Usage       : WYSIWYG.getTag(range)
	  Arguments   : range - Range
	\* ---------------------------------------------------------------------- */
	getTag: function(range) {
		try {
		    if(!WYSIWYG_Core.isMSIE) {
				var node = range.startContainer;	
				var pos = range.startOffset;
				if(node.nodeType != 3) { node = node.childNodes[pos]; }
				
				if(node.nodeName && node.nodeName.search(/#/) != -1) {
					return node.parentNode;
				}
				return node;
			}
			else {
				if(range.length > 0) {
					return range.item(0);
				}
				else if(range.parentElement()) {
					return range.parentElement();
				}
			}
			return null;
		}
		catch(e) {
			return null;
		}
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : getParent()
	  Description : Get the parent node of an node
	  Usage       : WYSIWYG.getParent(node)
	  Arguments   : element - Element which parent will be returned
	\* ---------------------------------------------------------------------- */
	getParent: function(element) {
		if(element.parentNode) {
			return element.parentNode;
		}
		return null;
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : getTextRange()
	  Description : Get the text range object of the given element
	  Usage       : WYSIWYG.getTextRange(element)
	  Arguments   : element - An element of which you get the text range object
	\* ---------------------------------------------------------------------- */
	getTextRange: function(element){
		var range = element.parentTextEdit.createTextRange();
		range.moveToElementText(element);
		return range;
	},
	

	
	/* ---------------------------------------------------------------------- *\
	  Function    : invertIELineBreakCapability()
	  Description : Inverts the line break capability of IE (Thx to richyrich)
	  				Normal: ENTER = <p> , SHIFT + ENTER = <br>
	  				Inverted: ENTER = <br>, SHIFT + ENTER = <p>
	  Usage       : WYSIWYG.invertIELineBreakCapability(n)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	\* ---------------------------------------------------------------------- */
	invertIELineBreakCapability: function(n) {
	
		var editor = this.getEditorWindow(n);
		var sel;
		// validate if the press key is the carriage return key
		if (editor.event.keyCode==13) {
	    	if (!editor.event.shiftKey) {
				sel = this.getRange(this.getSelection(n));
	            sel.pasteHTML("<br>");
	            editor.event.cancelBubble = true;
	            editor.event.returnValue = false;
	            sel.select();
	            sel.moveEnd("character", 1);
	            sel.moveStart("character", 1);
	            sel.collapse(false);
	            return false;
			}
	        else {
	            sel = this.getRange(this.getSelection(n));
	            sel.pasteHTML("<p>");
	            editor.event.cancelBubble = true;
	            editor.event.returnValue = false;
	            sel.select();
	            sel.moveEnd("character", 1);
	            sel.moveStart("character", 1);
	            sel.collapse(false);
	            return false;
	    	}
		}  
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : selectNode()
	  Description : Select a node within the current editor
	  Usage       : WYSIWYG.selectNode(n, level)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	  				level - identifies the level of the element which will be selected
	\* ---------------------------------------------------------------------- */
	selectNode: function(n, level) {
		
		var sel = this.getSelection(n);
		var range = this.getRange(sel);
		var parentnode = this.getTag(range);
		var i = 0;
		
		for (var node=parentnode; (node && (node.nodeType == 1)); node=node.parentNode) {
			if (i == level) {
				this.nodeSelection(n, node);
			}
			i++;
		}
		
		this.updateStatusBar(n);
	},
	
	/* ---------------------------------------------------------------------- *\
	  Function    : nodeSelection()
	  Description : Do the node selection
	  Usage       : WYSIWYG.nodeSelection(n, node)
	  Arguments   : n   - The editor identifier (the textarea's ID)
	  				node - The node which will be selected
	\* ---------------------------------------------------------------------- */
	nodeSelection: function(n, node) {
		
		var doc = this.getEditorWindow(n).document;
		var sel = this.getSelection(n);
		var range = this.getRange(sel);
		
		if(!WYSIWYG_Core.isMSIE) {
			if (node.nodeName == "BODY") {
				range.selectNodeContents(node);
			} else {
				range.selectNode(node);
			}

			/*
			if (endNode) {
				try {
					range.setStart(node, startOffset);
					range.setEnd(endNode, endOffset);
				} catch(e) {
				}
			}
			*/
			
			if (sel) { sel.removeAllRanges(); }
			if (sel) { sel.addRange(range);	 }
		}
		else {
			// MSIE may not select everything when BODY is selected - 
			// start may be set to first text node instead of first non-text node - 
			// no known workaround
			if ((node.nodeName == "TABLE") || (node.nodeName == "IMG") || (node.nodeName == "INPUT") || (node.nodeName == "SELECT") || (node.nodeName == "TEXTAREA")) {
				try {
					range = doc.body.createControlRange();
					range.addElement(node);
					range.select();
				} 
				catch(e) { }
			} 
			else {
				range = doc.body.createTextRange();
				if (range) {
					range.collapse();
					if (range.moveToElementText) {
						try {
							range.moveToElementText(node);
							range.select();
						} catch(e) {
							try {
								range = doc.body.createTextRange();
								range.moveToElementText(node);
								range.select();
							} 
							catch(e) {}
						}
					} else {
						try {
							range = doc.body.createTextRange();
							range.moveToElementText(node);
							range.select();
						} 
						catch(e) {}
					}
				}
			}
		}
	}
}

/********************************************************************
 * openWYSIWYG core functions Copyright (c) 2006 openWebWare.com
 * Contact us at devs@openwebware.com
 * This copyright notice MUST stay intact for use.
 *
 * $Id: wysiwyg.js,v 1.10 2006/12/25 09:17:07 xhaggi Exp $
 ********************************************************************/
var WYSIWYG_Core = {

	/**
	 * Holds true if browser is MSIE, otherwise false
	 */
	isMSIE: navigator.appName == "Microsoft Internet Explorer" ? true : false,

	/**
	 * Holds true if browser is Firefox (Mozilla)
	 */
	isFF: !document.all && document.getElementById && !this.isOpera,
	
	/**
	 * Holds true if browser is Opera, otherwise false
	 */
	isOpera: navigator.appName == "Opera" ? true : false,
	
	/**
	 * Trims whitespaces of the given string
	 *
	 * @param str String
	 * @return Trimmed string
	 */
	trim: function(str) {
		return str.replace(/^\s*|\s*$/g,"");
	},
	
	/**
	 * Determine if the given parameter is defined
	 * 
	 * @param p Parameter
	 * @return true/false dependents on definition of the parameter 
	 */
	defined: function(p) {
		return typeof p == "undefined" ? false : true;	
	},
	
	/**
	 * Determine if the browser version is compatible
	 *
	 * @return true/false depending on compatiblity of the browser
	 */
	isBrowserCompatible: function() {
		// Validate browser and compatiblity
		if ((navigator.userAgent.indexOf('Safari') != -1 ) || !document.getElementById || !document.designMode){   
			//no designMode (Safari lies)
	   		return false;
		} 
		return true;
	},
	
	/**
	 * Set the style attribute of the given element.
	 * Private method to solve the IE bug while setting the style attribute.
	 *
	 * @param element The element on which the style attribute will affect
	 * @param style Stylesheet which will be set
	 */
	_setStyleAttribute: function(element, style) {
		var styles = style.split(";");
		var pos;
		for(var i=0;i<styles.length;i++) {
			var attributes = styles[i].split(":");
			if(attributes.length == 2) {
				try {
					var attr = WYSIWYG_Core.trim(attributes[0]);
					while((pos = attr.search(/-/)) != -1) {
						var strBefore = attr.substring(0, pos);
						var strToUpperCase = attr.substring(pos + 1, pos + 2);
						var strAfter = attr.substring(pos + 2, attr.length);
						attr = strBefore + strToUpperCase.toUpperCase() + strAfter;
					}
					var value = WYSIWYG_Core.trim(attributes[1]).toLowerCase();
					element.style[attr] = value;
				}
				catch (e) {
					//alert(e);
				}
			}
		}
	},
	
	/**
	 * Set an attribute's value on the given node element.
	 *
	 * @param node Node element
	 * @param attr Attribute which is set
	 * @param value Value of the attribute
	 */
	setAttribute: function(node, attr, value) {
		if(value == "") {return;}
		if(attr.toLowerCase() == "style") {
			this._setStyleAttribute(node, value);
		}
		else {
			node.setAttribute(attr, value);
		}
	},
	
	/**
	 * Cancel the given event.
	 *
	 * @param e Event which will be canceled
	 */
	cancelEvent: function(e) {
		if (!e) return false;
		if (this.isMSIE) {
			e.returnValue = false;
			e.cancelBubble = true;
		} else {
			e.preventDefault();
			e.stopPropagation && e.stopPropagation();
		}
		return false;	
	},
	
	/**
	 * Converts a RGB color string to hex color string.
	 *
	 * @param color RGB color string
	 * @param Hex color string
	 */
	toHexColor: function(color) {
		color = color.replace(/^rgb/g,'');
		color = color.replace(/\(/g,'');
		color = color.replace(/\)/g,'');
		color = color.replace(/ /g,'');
		color = color.split(',');
		var r = parseFloat(color[0]).toString(16).toUpperCase();
		var g = parseFloat(color[1]).toString(16).toUpperCase();
		var b = parseFloat(color[2]).toString(16).toUpperCase();
		if (r.length<2) { r='0'+r; }
		if (g.length<2) { g='0'+g; }
		if (b.length<2) { b='0'+b; }
		return r + g + b;
	},
	
	/**
	 * Converts a decimal color to hex color string.
	 *
	 * @param Decimal color
	 * @param Hex color string
	 */
	_dec_to_rgb: function(value) {
		var hex_string = "";
		for (var hexpair = 0; hexpair < 3; hexpair++) {
			var myByte = value & 0xFF;            // get low byte
			value >>= 8;                          // drop low byte
			var nybble2 = myByte & 0x0F;          // get low nybble (4 bits)
			var nybble1 = (myByte >> 4) & 0x0F;   // get high nybble
			hex_string += nybble1.toString(16);   // convert nybble to hex
			hex_string += nybble2.toString(16);   // convert nybble to hex
		}
		return hex_string.toUpperCase();
	},
	
	/**
	 * Replace RGB color strings with hex color strings within a string.
	 * 
	 * @param {String} str RGB String
	 * @param {String} Hex color string
	 */
	replaceRGBWithHexColor: function(str) {
		// find all decimal color strings
		var matcher = str.match(/rgb\([0-9 ]+,[0-9 ]+,[0-9 ]+\)/gi);
		if(matcher) {
			for(var j=0; j<matcher.length;j++) {
				var regex = eval("/" + WYSIWYG_Core.stringToRegex(matcher[j]) + "/gi");
				// replace the decimal color strings with hex color strings
				str = str.replace(regex, "#" + this.toHexColor(matcher[j]));
			}
		}
		return str;
	},
	
	/**
	 * Execute the given command on the given editor
	 * 
	 * @param n The editor's identifier
	 * @param cmd Command which is execute
	 */
	execCommand: function(n, cmd, value) {
		if(typeof(value) == "undefined") value = null;
		
		// firefox BackColor problem fixed
		if(cmd == 'BackColor' && WYSIWYG_Core.isFF) cmd = 'HiliteColor';
		
		// firefox cut, paste and copy
		if(WYSIWYG_Core.isFF && (cmd == "Cut" || cmd == "Paste" || cmd == "Copy")) {
			try {
				WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
			}
			catch(e) {
				if(confirm("Copy/Cut/Paste is not available in Mozilla and Firefox\nDo you want more information about this issue?")) {
					window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html');
				}
			}
		}
		
		else {
			WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
		}
	},
	
	/**
	 * Parse a given string to a valid regular expression
	 * 
	 * @param {String} string String to be parsed
	 * @return {RegEx} Valid regular expression
	 */
	stringToRegex: function(string) {
		
		string = string.replace(/\//gi, "\\/");
		string = string.replace(/\(/gi, "\\(");
		string = string.replace(/\)/gi, "\\)");
		string = string.replace(/\[/gi, "\\[");
		string = string.replace(/\]/gi, "\\]");
		string = string.replace(/\+/gi, "\\+");
		string = string.replace(/\$/gi, "\\$");
		string = string.replace(/\*/gi, "\\*");
		string = string.replace(/\?/gi, "\\?");
		string = string.replace(/\^/gi, "\\^");
		string = string.replace(/\\b/gi, "\\\\b");
		string = string.replace(/\\B/gi, "\\\\B");
		string = string.replace(/\\d/gi, "\\\\d");
		string = string.replace(/\\B/gi, "\\\\B");
		string = string.replace(/\\D/gi, "\\\\D");
		string = string.replace(/\\f/gi, "\\\\f");
		string = string.replace(/\\n/gi, "\\\\n");
		string = string.replace(/\\r/gi, "\\\\r");
		string = string.replace(/\\t/gi, "\\\\t");
		string = string.replace(/\\v/gi, "\\\\v");
		string = string.replace(/\\s/gi, "\\\\s");
		string = string.replace(/\\S/gi, "\\\\S");
		string = string.replace(/\\w/gi, "\\\\w");
		string = string.replace(/\\W/gi, "\\\\W");
		
		return string;			
	},
	
	/**
	 * Add an event listener
	 *
	 * @param obj Object on which the event will be attached
	 * @param ev Kind of event
	 * @param fu Function which is execute on the event
	 */
	addEvent: function(obj, ev, fu) {
		if (obj.attachEvent)
			obj.attachEvent("on" + ev, fu);
		else
			obj.addEventListener(ev, fu, false);
	},
	
	/**
	 * Remove an event listener
	 *
	 * @param obj Object on which the event will be attached
	 * @param ev Kind of event
	 * @param fu Function which is execute on the event
	 */
	removeEvent:  function(obj, ev, fu) {
		if (obj.attachEvent)
			obj.detachEvent("on" + ev, fu);
		else
			obj.removeEventListener(ev, fu, false);
	},
	
	/**
	 * Includes a javascript file
	 *
	 * @param file Javascript file path and name
	 */
	includeJS: function(file) {
		var script = document.createElement("script");
		this.setAttribute(script, "type", "text/javascript");
		this.setAttribute(script, "src", file);
		var heads = document.getElementsByTagName("head");
		for(var i=0;i<heads.length;i++) {
			heads[i].appendChild(script);		
		}
	},
	
	/**
	 * Includes a stylesheet file
	 *
	 * @param file Stylesheet file path and name
	 */
	includeCSS: function(path) {
		var link = document.createElement("link");
		this.setAttribute(link, "rel", "stylesheet");
		this.setAttribute(link, "type", "text/css");
		this.setAttribute(link, "href", path);
		var heads = document.getElementsByTagName("head");
		for(var i=0;i<heads.length;i++) {
			heads[i].appendChild(link);		
		}
	},
	
	/**
	 * Get the screen position of the given element.
	 * 
	 * @param {HTMLObject} elm1 Element which position will be calculate
	 * @param {HTMLObject} elm2 Element which is the last one before calculation stops
	 * @param {Object} Left and top position of the given element
	 */
	getElementPosition: function(elm1, elm2) {
		var top = 0, left = 0; 	
		while (elm1 && elm1 != elm2) {
			left += elm1.offsetLeft;
			top += elm1.offsetTop;
			elm1 = elm1.offsetParent;
		}
		return {left : left, top : top};
	}
}

/**
 * Context menu object
 */
var WYSIWYG_ContextMenu = {
	
	html: "",
	contextMenuDiv: null,
	
	/**
	 * Init function
	 *
	 * @param {String} n Editor identifier
	 */
	init: function(n) {
		var doc = WYSIWYG.getEditorWindow(n).document;
			
		// create context menu div
		this.contextMenuDiv = document.createElement("div");
		this.contextMenuDiv.className = "wysiwyg-context-menu-div";
		this.contextMenuDiv.setAttribute("class", "wysiwyg-context-menu-div");
		this.contextMenuDiv.style.display = "none";
		this.contextMenuDiv.style.position = "absolute";
		this.contextMenuDiv.style.zindex = 1000;
		this.contextMenuDiv.style.left = "0";
		this.contextMenuDiv.style.top = "0";
		this.contextMenuDiv.unselectable = "on";		
		document.body.insertBefore(this.contextMenuDiv, document.body.firstChild);
		
		// bind event listeners
		WYSIWYG_Core.addEvent(doc, "contextmenu", function context(e) { WYSIWYG_ContextMenu.show(e, n); });
		WYSIWYG_Core.addEvent(doc, "click", function context(e) { WYSIWYG_ContextMenu.close(); });
		WYSIWYG_Core.addEvent(doc, "keydown", function context(e) { WYSIWYG_ContextMenu.close(); });
		WYSIWYG_Core.addEvent(document, "click", function context(e) { WYSIWYG_ContextMenu.close(); });
	},
	
	/**
	 * Show the context menu
	 *
	 * @param e Event
	 * @param n Editor identifier
	 */
	show: function(e, n) {
		if(this.contextMenuDiv == null) return false;
		
		var ifrm = WYSIWYG.getEditor(n);
		var doc = WYSIWYG.getEditorWindow(n).document;
	
		// set the context menu position
		var pos = WYSIWYG_Core.getElementPosition(ifrm);		
		var x = WYSIWYG_Core.isMSIE ? pos.left + e.clientX : pos.left + (e.pageX - doc.body.scrollLeft);
		var y = WYSIWYG_Core.isMSIE ? pos.top + e.clientY : pos.top + (e.pageY - doc.body.scrollTop);
					
		this.contextMenuDiv.style.left = x + "px"; 
		this.contextMenuDiv.style.top = y + "px";
		this.contextMenuDiv.style.visibility = "visible";
		this.contextMenuDiv.style.display = "block";	
		
		// call the context menu, mozilla needs some time
		window.setTimeout("WYSIWYG_ContextMenu.output('" + n + "')", 10);
			
		WYSIWYG_Core.cancelEvent(e);
		return false;
	},
	
	/**
	 * Output the context menu items
	 *
	 * @param n Editor identifier
	 */
	output: function (n) {
												
		// get selection
		var sel = WYSIWYG.getSelection(n);
		var range = WYSIWYG.getRange(sel);
	
		// get current selected node					
		var tag = WYSIWYG.getTag(range);
		if(tag == null) { return; }
		
		// clear context menu
		this.clear();
		
		// Determine kind of nodes
		var isImg = (tag.nodeName == "IMG") ? true : false;
		var isLink = (tag.nodeName == "A") ? true : false;
		
		// Selection is an image or selection is a text with length greater 0
		var len = 0;
		if(WYSIWYG_Core.isMSIE)
			len = (document.selection && range.text) ? range.text.length : 0;
		else
			len = range.toString().length;
		var sel = len != 0 || isImg;
		
		// Icons
		var iconLink = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][2]};
		var iconImage = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][2]};
		var iconDelete = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][2]};
		var iconCopy = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][2]};
		var iconCut = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][2]};
		var iconPaste = { enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][3], disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][2]};
		
		// Create context menu html
		this.html += '<table class="wysiwyg-context-menu" border="0" cellpadding="0" cellspacing="0">';
		
		// Add items
		this.addItem(n, 'Copy', iconCopy, 'Copy', sel);
		this.addItem(n, 'Cut', iconCut, 'Cut', sel);
		this.addItem(n, 'Paste', iconPaste, 'Paste', true);
		this.addSeperator();
		this.addItem(n, 'InsertImage', iconImage, 'Modify Image Properties...', isImg);
		this.addItem(n, 'CreateLink', iconLink, 'Create or Modify Link...', sel || isLink);
		this.addItem(n, 'RemoveNode', iconDelete, 'Remove', true);
		
		this.html += '</table>';
		this.contextMenuDiv.innerHTML = this.html;
	},
	
	/**
	 * Close the context menu
	 */
	close: function() {
		this.contextMenuDiv.style.visibility = "hidden";
		this.contextMenuDiv.style.display = "none";
	},
	
	/**
	 * Clear context menu
	 */
	clear: function() {
		this.contextMenuDiv.innerHTML = "";
		this.html = "";	
	},
		
	/**
	 * Add context menu item 
	 * 
	 * @param n editor identifier
	 * @param cmd Command
	 * @param icon Icon which is diabled
	 * @param title Title of the item
	 * @param disabled If item is diabled
	 */
	addItem: function(n, cmd, icon, title, disabled) {
		var item = '';
		
		if(disabled) {
			item += '<tr>';
			item += '<td class="icon"><a href="javascript:WYSIWYG.formatText(\'' + cmd + '\',\'' + n + '\', null);"><img src="' + icon.enabled + '" border="0"></a></td>';
			item += '<td onmouseover="this.className=\'mouseover\'" onmouseout="this.className=\'\'" onclick="WYSIWYG.formatText(\'' + cmd + '\',\'' + n + '\', null);WYSIWYG_ContextMenu.close();"><a href="javascript:void(0);">' + title + '</a></td>';
			item += '</tr>';
		}
		else {
			item += '<tr>';
			item += '<td class="icon"><img src="' + icon.disabled + '" border="0"></td>';
			item += '<td onmouseover="this.className=\'mouseover\'" onmouseout="this.className=\'\'"><span class="disabled">' + title + '</span></td>';
			item += '</tr>';
		}
		
		this.html += item;
	},
	
	/**
	 * Add seperator to context menu
	 */
	addSeperator: function() {
		var output = '';
		output += '<tr>';
		output += '<td colspan="2" style="text-align:center;"><hr size="1" color="#C9C9C9" width="95%"></td>';
		output += '</tr>';
		this.html += output;
	}
}

/**
 * Get an element by it's identifier
 *
 * @param id Element identifier
 */
function $(id) {
	return document.getElementById(id);
}

/**
 * Emulates insertAdjacentHTML(), insertAdjacentText() and 
 * insertAdjacentElement() three functions so they work with Netscape 6/Mozilla
 * by Thor Larholm me@jscript.dk
 */
if(typeof HTMLElement!="undefined" && !HTMLElement.prototype.insertAdjacentElement){
	HTMLElement.prototype.insertAdjacentElement = function (where,parsedNode) {
	  switch (where){
		case 'beforeBegin':
			this.parentNode.insertBefore(parsedNode,this);
			break;
		case 'afterBegin':
			this.insertBefore(parsedNode,this.firstChild);
			break;
		case 'beforeEnd':
			this.appendChild(parsedNode);
			break;
		case 'afterEnd':
			if (this.nextSibling) { 
				this.parentNode.insertBefore(parsedNode,this.nextSibling); 
			}
			else { 
				this.parentNode.appendChild(parsedNode); 
			}
			break;
	  }
	};

	HTMLElement.prototype.insertAdjacentHTML = function (where,htmlStr) {
		var r = this.ownerDocument.createRange();
		r.setStartBefore(this);
		var parsedHTML = r.createContextualFragment(htmlStr);
		this.insertAdjacentElement(where,parsedHTML);
	};


	HTMLElement.prototype.insertAdjacentText = function (where,txtStr) {
		var parsedText = document.createTextNode(txtStr);
		this.insertAdjacentElement(where,parsedText);
	};
}



