

/********************************************************************
 * 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 = "../popups/images/";
		
		// Popups Directory
		this.PopupsDir = "../popups/";
		
		// CSS Directory File
		this.CSSFile = "../styles/wysiwyg.css";		
		
		// Default WYSIWYG width and height (use px or %)
		this.Width = "900px";
		this.Height = "360px";
		
		// Form name where WYSIWYG is attached to textarea
		this.FormName = "edit";
      
		// Page that opened the WYSIWYG (Used for the return command)
		this.Opener = "admin.asp";
		
		// Default stylesheet of the WYSIWYG editor window
		this.DefaultStyle = "font-family: arial, verdana, helvetica; font-size: 10pt;";
		
		// Stylesheet if editor is disabled
		this.DisabledStyle = "font-family: Arial; font-size: 10pt; background-color: #EEEEEE";
				
		// Width + Height of the preview window
		this.PreviewWidth = 980;
		this.PreviewHeight = 700; 
		
		// Action of the preview window
		this.Preview = ""
		
		// 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(
			"bold", 
			"italic", 
			"underline", 
			"strikethrough",
			"seperator", 
			"forecolor", 
			"backcolor", 
			"seperator", 
			"justifyleft", 
			"justifycenter", 
			"justifyright", 
			"seperator", 
			"unorderedlist", 
			"orderedlist",
			"outdent", 
			"indent",
			"seperator", 
			"subscript", 
			"superscript", 
			"seperator", 
			"cut", 
			"copy", 
			"paste",
			"removeformat",
			"seperator", 
			"undo", 
			"redo", 
			"seperator", 
			"inserttable", 
			"insertimage", 
			"createlink", 
			"seperator", 
			"viewSource"
		);
			
		// 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               	buttonImageDown
	"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'],
	"save": 	  ['Save', 		   'Save document', 		'save.gif', 		  'save_on.gif'],
	"saveclose": 	  ['Saveclose', 	   'Save and close document', 	'saveclose.gif', 	  'saveclose_on.gif'],
	"return": 	  ['Return', 		   'Close without saving', 	'return.gif', 		  'return_on.gif',			'return_down.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, 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);
		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, '');
		
		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>';
				
				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]; 
							var buttonImageDown  = this.config[n].ImagesDir + buttonObj[4];
							    
							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>';
							}
							else if (toolbar[i] == "return") {
								editor += '<td style="width: 100%;">';
								editor += '<img align="right" src="' + buttonImage + '" border=0 unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.src=\'' + buttonImageRollover + '\';" onmouseout="this.src=\'' + buttonImage + '\';" onmousedown="this.src=\'' + buttonImageDown + '\';" onclick="WYSIWYG.formatText(\'' + buttonID + '\',\'' + n + '\');" unselectable="on" width="20" height="20">';
								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 id="wysiwyg' + n + '" class="iframeText" style="border:1px solid #a5acb2; 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 / 8), top: parseInt(window.screen.availHeight / 4)};		
		
		// 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 = 215;
		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 + '?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=400,height=360,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			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('http://www.tilseth-as.no/default.asp?pageID=preview&wysiwyg=' + n + '&preview=' + this.config[n].Preview,'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;
			
			// Save
			case "Save":
			WYSIWYG.updateTextArea(n);
   				document.getElementById(this.config[n].FormName).submit();
			break;
			
			// Save and close
			case "Saveclose":
			WYSIWYG.updateTextArea(n);
   				document.getElementById(this.config[n].FormName).submit();
			break;
         
			// Return
			case "Return":
   				history.go(-1);
			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);
	};
}

var news = new WYSIWYG.Settings();
news.Width = "500px"; 
news.Height = "380px";   
news.FormName = "article"; 
news.Preview = "news";
news.Toolbar[0] = new Array("bold", "italic", "underline", "strikethrough", "seperator", "forecolor", "backcolor", "seperator", "justifyleft", "justifycenter", "justifyright", "seperator", "unorderedlist", "orderedlist", "outdent", "indent"); 
news.Toolbar[1] = new Array("subscript", "superscript", "seperator", "cut", "copy", "paste", "removeformat", "seperator", "undo", "redo", "seperator", "inserttable", "insertimage", "createlink", "seperator", "preview", "print", "seperator", "viewSource"); 

var full = new WYSIWYG.Settings();
full.Width = "890px"; 
full.Height = "400px";   
full.FormName = "edit"; 