/********************************************************************
 * openWYSIWYG v1.47 Copyright (c) 2006 openWebWare.com 
 * Contact us at devs@openwebware.com
 * This copyright notice MUST stay intact for use.
 *
 * $Id: wysiwyg.js,v 1.22 2007/09/08 21:45:57 xhaggi Exp $
 * $Revision: 1.22 $
 *
 * 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 = "wysiwyg/images/";
		
		// Popups Directory
		this.PopupsDir = "wysiwyg/popups/";
		
		// CSS Directory File
		this.CSSFile = "wysiwyg/styles/wysiwyg.css";
		//this.CSSFile = "../../tlcstyles.css";
		
		// Default WYSIWYG width and height (use px or %)
		this.Width = "100%";
		this.Height = "200px";
		
		// Default stylesheet of the WYSIWYG editor window
		this.DefaultStyle = "background-color: #F7F7F7; ";
		
		// Stylesheet if editor is disabled
		this.DisabledStyle = "background-color: #F7F7F7";
				
		// Width + Height of the preview window
		this.PreviewWidth = 500;
		this.PreviewHeight = 400;
		
		// Confirmation message if you strip any HTML added by word
		this.RemoveFormatConfMessage = "Clean HTML inserted by MS Word ?";
		
		// Nofication if browser is not supported by openWYSIWYG, leave it blank for no message output.
		this.NoValidBrowserMessage = "openWYSIWYG does not support your browser.";
				
		// Anchor path to strip, leave it blank to ignore
		// or define auto to strip the path where the editor is placed 
		// (only IE)
		this.AnchorPathToStrip = "auto";
		
		// Image path to strip, leave it blank to ignore
		// or define auto to strip the path where the editor is placed 
		// (only IE)
		this.ImagePathToStrip = "auto";
		
		// Enable / Disable the custom context menu
		this.ContextMenu = true;
		
		// Enabled the status bar update. Within the status bar 
		// node tree of the actually selected element will build
		this.StatusBarEnabled = true;
		
		// If enabled than the capability of the IE inserting line breaks will be inverted.
		// Normal: ENTER = <p> , SHIFT + ENTER = <br>
		// Inverted: ENTER = <br>, SHIFT + ENTER = <p>
		this.InvertIELineBreaks = false;
		
		// Replace line breaks with <br> tags
		this.ReplaceLineBreaks = false;
      
		// Page that opened the WYSIWYG (Used for the return command)
		this.Opener = "admin.asp";
		
		// Insert image implementation
		this.ImagePopupFile = "";
		this.ImagePopupWidth = 0;
		this.ImagePopupHeight = 0;
		
		// Holds the available buttons displayed 
		// on the toolbar of the editor
		this.Toolbar = new Array();
		
		this.Toolbar[0] = new Array(
			"font", 
			"fontsize",
			"headings",	
			"bold", 
			"italic", 
			"underline", 
			"strikethrough",
			"seperator", 
			"forecolor", 
			"backcolor", 
			"seperator",
			"justifyfull", 
			"justifyleft", 
			"justifycenter", 
			"justifyright", 
			"seperator", 
			"unorderedlist", 
			"orderedlist",
			"outdent", 
			"indent"
		);
		this.Toolbar[1] = new Array(
			"save",
			// "return",  // return button disabled by default
			"seperator", 
			//"YSMclass", // this is purely experimantal and it not functional
			"subscript", 
			"superscript", 
			"seperator", 
			"cut", 
			"copy", 
			"paste",
			"removeformat",
			"seperator", 
			"undo", 
			"redo", 
			"seperator", 
			"inserttable", 
			"insertimage", 
			"createlink", 
			"seperator",  
			"preview", 
			"print",
			"seperator", 
			"viewSource",
			"maximize", 
			"seperator"
		);
		
		// DropDowns
		this.DropDowns = new Array();
		
					//test classes
			this.DropDowns['YSMclass'] = {
			id: "YSMclass",
			command: "YSMclass",
			label: "<span class=\"{value}\">{value}</font>",
			width: "90px",
			elements: new Array(
				"searchtext", 
				"events" , 
				"topics", 
				"emphasis", 
				"headers", 
				"categories", 
				"construction"
			)
		};
		
		// Fonts
		this.DropDowns['font'] = {
			id: "fonts",
			command: "FontName",
			label: "<font style=\"font-family:{value};font-size:12px;\">{value}</font>",
			width: "90px",
			elements: new Array(
				"Arial",
				"Arial Black",
				"Courier New",
				"Cursive", 
				"Sans Serif",
				"Serif", 
				"Tahoma", 
				"Verdana", 
				"Fantasy", 
				"Georgia", 
				"Times New Roman", 
				"Impact", 
				"Comic Sans MS",	
				"Monotype Corsiva",
				"Monospace",
				"Book Antiqua",
				"Bookman Old Style",
				"Century Gothic",
				"Arial Narrow",
				"Lucida Sans Unicode",
				"Sylfaen",
				"Trebuchet MS",
				"Lucida Console",
				"Franklin Gothic Medium",
				"Palatino Linotype"
			)
		};
		// Font sizes
		this.DropDowns['fontsize'] = {	
			id: "fontsizes",
			command: "FontSize",
			label: "<font size=\"{value}\">Size {value}</font>",
			width: "54px",
			elements: new Array(
				"1", 
				"2", 
				"3", 
				"4", 
				"5", 
				"6", 
				"7"
			)
		};
		// Headings
		this.DropDowns['headings'] = {	
			id: "headings",
			command: "FormatBlock",
			label: "<{value} style=\"margin:0px;text-decoration:none;font-family:Arial\">{value}</{value}>",
			width: "74px",
			elements: new Array(
				"H1", 
				"H2", 
				"H3", 
				"H4", 
				"H5", 
				"H6",
				"p"
			)
		};
				
		// 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
	"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'],
	"justifyfull": 	  ['Justifyfull', 		   'Justifyfull', 			'justify_justify.gif', 	  'justify_justify_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'],
	"fonts":     	  ['Fonts',           	   'Select Font',        	'select_font.gif',        'select_font_on.gif'],
	"fontsizes":      ['Fontsizes',            'Select Size',        	'select_size.gif',        'select_size_on.gif'],
	"headings":       ['Headings',             'Select Size',        	'select_heading.gif',     'select_heading_on.gif'],
	"preview":		  ['Preview', 			   'Preview',       	 	'preview.gif',			  'preview_on.gif'],
	"print":		  ['Print', 			   'Print',       	 	 	'print.gif',			  'print_on.gif'],
	"removeformat":   ['RemoveFormat',         'Strip Word HTML',    	'remove_format.gif',      'remove_format_on.gif'],
	"delete":         ['Delete',               'Delete',             	'delete.gif',     		  'delete_on.gif'],
	"save": 		  ['Save', 				   'Save document',         'save.gif', 			  'save_on.gif'],
	"return": 		  ['Return', 			   'Return without saving', 'return.gif', 			  'return_on.gif'],
	"maximize": 	  ['Maximize', 			   'Maximize the editor',   'maximize.gif', 		  'maximize_on.gif'],
	"YSMclass": 	  ['YSMclass', 			   'YSM Classes',   		'select_font.gif', 		  'select_font_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(),
	// maximized
	maximized: 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);
	},
	
	/**
	 * Return the editor div element
	 *
	 * @param {String} n Editor identifier 
	 * @return {HtmlDivElement} Iframe object
	 */
	getEditorDiv: function(n) {
		return $("wysiwyg_div_" + n);
	},
	
	/**
	 * Return the editor table element
	 *
	 * @param {String} n Editor identifier 
	 * @return {HtmlTableElement} Iframe object
	 */
	getEditorTable: function(n) {
		return $("wysiwyg_table_" + n);
	},
	
	/**
	 * Get the iframe object of the WYSIWYG editor
	 * 
	 * @param {String} n Editor identifier 
	 * @return {HtmlIframeElement} Iframe object
	 */
	getEditor: function(n) {
		return $("wysiwyg" + n);
	},
	
	/**
	 * Get editors window element
	 *
	 * @param {String} n Editor identifier 
	 * @return {HtmlWindowElement} 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;
		}
	},
	
	/**
	 * Insert or modify an image
	 * 
	 * @param {String} src Source of the image
	 * @param {Integer} width Width
	 * @param {Integer} height Height
	 * @param {String} align Alignment of the image
	 * @param {String} border Border size
	 * @param {String} alt Alternativ Text
	 * @param {Integer} hspace Horizontal Space
	 * @param {Integer} vspace Vertical Space
	 * @param {String} 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.findParent("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);
		}
	},
	
	/**
	 * Insert or modify a link
	 * 
	 * @param {String} href The url of the link
	 * @param {String} target Target of the link
	 * @param {String} style Stylesheet of the link
	 * @param {String} styleClass Stylesheet class of the link
	 * @param {String} name Name attribute of the link
	 * @param {String} 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.findParent("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);
		}
	},
	
	/**
	 * Strips any HTML added by word
	 *
     * @param {String} n The editor identifier (the textarea's ID)
	 */
	removeFormat: function(n) {
		
		if ( !confirm(this.config[n].RemoveFormatConfMessage) ) { return; }
		var doc = this.getEditorWindow(n).document;
		var str = doc.body.innerHTML;
		
		str = str.replace(/<span([^>])*>(&nbsp;)*\s*<\/span>/gi, '');
	    str = str.replace(/<span[^>]*>/gi, '');
	    str = str.replace(/<\/span[^>]*>/gi, '');
	    str = str.replace(/<p([^>])*>(&nbsp;)*\s*<\/p>/gi, '');
	    str = str.replace(/<p[^>]*>/gi, '');
	    str = str.replace(/<\/p[^>]*>/gi, '');
	    str = str.replace(/<h([^>])[0-9]>(&nbsp;)*\s*<\/h>/gi, '');
	    str = str.replace(/<h[^>][0-9]>/gi, '');
	    str = str.replace(/<\/h[^>][0-9]>/gi, ''); 
		str = str.replace (/<B [^>]*>/ig, '<b>');
		
		// var repl_i1 = /<I[^>]*>/ig;
		// str = str.replace (repl_i1, '<i>');
		
		str = str.replace (/<DIV[^>]*>/ig, '');
		str = str.replace (/<\/DIV>/gi, '');
		str = str.replace (/<[\/\w?]+:[^>]*>/ig, '');
		str = str.replace (/(&nbsp;){2,}/ig, '&nbsp;');
		str = str.replace (/<STRONG>/ig, '');
		str = str.replace (/<\/STRONG>/ig, '');
		str = str.replace (/<TT>/ig, '');
		str = str.replace (/<\/TT>/ig, '');
		str = str.replace (/<FONT [^>]*>/ig, '');
		str = str.replace (/<\/FONT>/ig, '');
		str = str.replace (/STYLE=\"[^\"]*\"/ig, '');
		str = str.replace(/<([\w]+) class=([^ |>]*)([^>]*)/gi, '<$1$3');
  	    str = str.replace(/<([\w]+) style="([^"]*)"([^>]*)/gi, '<$1$3'); 
		str = str.replace(/width=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/classname=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/align=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/valign=([^ |>]*)([^>]*)/gi, '');
	    str = str.replace(/<\\?\??xml[^>]>/gi, '');
	    str = str.replace(/<\/?\w+:[^>]*>/gi, '');
	    str = str.replace(/<st1:.*?>/gi, '');
	    str = str.replace(/o:/gi, ''); 
	    
	    str = str.replace(/<!--([^>])*>(&nbsp;)*\s*<\/-->/gi, '');
   		str = str.replace(/<!--[^>]*>/gi, '');
   		str = str.replace(/<\/--[^>]*>/gi, '');
		
		doc.body.innerHTML = str;
	},
	
	/**
	 * Display an iframe instead of the textarea.
	 * 
	 * @private
	 * @param {String} n The editor identifier (the textarea's ID)
	 * @param {Object} 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
	 * 
	 * @private
	 * @param {String} n The editor identifier (the textarea's ID)
	 * @param {Object} 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 += '<div id="wysiwyg_div_' + n + '" style="width:' + currentWidth  +';">';
	    editor += '<table border="0" cellpadding="0" cellspacing="0" class="tableTextareaEditor" id="wysiwyg_table_' + n + '" style="width:' + currentWidth  + '; height:' + currentHeight + ';">';
	    editor += '<tr><td style="height:22px;vertical-align:top;">';
	    	  
		// Output all command buttons that belong to toolbar one
		for (var j = 0; j < this.config[n].Toolbar.length;j++) { 
			if(this.config[n].Toolbar[j] && this.config[n].Toolbar[j].length > 0) {
				var toolbar = this.config[n].Toolbar[j];
				
				// Generate WYSIWYG toolbar one
			    editor += '<table border="0" cellpadding="0" cellspacing="0" class="toolbar1" style="width:100%;" id="toolbar' + j + '_' + n + '">';
	    		editor += '<tr><td style="width:6px;"><img src="' + this.config[n].ImagesDir + 'seperator2.gif" alt="" hspace="3"></td>';
				
				// Interate over the toolbar element
				for (var i = 0; i < toolbar.length;i++) { 
					var id = toolbar[i];
				    if (toolbar[i]) {
				    	if(typeof (this.config[n].DropDowns[id]) != "undefined") {
				    		var dropdown = this.config[n].DropDowns[id];
				    		editor += '<td style="width: ' + dropdown.width + ';">';
				    		// write the drop down content
				    		editor += this.writeDropDown(n, id);
				    		editor += '</td>';
				    	}
				    	else {
				    			
				    		// Get the values of the Button from the global ToolbarList object
							var buttonObj = this.ToolbarList[toolbar[i]];
							if(buttonObj) {
								var buttonID = buttonObj[0];
								var buttonTitle = buttonObj[1];
								var buttonImage = this.config[n].ImagesDir + buttonObj[2];
								var buttonImageRollover  = this.config[n].ImagesDir + buttonObj[3];
								    
								if (toolbar[i] == "seperator") {
									editor += '<td style="width: 12px;" align="center">';
									editor += '<img src="' + buttonImage + '" border=0 unselectable="on" width="2" height="18" hspace="2" unselectable="on">';
									editor += '</td>';
								}
								// View Source button
								else if (toolbar[i] == "viewSource"){
								    editor += '<td style="width: 22px;">';
									editor += '<span id="HTMLMode' + n + '"><img src="' + buttonImage +  '" border="0" unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + buttonImageRollover + '\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + buttonImage + '\';" onclick="WYSIWYG.execCommand(\'' + n + '\', \'' + buttonID + '\');" 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.execCommand(\'' + n + '\',\'ViewText\');" 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.execCommand(\'' + n + '\', \'' + buttonID + '\');" unselectable="on" width="20" height="20">';
									editor += '</td>';
								}
							}
						}
			  		}
			  	}
			  	editor += '<td>&nbsp;</td></tr></table>';
			}
		}
		
	 	editor += '</td></tr><tr><td valign="top">\n';
		// Create iframe which will be used for rich text editing
		editor += '<iframe frameborder="0" id="wysiwyg' + n + '" class="iframeText" style="width:100%;height:' + currentHeight + ';"></iframe>\n'
	    + '</td></tr>';
	    // Status bar HTML code
	    if(this.config[n].StatusBarEnabled) {
		    editor += '<tr><td class="wysiwyg-statusbar" style="height:10px;" id="wysiwyg_statusbar_' + n + '">&nbsp;</td></tr>';    
		}
	    editor += '</table>';
	    editor += '</div>';
	    
	    // Insert the editor after the textarea	    
	    textarea.insertAdjacentHTML("afterEnd", editor);
	    					
		// 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);
		
		// Enable table highlighting
		WYSIWYG_Table.refreshHighlighting(n);
	    
	    // 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.closeDropDowns(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;			
	},
	
	/**
	 * Disable the given WYSIWYG Editor Box
	 * 
	 * @param {String} n The editor identifier (the textarea's ID)
	 */ 
	disable: function(n) {
		
		// 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);
		}
	},
	
	/**
	 * Enables the given WYSIWYG Editor Box
	 * 
	 * @param {String} n The editor identifier (the textarea's ID)
	 */
	enable: function(n) {
			
		// 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);
		}
	},
	
	/**
	 * Returns the node structure of the current selection as array
	 * 
	 * @param {String} 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 {String} 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;
	},
	
	/**
	 * Updates the status bar with the current node tree
	 *
	 * @param {String} 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; 
		}
	},
	
	/**
	 * Execute a command on the editor document
	 * 
	 * @param {String} command The execCommand (e.g. Bold)
	 * @param {String} n The editor identifier
	 * @param {String} value The value when applicable
	 */
	execCommand: function(n, cmd, value) {
				
		// When user clicks toolbar button make sure it always targets its respective WYSIWYG
		this.getEditorWindow(n).focus();
		
		// When in Text Mode these execCommands are enabled
		var textModeCommands = new Array("ViewText", "Print");
	  
	  	// Check if in Text mode and a disabled command execute
		var cmdValid = false;
		for (var i = 0; i < textModeCommands.length; i++) {
			if (textModeCommands[i] == cmd) {
				cmdValid = true;
			}
		}
		if(this.viewTextMode[n] && !cmdValid) {
			alert("You are in TEXT Mode. This feature has been disabled.");
		  	return;
		} 
		
		// rbg to hex convertion implementation dependents on browser
		var toHexColor = WYSIWYG_Core.isMSIE ? WYSIWYG_Core._dec_to_rgb : WYSIWYG_Core.toHexColor;
		
		// popup screen positions
		var popupPosition = {left: parseInt(window.screen.availWidth / 3), top: parseInt(window.screen.availHeight / 3)};		
		
		// Check the insert image popup implementation
		var imagePopupFile = this.config[n].PopupsDir + 'insert_image.asp';
		var imagePopupWidth = 440;
		var imagePopupHeight = 330;
		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) {
			case "Maximize":
				this.maximize(n);
			break;
			case "FormatBlock":
				WYSIWYG_Core.execCommand(n, cmd, "<" + value + ">");
			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.asp?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=500,height=260,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(this.config[n].PopupsDir + 'preview.html?wysiwyg=' + n,'popup', 'location=0,status=0,scrollbars=1,resizable=1,width=' + this.config[n].PreviewWidth + ',height=' + this.config[n].PreviewHeight + ',top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
			break;
			
			// Print
			case "Print":
				this.print(n);
			break;
			
			// Save
			case "Save":
			    WYSIWYG.updateTextArea(n);
			    var form = WYSIWYG_Core.findParentNode("FORM", this.getEditor(n));
			    if(form == null) {
			    	alert("Can not submit the content, because no form element found.");
			    	return;
			    }
			    form.submit();
			break;
			         
			// Return
			case "Return":
			   location.replace(this.config[n].Opener);
			break;
						
			default: 
				WYSIWYG_Core.execCommand(n, cmd, value);
				
		}
	 		
		// hide node the font + font size selection
		this.closeDropDowns(n);
	},	
	
	/**
	 * Maximize the editor instance
	 * 
	 * @param {String} n The editor identifier
	 */
	maximize: function(n) {
		
		var divElm = this.getEditorDiv(n);
		var tableElm = this.getEditorTable(n);
		var editor = this.getEditor(n);
		var setting = this.config[n];
		var size = WYSIWYG_Core.windowSize();
		size.width -= 5;
		if(this.maximized[n]) {
			WYSIWYG_Core.setAttribute(divElm, "style", "position:static;z-index:9998;top:0px;left:0px;width:" + setting.Width + ";height:100%;");
			WYSIWYG_Core.setAttribute(tableElm, "style", "width:" + setting.Width + ";height:" + setting.Height + ";");
			WYSIWYG_Core.setAttribute(editor, "style", "width:100%;height:" + setting.Height + ";");
			this.maximized[n] = false;
		}
		else {
			WYSIWYG_Core.setAttribute(divElm, "style", "position:absolute;z-index:9998;top:0px;left:0px;width:" + size.width + "px;height:" + size.height + "px;");
			WYSIWYG_Core.setAttribute(tableElm, "style", "width:100%;height:100%;");
			WYSIWYG_Core.setAttribute(editor, "style", "width:100%;height:100%;");
			this.maximized[n] = true;
		}

	},	
			
	/**
	 * Insert HTML into WYSIWYG in rich text
	 *
	 * @param {String} html The HTML being inserted (e.g. <b>hello</b>)
	 * @param {String} n The editor identifier
	 */
	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);
			}
			
			try {
				range.setEnd(afterNode, 0);
				range.setStart(afterNode, 0);
			}
			catch(e) {
				alert(e);
			}
		}
		
		sel.addRange(range);
	},
	
	/**
	 * Prints the content of the WYSIWYG editor area
	 * 
	 * @param {String} 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();
		}
	},
	
	/**
	 * Writes the content of an drop down
	 *
	 * @param {String} n The editor identifier (textarea ID)
	 * @param {String} id Drop down identifier
	 * @return {String} Drop down HTML
	 */
	writeDropDown: function(n, id) {
	
		var dropdown = this.config[n].DropDowns[id];
		var toolbarObj = this.ToolbarList[dropdown.id];
		var image = this.config[n].ImagesDir  + toolbarObj[2];
		var imageOn  = this.config[n].ImagesDir + toolbarObj[3];
		dropdown.elements.sort();
		
		var output = "";
		output += '<table border="0" cellpadding="0" cellspacing="0"><tr>';
		output += '<td onMouseOver="$(\'img_' + dropdown.id + '_' + n + '\').src=\'' + imageOn + '\';" onMouseOut="$(\'img_' + dropdown.id + '_' + n + '\').src=\'' + image + '\';">';
		output += '<img src="' + image + '" id="img_' + dropdown.id + '_' + n + '" height="20" onClick="WYSIWYG.openDropDown(\'' + n + '\',\'' + dropdown.id + '\');" unselectable="on" border="0"><br>';
		output += '<span id="elm_' + dropdown.id + '_' + n + '" class="dropdown" style="width: 145px;display:none;">';
		for (var i = 0; i < dropdown.elements.length;i++) {
			if (dropdown.elements[i]) {
				var value = dropdown.elements[i];
				var label = dropdown.label.replace(/{value}/gi, value);
				// output
		  		output += '<button type="button" onClick="WYSIWYG.execCommand(\'' + n + '\',\'' + dropdown.command + '\',\'' + value + '\')\;" onMouseOver="this.className=\'mouseOver\'" onMouseOut="this.className=\'mouseOut\'" class="mouseOut" style="width: 120px;">';
		  		output += '<table cellpadding="0" cellspacing="0" border="0"><tr>';
		  		output += '<td align="left">' + label + '</td>';
		  		output += '</tr></table></button><br>';	
		  	}
	  	}
  		output += '</span></td></tr></table>';	
  		
		return output;
	},
	
	/**
	 * Close all drop downs. You can define a exclude dropdown id
	 * 
	 * @param {String} n The editor identifier (textarea ID)
     * @param {String} exid Excluded drop down identifier
	 */
	closeDropDowns: function(n, exid) {
		if(typeof(exid) == "undefined") exid = "";
		var dropdowns = this.config[n].DropDowns;
		for(var id in dropdowns) {
			var dropdown = dropdowns[id];
			if(dropdown.id != exid) {
				var divId = "elm_" + dropdown.id + "_" + n;
				if($(divId)) $(divId).style.display = 'none';
			}
		}			
	},
	
	/**
	 * Open a defined drop down
	 * 
     * @param {String} n The editor identifier (textarea ID)
	 * @param {String} id Drop down identifier
	 */
	openDropDown: function(n, id) {
		var divId = "elm_" + id + "_" + n;
		if($(divId).style.display == "none") {	
			$(divId).style.display = "block"; 
		}
		else {
			$(divId).style.display = "none"; 
		}
		$(divId).style.position = "absolute";
		this.closeDropDowns(n, id);
	},
	
	/**
	 * Shows the HTML source code generated by the WYSIWYG editor
	 * 
	 * @param {String} n The editor identifier (textarea ID)
	 */
	viewSource: function(n) {
		
		// document
		var doc = this.getEditorWindow(n).document;
		
		// Enable table highlighting
		WYSIWYG_Table.disableHighlighting(n);
			
		// 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;
	},
		
	/**
	 * Shows the HTML source code generated by the WYSIWYG editor
	 * 
	 * @param {String} n The editor identifier (textarea 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;
		}
		
		// Enable table highlighting
		WYSIWYG_Table.refreshHighlighting(n);
					  
		// 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    : 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 = WYSIWYG_Core.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 = WYSIWYG_Core.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(WYSIWYG_Core.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 = WYSIWYG_Core.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(WYSIWYG_Core.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 = WYSIWYG_Core.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 = WYSIWYG_Core.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 = ""; }
	},
	
	/**
	 * Finds the node with the given tag name in the given range
	 * 
	 * @param {String} tagName Parent tag to find
	 * @param {Range} range Current range
	 */
	findParent: 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]; }
				return WYSIWYG_Core.findParentNode(parentTagName, node);
			}
			else {
				elmWorking = (range.length > 0) ? range.item(0): range.parentElement();					
				elmWorking = WYSIWYG_Core.findParentNode(parentTagName, elmWorking);
				if(elmWorking != null) return elmWorking;
				
				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;
		}
	},
		
	/**
	 * Get the acutally tag of the given range
	 *
	 * @param {Range} range Current 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;
		}
	},
	
	/**
	 * Get the parent node of the given node
	 * 
	 * @param {DOMElement} 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.22 2007/09/08 21:45:57 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 {DOMElement} node The element on which the style attribute will affect
	 * @param {String} style Stylesheet which will be set
	 */
	_setStyleAttribute: function(node, style) {
		if(style == null) return;
		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();
					node.style[attr] = value;
				}
				catch (e) {
					alert(e);
				}
			}
		}
	},
	
	/**
	 * Fix's the issue while getting the attribute style on IE
	 * It's return an object but we need the style string
	 *
	 * @private
	 * @param {DOMElement} node Node element
	 * @return {String} Stylesheet
	 */
	_getStyleAttribute: function(node) {
		if(this.isMSIE) {
			return node.style['cssText'].toLowerCase();		
		}	
		else {
			return node.getAttribute("style");
		}
	},
	
	/**
	 * Set an attribute's value on the given node element.
	 *
	 * @param {DOMElement} node Node element
	 * @param {String} attr Attribute which is set
	 * @param {String} value Value of the attribute
	 */
	setAttribute: function(node, attr, value) {
		if(value == null || node == null || attr == null) return;
		if(attr.toLowerCase() == "style") {
			this._setStyleAttribute(node, value);
		}
		else {
			node.setAttribute(attr, value);
		}
	},
	
	/**
	 * Removes an attribute on the given node
	 * 
	 * @param {DOMElement} node Node element
	 * @param {String} attr Attribute which will be removed
	 */ 
	removeAttribute: function(node, attr) {
		node.removeAttribute(attr, false);
	},
	
	/**
	 * Get the vale of the attribute on the given node
	 * 
	 * @param {DOMElement} node Node element
	 * @param {String} attr Attribute which value will be returned
	 */
	getAttribute: function(node, attr) {
		if(node == null || attr == null) return;
		if(attr.toLowerCase() == "style") {
			return this._getStyleAttribute(node);
		}
		else {
			return node.getAttribute(attr);
		}
	},
	
	/**
	 * Get the path out of an given url
	 * 
	 * @param {String} url The url with is used to get 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;
	},
	
	/**
	 * Get the documents url, convert local urls to web urls
	 * 
	 * @param {DOMElement} doc Document which is used to get the url
	 */
	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;
	},
	
	/**
	 * Find a parent node with the given name, of the given start node
	 * 
	 * @param {String} tagName - Tag name of the node to find
	 * @param {DOMElement} node - Node element
	 */
	findParentNode: function(tagName, node) {
		while (node.tagName != "HTML") {
	  		if (node.tagName == tagName){
	  			return node;
	  		} 
	  		node = node.parentNode;
	 	}
	 	return null;
	},
	
	/**
	 * 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) {
		if(str == null) return "";
		// 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};
	},
	
	/**
	 * Get the window size
	 * @private
	 */
	windowSize: function() {
		if (window.innerWidth) {
	  		return {width: window.innerWidth, height: window.innerHeight};
	  	} 
		else if (document.body && document.body.offsetWidth) {
	  		return {width: document.body.offsetWidth, height: document.body.offsetHeight};
	  	} 
		else {
	  		return {width: 0, height: 0};
	  	}
	}
}

/**
 * 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 = 9999;
		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.execCommand(\'' + n + '\',\'' + cmd + '\', null);"><img src="' + icon.enabled + '" border="0"></a></td>';
			item += '<td onmouseover="this.className=\'mouseover\'" onmouseout="this.className=\'\'" onclick="WYSIWYG.execCommand(\'' + n + '\', \'' + cmd + '\', 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;
	}
}

/**
 * Table object
 */
var WYSIWYG_Table = {

	/**
	 * 
	 */
	create: function(n, tbl) {
		
		// get editor
		var doc = WYSIWYG.getEditorWindow(n).document;
		// get selection and range
		var sel = WYSIWYG.getSelection(n);
		var range = WYSIWYG.getRange(sel);
		var table = null;
				
		// get element from selection
		if(WYSIWYG_Core.isMSIE) {
			if(sel.type == "Control" && range.length == 1) {	
				range = WYSIWYG.getTextRange(range(0));
				range.select();
			}
		}

		// find a parent TABLE element
		//table = WYSIWYG.findParent("table", range);
				
		// check if parent is found
		//var update = (table == null) ? false : true;
		//if(!update) table = tbl;
		table = tbl;
		
		// add rows and cols
		var rows = WYSIWYG_Core.getAttribute(tbl, "tmprows");
		var cols = WYSIWYG_Core.getAttribute(tbl, "tmpcols");
		WYSIWYG_Core.removeAttribute(tbl, "tmprows");
		WYSIWYG_Core.removeAttribute(tbl, "tmpcols");
		for(var i=0;i<rows;i++) {
			var tr = doc.createElement("tr");
			for(var j=0;j<cols;j++){
				var td = createTD();
				tr.appendChild(td);	
			}
			table.appendChild(tr);
		}
		
		// on update exit here
		//if(update) { return; }
	
		// Check if IE or Mozilla (other)
		if (WYSIWYG_Core.isMSIE) {
			range.pasteHTML(table.outerHTML);   
		}
		else {
			WYSIWYG.insertNodeAtSelection(table, n);
		}
		
		// refresh table highlighting
		this.refreshHighlighting(n);
		
		
		
		
	 	// functions
		function createTD() {
			var td = doc.createElement("td");
			td.innerHTML = "&nbsp;";
			return td;
		}
	 	
	},

	/**
	 * Enables the table highlighting
	 * 
	 * @param {String} n The editor identifier (the textarea's ID)
	 */
	refreshHighlighting: function(n) {
		var doc = WYSIWYG.getEditorWindow(n).document;
		var tables = doc.getElementsByTagName("table");	
		for(var i=0;i<tables.length;i++) {
			this._enableHighlighting(tables[i]);
		}
		var tds = doc.getElementsByTagName("td");	
		for(var i=0;i<tds.length;i++) {
			this._enableHighlighting(tds[i]);
		}
	},
	
	/**
	 * Enables the table highlighting
	 * 
	 * @param {String} n The editor identifier (the textarea's ID)
	 */
	disableHighlighting: function(n) {
		var doc = WYSIWYG.getEditorWindow(n).document;
		var tables = doc.getElementsByTagName("table");	
		for(var i=0;i<tables.length;i++) {
			this._disableHighlighting(tables[i]);
		}
		var tds = doc.getElementsByTagName("td");	
		for(var i=0;i<tds.length;i++) {
			this._disableHighlighting(tds[i]);
		}
	
	},
	
	/**
	 * @private
	 */
	_enableHighlighting: function(node) {
		var style = WYSIWYG_Core.getAttribute(node, "style");	
		if(style == null) style = " ";
		//alert("ENABLE: ELM = " + node.tagName + "; STYLE = " + style);
		WYSIWYG_Core.removeAttribute(node, "prevstyle");
		WYSIWYG_Core.setAttribute(node, "prevstyle", style);
		WYSIWYG_Core.setAttribute(node, "style", "");
	},
	
	/**
	 * @private
	 */
	_disableHighlighting: function(node) {
		var style = WYSIWYG_Core.getAttribute(node, "prevstyle");
		//alert("DISABLE: ELM = " + node.tagName + "; STYLE = " + style);
		// if no prevstyle is defined, the table is not in highlighting mode
		if(style == null || style == "") { 
			this._enableHighlighting(node); 
			return; 
		}			
		WYSIWYG_Core.removeAttribute(node, "prevstyle");
		WYSIWYG_Core.removeAttribute(node, "style");
		WYSIWYG_Core.setAttribute(node, "style", style);
	}
}


/**
 * 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);
	};
}




