
/*  Prototype JavaScript framework
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
/*--------------------------------------------------------------------------*/

//note: modified & stripped down version of prototype, to be used with moo.fx by mad4milk (http://moofx.mad4milk.net).

var Class = {
	create: function() {
		return function() {
			this.initialize.apply(this, arguments);
		}
	}
}

Object.extend = function(destination, source) {
	for (property in source) destination[property] = source[property];
	return destination;
}

Function.prototype.bind = function(object) {
	var __method = this;
	return function() {
		return __method.apply(object, arguments);
	}
}

Function.prototype.bindAsEventListener = function(object) {
var __method = this;
	return function(event) {
		__method.call(object, event || window.event);
	}
}

function $() {
	if (arguments.length == 1) return get$(arguments[0]);
	var elements = [];
	$c(arguments).each(function(el){
		elements.push(get$(el));
	});
	return elements;

	function get$(el){
		if (typeof el == 'string') el = document.getElementById(el);
		return el;
	}
}

if (!window.Element) var Element = new Object();

Object.extend(Element, {
	remove: function(element) {
		element = $(element);
		element.parentNode.removeChild(element);
	},

	hasClassName: function(element, className) {
		element = $(element);
		if (!element) return;
		var hasClass = false;
		element.className.split(' ').each(function(cn){
			if (cn == className) hasClass = true;
		});
		return hasClass;
	},

	addClassName: function(element, className) {
		element = $(element);
		Element.removeClassName(element, className);
		element.className += ' ' + className;
	},

	removeClassName: function(element, className) {
		element = $(element);
		if (!element) return;
		var newClassName = '';
		element.className.split(' ').each(function(cn, i){
			if (cn != className){
				if (i > 0) newClassName += ' ';
				newClassName += cn;
			}
		});
		element.className = newClassName;
	},

	cleanWhitespace: function(element) {
		element = $(element);
		$c(element.childNodes).each(function(node){
			if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node);
		});
	},

	find: function(element, what) {
		element = $(element)[what];
		while (element.nodeType != 1) element = element[what];
		return element;
	}
});

var Position = {
	cumulativeOffset: function(element) {
		var valueT = 0, valueL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			element = element.offsetParent;
		} while (element);
		return [valueL, valueT];
	}
};

document.getElementsByClassName = function(className) {
	var children = document.getElementsByTagName('*') || document.all;
	var elements = [];
	$c(children).each(function(child){
		if (Element.hasClassName(child, className)) elements.push(child);
	});
	return elements;
}

//useful array functions
Array.prototype.iterate = function(func){
	for(var i=0;i<this.length;i++) func(this[i], i);
}
if (!Array.prototype.each) Array.prototype.each = Array.prototype.iterate;

function $c(array){
	var nArray = [];
	for (var i=0;i<array.length;i++) nArray.push(array[i]);
	return nArray;
}

/*--------------------------------------------------------------------------*/

Object.extend(String.prototype, {
	ltrim: function() {
		return this.replace(/^\\s+/, '');
	},

	rtrim: function() {
		return this.replace(/\\s+$/, '');
	},

	trim: function() {
		return this.replace(/^\\s+|\\s+$/g, '');
	}
});

/*--------------------------------------------------------------------------*/

/*
moo.fx, simple effects library built with prototype.js (http://prototype.conio.net).
by Valerio Proietti (http://mad4milk.net) MIT-style LICENSE.
for more info (http://moofx.mad4milk.net).
Sunday, March 05, 2006
v 1.2.3
*/

var fx = new Object();
//base
fx.Base = function(){};
fx.Base.prototype = {
	setOptions: function(options) {
	this.options = {
		duration: 500,
		onComplete: '',
		transition: fx.sinoidal
	}
	Object.extend(this.options, options || {});
	},

	step: function() {
		var time  = (new Date).getTime();
		if (time >= this.options.duration+this.startTime) {
			this.now = this.to;
			clearInterval (this.timer);
			this.timer = null;
			if (this.options.onComplete) setTimeout(this.options.onComplete.bind(this), 10);
		}
		else {
			var Tpos = (time - this.startTime) / (this.options.duration);
			this.now = this.options.transition(Tpos) * (this.to-this.from) + this.from;
		}
		this.increase();
	},

	custom: function(from, to) {
		if (this.timer != null) return;
		this.from = from;
		this.to = to;
		this.startTime = (new Date).getTime();
		this.timer = setInterval (this.step.bind(this), 13);
	},

	hide: function() {
		this.now = 0;
		this.increase();
	},

	clearTimer: function() {
		clearInterval(this.timer);
		this.timer = null;
	}
}

//stretchers
fx.Layout = Class.create();
fx.Layout.prototype = Object.extend(new fx.Base(), {
	initialize: function(el, options) {
		this.el = $(el);
		this.el.style.overflow = "hidden";
		this.iniWidth = this.el.offsetWidth;
		this.iniHeight = this.el.offsetHeight;
		this.setOptions(options);
	}
});

fx.Height = Class.create();
Object.extend(Object.extend(fx.Height.prototype, fx.Layout.prototype), {
	increase: function() {
		this.el.style.height = this.now + "px";
	},

	toggle: function() {
		if (this.el.offsetHeight > 0) this.custom(this.el.offsetHeight, 0);
		else this.custom(0, this.el.scrollHeight);
	}
});

fx.Width = Class.create();
Object.extend(Object.extend(fx.Width.prototype, fx.Layout.prototype), {
	increase: function() {
		this.el.style.width = this.now + "px";
	},

	toggle: function(){
		if (this.el.offsetWidth > 0) this.custom(this.el.offsetWidth, 0);
		else this.custom(0, this.iniWidth);
	}
});

//fader
fx.Opacity = Class.create();
fx.Opacity.prototype = Object.extend(new fx.Base(), {
	initialize: function(el, options) {
		this.el = $(el);
		this.now = 1;
		this.increase();
		this.setOptions(options);
	},

	increase: function() {
		if (this.now == 1 && (/Firefox/.test(navigator.userAgent))) this.now = 0.9999;
		this.setOpacity(this.now);
	},

	setOpacity: function(opacity) {
		if (opacity == 0 && this.el.style.visibility != "hidden") this.el.style.visibility = "hidden";
		else if (this.el.style.visibility != "visible") this.el.style.visibility = "visible";
		if (window.ActiveXObject) this.el.style.filter = "alpha(opacity=" + opacity*100 + ")";
		this.el.style.opacity = opacity;
	},

	toggle: function() {
		if (this.now > 0) this.custom(1, 0);
		else this.custom(0, 1);
	}
});

//transitions
fx.sinoidal = function(pos){
	return ((-Math.cos(pos*Math.PI)/2) + 0.5);
	//this transition is from script.aculo.us
}
fx.linear = function(pos){
	return pos;
}
fx.cubic = function(pos){
	return Math.pow(pos, 3);
}
fx.circ = function(pos){
	return Math.sqrt(pos);
}

/*--------------------------------------------------------------------------*/

/*
moo.fx pack, effects extensions for moo.fx.
by Valerio Proietti (http://mad4milk.net) MIT-style LICENSE
for more info visit (http://moofx.mad4milk.net).
Friday, April 14, 2006
v 1.2.4
*/

//smooth scroll
fx.Scroll = Class.create();
fx.Scroll.prototype = Object.extend(new fx.Base(), {
	initialize: function(options) {
		this.setOptions(options);
	},

	scrollTo: function(el){
		var dest = Position.cumulativeOffset($(el))[1];
		var client = window.innerHeight || document.documentElement.clientHeight;
		var full = document.documentElement.scrollHeight;
		var top = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
		if (dest+client > full) this.custom(top, dest - client + (full-dest));
		else this.custom(top, dest);
	},

	increase: function(){
		window.scrollTo(0, this.now);
	}
});

//text size modify, now works with pixels too.
fx.Text = Class.create();
fx.Text.prototype = Object.extend(new fx.Base(), {
	initialize: function(el, options) {
		this.el = $(el);
		this.setOptions(options);
		if (!this.options.unit) this.options.unit = "em";
	},

	increase: function() {
		this.el.style.fontSize = this.now + this.options.unit;
	}
});

//composition effect: width/height/opacity
fx.Combo = Class.create();
fx.Combo.prototype = {
	setOptions: function(options) {
		this.options = {
			opacity: true,
			height: true,
			width: false
		}
		Object.extend(this.options, options || {});
	},

	initialize: function(el, options) {
		this.el = $(el);
		this.setOptions(options);
		if (this.options.opacity) {
			this.o = new fx.Opacity(el, options);
			options.onComplete = null;
		}
		if (this.options.height) {
			this.h = new fx.Height(el, options);
			options.onComplete = null;
		}
		if (this.options.width) this.w = new fx.Width(el, options);
	},

	toggle: function() { this.checkExec('toggle'); },

	hide: function(){ this.checkExec('hide'); },

	clearTimer: function(){ this.checkExec('clearTimer'); },

	checkExec: function(func){
		if (this.o) this.o[func]();
		if (this.h) this.h[func]();
		if (this.w) this.w[func]();
	},

	//only if width+height
	resizeTo: function(hto, wto) {
		if (this.h && this.w) {
			this.h.custom(this.el.offsetHeight, this.el.offsetHeight + hto);
			this.w.custom(this.el.offsetWidth, this.el.offsetWidth + wto);
		}
	},

	customSize: function(hto, wto) {
		if (this.h && this.w) {
			this.h.custom(this.el.offsetHeight, hto);
			this.w.custom(this.el.offsetWidth, wto);
		}
	}
}

fx.Accordion = Class.create();
fx.Accordion.prototype = {
	setOptions: function(options) {
		this.options = {
			delay: 100,
			opacity: false
		}
		Object.extend(this.options, options || {});
	},

	initialize: function(togglers, elements, options) {
		this.elements = elements;
		this.setOptions(options);
		var options = options || '';
		this.fxa = [];
		if (options && options.onComplete) options.onFinish = options.onComplete;
		elements.each(function(el, i){
			options.onComplete = function(){
				if (el.offsetHeight > 0) el.style.height = '1%';
				if (options.onFinish) options.onFinish(el);
			}
			this.fxa[i] = new fx.Combo(el, options);
			this.fxa[i].hide();
		}.bind(this));

		togglers.each(function(tog, i){
			if (typeof tog.onclick == 'function') var exClick = tog.onclick;
			tog.onclick = function(){
				if (exClick) exClick();
				this.showThisHideOpen(elements[i]);
			}.bind(this);
		}.bind(this));
	},

	showThisHideOpen: function(toShow){
		this.elements.each(function(el, j){
			if (el.offsetHeight > 0 && el != toShow) this.clearAndToggle(el, j);
			if (el == toShow && toShow.offsetHeight == 0) setTimeout(function(){this.clearAndToggle(toShow, j);}.bind(this), this.options.delay);
		}.bind(this));
	},

	clearAndToggle: function(el, i){
		this.fxa[i].clearTimer();
		this.fxa[i].toggle();
	}
}

var Remember = new Object();
Remember = function(){};
Remember.prototype = {
	initialize: function(el, options){
		this.el = $(el);
		this.days = 365;
		this.options = options;
		this.effect();
		var cookie = this.readCookie();
		if (cookie) {
			this.fx.now = cookie;
			this.fx.increase();
		}
	},

	//cookie functions based on code by Peter-Paul Koch
	setCookie: function(value) {
		var date = new Date();
		date.setTime(date.getTime()+(this.days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
		document.cookie = this.el+this.el.id+this.prefix+"="+value+expires+"; path=/";
	},

	readCookie: function() {
		var nameEQ = this.el+this.el.id+this.prefix + "=";
		var ca = document.cookie.split(';');
		for(var i=0;c=ca[i];i++) {
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return false;
	},

	custom: function(from, to){
		if (this.fx.now != to) {
			this.setCookie(to);
			this.fx.custom(from, to);
		}
	}
}

fx.RememberHeight = Class.create();
fx.RememberHeight.prototype = Object.extend(new Remember(), {
	effect: function(){
		this.fx = new fx.Height(this.el, this.options);
		this.prefix = 'height';
	},

	toggle: function(){
		if (this.el.offsetHeight == 0) this.setCookie(this.el.scrollHeight);
		else this.setCookie(0);
		this.fx.toggle();
	},

	resize: function(to){
		this.setCookie(this.el.offsetHeight+to);
		this.fx.custom(this.el.offsetHeight,this.el.offsetHeight+to);
	},

	hide: function(){
		if (!this.readCookie()) {
			this.fx.hide();
		}
	}
});

fx.RememberText = Class.create();
fx.RememberText.prototype = Object.extend(new Remember(), {
	effect: function(){
		this.fx = new fx.Text(this.el, this.options);
		this.prefix = 'text';
	}
});

//useful for-replacement
Array.prototype.iterate = function(func){
	for(var i=0;i<this.length;i++) func(this[i], i);
}
if (!Array.prototype.each) Array.prototype.each = Array.prototype.iterate;

//Easing Equations (c) 2003 Robert Penner, all rights reserved.
//This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html.

//expo
fx.expoIn = function(pos){
	return Math.pow(2, 10 * (pos - 1));
}
fx.expoOut = function(pos){
	return (-Math.pow(2, -10 * pos) + 1);
}

//quad
fx.quadIn = function(pos){
	return Math.pow(pos, 2);
}
fx.quadOut = function(pos){
	return -(pos)*(pos-2);
}

//circ
fx.circOut = function(pos){
	return Math.sqrt(1 - Math.pow(pos-1,2));
}
fx.circIn = function(pos){
	return -(Math.sqrt(1 - Math.pow(pos, 2)) - 1);
}

//back
fx.backIn = function(pos){
	return (pos)*pos*((2.7)*pos - 1.7);
}
fx.backOut = function(pos){
	return ((pos-1)*(pos-1)*((2.7)*(pos-1) + 1.7) + 1);
}

//sine
fx.sineOut = function(pos){
	return Math.sin(pos * (Math.PI/2));
}
fx.sineIn = function(pos){
	return -Math.cos(pos * (Math.PI/2)) + 1;
}
fx.sineInOut = function(pos){
	return -(Math.cos(Math.PI*pos) - 1)/2;
}

/*--------------------------------------------------------------------------*/

//moo.dom by Valerio Proietti (http://mad4milk.net) MIT Open Source license;
//v 1.5 (beta);

function $S() {
	var elements = [];
	$c(arguments).each(function(sel){
		if (typeof sel == 'string') {
			sel.getElements().each(function(el){
				elements.push(el);
			});
		}
		else elements.push(sel);
	});
	return elements;
}

/*------------------String Prototypes----------------------*/

Object.extend(String.prototype, {
	getElements: function(filter){
		var params = [];
		this.split(' ').each(function(arg, j){
			params[j] = param = [];
			if (arg.indexOf('#') > -1) {
				var bits = arg.split('#');
				param['tag'] = bits[0] || '*';
				param['id'] = bits[1];
			}
			else if (arg.indexOf('.') > -1) {
				var bits = arg.split('.');
				param['tag'] = bits[0] || '*';
				param['class'] = bits[1];
			}
			else param['tag'] = arg;
		});
		var filter = filter || document;
		filter = $c(filter.getElementsByTagName('*'));
		params.each(function(param, k){
			if (param['tag'] != '*' && k == 0) filter = filter.filterByTagName(param['tag']);
			else if (k != 0) filter = filter.getElementsByTagName(param['tag']);
			if (param['id']) filter = filter.filterById(param['id']);
			if (param['class']) filter = filter.filterByClassName(param['class']);
		});
		return filter;
	},

	getElementsBySelector: function(filter){
		if (!filter) filter = null;
		var elements = [];
		this.split(',').each(function(selector){
			elmnts = selector.replace(/^\s*|\s*$/g,"").getElements(filter);
			elmnts.each(function(el){
				elements.push(el);
			});
		});
		return elements;
	}
});


/*----------------------Array Prototypes-----------------------*/

function $c(array){
	var nArray = [];
	for (i=0;el=array[i];i++) nArray.push(el);
	return nArray;
}

Object.extend(Array.prototype, {
	iterate: function(func){
		for(var i=0;ob=this[i];i++) func(ob, i);
	},

	action: function(actions){
		this.each(function(el){
			if (actions.initialize) actions.initialize.apply(el);
			for(action in actions){
				if (action.slice(0,2) == 'on') el[action] = actions[action];
			}
		});
	},

	filterById: function(id){
		var found = [];
		this.each(function(el){
			if (el.id == id) found.push(el);
		});
		return found;
	},

	filterByClassName: function(className){
		var found = [];
		this.each(function(el){
			if (Element.hasClassName(el, className)) found.push(el);
		});
		return found;
	},

	filterByTagName: function(tagName){
		var found = [];
		this.each(function(el){
			if (el.tagName.toLowerCase() == tagName) found.push(el);
		});
		return found;
	},

	getElementsByTagName: function(tagName){
		var found = [];
		this.each(function(el){
			$c(el.getElementsByTagName(tagName)).each(function(tn){
				found.push(tn);
			});
		});
		return found;
	}
});

if(!Array.prototype.each) Array.prototype.each = Array.prototype.iterate;

/*--------------------------------------------------------------------------*/

	// This is the url to the current editor and must have exactly one backslash at the end!
	// Can this not be retrieved using javascript?
	if (typeof _editor_url == "string") {
	  // Leave exactly one backslash at the end of _editor_url
	  _editor_url = _editor_url.replace(/\x2f*$/, '/');
	} else {
	  _editor_url = '/adm/s/';
	}

	var	RTF_COLOR1 = "666";
	var	RTF_COLOR2 = "333";
	var	RTF_COLOR3 = "000";

	var editor = false;

	function RTF(textarea) {
		if (RTF.checkSupportedBrowser()) {
			// maximum size of the undo queue
			this.undoSteps = 20;

			// html style of the editer iframe
			this.pageStyle =
				"body,html{border:0;margin:0;padding:2px;font:0.9em Arial,Helvetica,Sans-Serif;}"+
				"h2,h3,p{margin:0 0 0.9em 0;padding:0;color:#"+RTF_COLOR2+"}"+
				"h2,h3{margin:1.8em 0 0 0}"+
				"h2,h3{font-weight:bold;font-size:1.2em}"+
				"a,h3{color:#"+RTF_COLOR1+"666;text-decoration:none}"+
				"a:hover{text-decoration:underline}"+
				"em{color:#"+RTF_COLOR3+"000;font-style:italic}"+
				"img{border:0}"+
				".htmtableborders td{border:1px dashed #eee}";

			this.imgFolder = "../i/";
			this.popupFolder = "/jobwise/popup/";

			// TODO : should be able to turn certain toolbars on and off
			this.toolbar = [
				[ "wordclean" ],
				[ "separator", "bold", "italic", "separator", "mainHeading", "subHeading", "paragraph", "separator", "subscript", "superscript" ],
				[ "separator", "createlink", "unlink", "insertimage" ],
				[ "separator", "outdent", "indent", "separator", "insertorderedlist", "insertunorderedlist" ],
				[ "separator", "htmlmode" ],
				[ "separator", "inserttable", "tableprop" ],
				[ "rowprop", "rowinsertabove", "rowinsertunder", "rowdelete", "rowsplit" ],
				[ "colinsertbefore", "colinsertafter", "coldelete", "colsplit" ],
				[ "cellprop", "cellinsertbefore", "cellinsertafter", "celldelete", "cellmerge", "cellsplit" ],
				[ "separator", "toggleborders" ]
			];

			this.btnList = {
				wordclean: [ "Clean Word Code", ["buttons.gif", 0, 0], false, function(e) {e.execCommand("killword");} ],
				bold: [ "Bold", ["buttons.gif", 0, 1], false, function(e) {e.execCommand("bold");} ],
				italic: [ "Italic", ["buttons.gif", 0, 2], false, function(e) {e.execCommand("italic");} ],
				createlink: [ "Insert Web Link", ["buttons.gif", 0, 3], false, function(e) {e.execCommand("createlink", true);} ],
				unlink: [ "Remove Web Link", ["buttons.gif", 0, 4], false, function(e) {e.execCommand("unlink", true);} ],
				insertimage: [ "Insert/Modify Image", ["buttons.gif", 0, 5], false, function(e) {e.execCommand("insertimage");} ],
				mainHeading: [ "Format as Heading", ["buttons.gif", 0, 6], false, function(e) {e.execCommand("formatblock",null,"<h2>");} ],
				subHeading: [ "Format as Sub Heading", ["buttons.gif", 0, 7], false, function(e) {e.execCommand("formatblock",null,"<h3>");} ],
				paragraph: [ "Format as Paragraph", ["buttons.gif", 0, 8], false, function(e) {e.execCommand("formatblock",null,"<p>");} ],
				insertunorderedlist: [ "Bulleted List", ["buttons.gif", 0, 9], false, function(e) {e.execCommand("insertunorderedlist");} ],
				insertorderedlist: [ "Ordered List", ["buttons.gif", 1, 0], false, function(e) {e.execCommand("insertorderedlist");} ],
				superscript: [ "Superscript", ["buttons.gif", 1, 1], false, function(e) {e.execCommand("superscript");} ],
				subscript: [ "Subscript", ["buttons.gif", 1, 2], false, function(e) {e.execCommand("subscript");} ],
				outdent: [ "Outdent", ["buttons.gif", 1, 3], false, function(e) {e.execCommand("outdent");} ],
				indent: [ "Indent", ["buttons.gif", 1, 4], false, function(e) {e.execCommand("indent");} ],
				htmlmode: [ "Toggle HTML Source", ["buttons.gif", 1, 5], true, function(e) {e.execCommand("htmlmode");} ],
    		inserttable: [ "Insert Table", ["buttons.gif", 1, 6], false, function(e) {e.execCommand("inserttable");} ],
				tableprop: ["Table properties", ["buttons.gif", 1, 7], false, function(e) {e.execCommand("table",null,"table-prop");} ],
				rowprop: ["Row properties", ["buttons.gif", 1, 8], false, function(e) {e.execCommand("table",null,"row-prop");} ],
				rowinsertabove: ["Insert row before", ["buttons.gif", 1, 9], false, function(e) {e.execCommand("table",null,"row-insert-above");} ],
				rowinsertunder: ["Insert row after", ["buttons.gif", 2, 0], false, function(e) {e.execCommand("table",null,"row-insert-under");} ],
				rowdelete: ["Delete row", ["buttons.gif", 2, 1], false, function(e) {e.execCommand("table",null,"row-delete");} ],
				rowsplit: ["Split row", ["buttons.gif", 2, 2], false, function(e) {e.execCommand("table",null,"row-split");} ], //td[rowSpan!=1]
				colinsertbefore: ["Insert column before", ["buttons.gif", 2, 3], false, function(e) {e.execCommand("table",null,"col-insert-before");} ],
				colinsertafter: ["Insert column after", ["buttons.gif", 2, 4], false, function(e) {e.execCommand("table",null,"col-insert-after");} ],
				coldelete: ["Delete column", ["buttons.gif", 2, 5], false, function(e) {e.execCommand("table",null,"col-delete");} ],
				colsplit: ["Split column", ["buttons.gif", 2, 6], false, function(e) {e.execCommand("table",null,"col-split");} ], //td[colSpan!=1]
				cellprop:	["Cell properties", ["buttons.gif", 2, 7], false, function(e) {e.execCommand("table",null,"cell-prop");} ],
				cellinsertbefore:	["Insert cell before", ["buttons.gif", 2, 8], false, function(e) {e.execCommand("table",null,"cell-insert-before");} ],
				cellinsertafter:	["Insert cell after", ["buttons.gif", 2, 9], false, function(e) {e.execCommand("table",null,"cell-insert-after");} ],
				celldelete:	["Delete cell", ["buttons.gif", 3, 0], false, function(e) {e.execCommand("table",null,"cell-delete");} ],
				cellmerge:	["Merge cells", ["buttons.gif", 3, 1], false, function(e) {e.execCommand("table",null,"cell-merge");} ],
				cellsplit:	["Split cell", ["buttons.gif", 3, 2], false, function(e) {e.execCommand("table",null,"cell-split");} ], //td[colSpan!=1,rowSpan!=1]
    		toggleborders: [ "Toggle Borders", ["buttons.gif",3,3], false, function(e) {e.execCommand("toggleborders");} ]
			};

			// Config ends
			this.baseURL = document.URL;
			if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/)) this.baseURL = RegExp.$1 + "/";

			// initialize tooltips and generate correct image path
			for (var i in this.btnList) {
				var btn = this.btnList[i];
				if(typeof btn[1] != 'string') {
					btn[1][0] = _editor_url + this.imgFolder + btn[1][0];
				}  else {
					btn[1] = _editor_url + this.imgFolder + btn[1];
				}
			}

			this._rtf = null;
			this._textArea = textarea;
			this._editMode = "wysiwyg";
			this.plugins = {};
			this._timerToolbar = null;
			this._timerUndo = null;
			this._undoQueue = new Array(this.undoSteps);
			this._undoPos = -1;
			this._mdoc = document; // cache the document, we need it in plugins
			this.doctype = '';

			// register our table operations plugin
			this._tableOperations = new TableOperations(this);
		}
	};

	// cache some regexps
	RTF.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig;
	RTF.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i;
	RTF.RE_head    = /<head>((.|\n)*?)<\/head>/i;
	RTF.RE_body    = /<body>((.|\n)*?)<\/body>/i;

	RTF.replace = function(id, config) {
		var ta = RTF.getElementById("textarea", id);
		return ta ? (new RTF(ta, config)).generate() : null;
	};

// Creates the toolbar and appends it to the _rtf
RTF.prototype._createToolbar = function () {
	var editor = this;	// to access this in nested functions

	var toolbar = document.createElement("div");
	this._toolbar = toolbar;
	toolbar.className = "toolbar";
	toolbar.unselectable = "1";
	var tb_row = null;
	var tb_objects = new Object();
	this._toolbarObjects = tb_objects;

	// creates a new line in the toolbar
	function newLine() {
		var table = document.createElement("table");
		table.border = "0px";
		table.cellSpacing = "0px";
		table.cellPadding = "0px";
		toolbar.appendChild(table);
		// TBODY is required for IE, otherwise you don't see anything
		// in the TABLE.
		var tb_body = document.createElement("tbody");
		table.appendChild(tb_body);
		tb_row = document.createElement("tr");
		tb_body.appendChild(tb_row);
	}; // END of function: newLine

	newLine();

	function setButtonStatus(id, newval) {
		var oldval = this[id];
		var el = this.element;
		if (oldval != newval) {
			switch (id) {
				case "enabled":
					if (newval) {
						RTF._removeClass(el, "buttonDisabled");
						el.disabled = false;
					} else {
						RTF._addClass(el, "buttonDisabled");
						el.disabled = true;
					}
					break;
				case "active":
					if (newval) {
						RTF._addClass(el, "buttonPressed");
					} else {
						RTF._removeClass(el, "buttonPressed");
					}
					break;
			}
			this[id] = newval;
		}
	}; // END of function: setButtonStatus

	// appends a new button to toolbar
	function createButton(txt) {
		// the element that will be created
		var el = null;
		var btn = null;
		switch (txt) {
			case "separator":
				el = document.createElement("div");
				el.className = "separator";
				break;
			case "space":
				el = document.createElement("div");
				el.className = "space";
				break;
			case "linebreak":
				newLine();
				return false;
			case "textindicator":
				el = document.createElement("div");
				el.appendChild(document.createTextNode("A"));
				el.className = "indicator";
				el.title = RTF.tooltips.textindicator;
				var obj = {
					name	: txt, // the button name (i.e. 'bold')
					element : el, // the UI element (DIV)
					enabled : true, // is it enabled?
					active	: false, // is it pressed?
					text	: false, // enabled in text mode?
					cmd	: "textindicator", // the command ID
					state	: setButtonStatus // for changing state
				};
				tb_objects[txt] = obj;
				break;
			default:
				btn = editor.btnList[txt];
		}
		if (!el && btn) {
      el = document.createElement("a");//("div");
      el.style.display = 'block';
      el.href = 'javascript:void(0)';
      el.style.textDecoration = 'none';
      el.title = btn[0];
      el.className = "button";
			// let's just pretend we have a button object, and
			// assign all the needed information to it.
			var obj = {
				name	: txt, // the button name (i.e. 'bold')
				element : el, // the UI element (DIV)
				enabled : true, // is it enabled?
				active	: false, // is it pressed?
				text	: btn[2], // enabled in text mode?
				cmd	: btn[3], // the command ID
				state	: setButtonStatus, // for changing state
				context : btn[4] || null // enabled in a certain context?
			};
			tb_objects[txt] = obj;
			// handlers to emulate nice flat toolbar buttons
			RTF._addEvent(el, "mouseover", function () {if (obj.enabled) {RTF._addClass(el, "buttonHover");}});
			RTF._addEvent(el, "mouseout", function () {
				if (obj.enabled) with (RTF) {
					_removeClass(el, "buttonHover");
					_removeClass(el, "buttonActive");
					(obj.active) && _addClass(el, "buttonPressed");
				}
			});
			RTF._addEvent(el, "mousedown", function (ev) {
				if (obj.enabled) with (RTF) {
					_addClass(el, "buttonActive");
					_removeClass(el, "buttonPressed");
					_stopEvent(pxAPI.is_ie ? window.event : ev);
				}
			});
			// when clicked, do the following:
			RTF._addEvent(el, "click", function (ev) {
				if (obj.enabled) with (RTF) {
					_removeClass(el, "buttonActive");
					_removeClass(el, "buttonHover");
					obj.cmd(editor, obj.name, obj);
					_stopEvent(pxAPI.is_ie ? window.event : ev);
				}
			});

      var i_contain = RTF.makeBtnImg(btn[1]);
      var img = i_contain.firstChild;
      el.appendChild(i_contain);

      obj.imgel = img;
      obj.swapImage = function(newimg){
        if(typeof newimg != 'string'){
          img.src = newimg[0];
          img.style.position = 'relative';
          img.style.top = newimg[1] ? ('-' + (20 * (newimg[1] + 1)) + 'px') : '-20px';
          img.style.left = newimg[2] ? ('-' + (20 * (newimg[2] + 1)) + 'px') : '-20px';
        }else{
          obj.img = newimg;
          img.style.top = '0px';
          img.style.left = '0px';
        }
      }
		}
		if (el) {
			var tb_cell = document.createElement("td");
			tb_row.appendChild(tb_cell);
			tb_cell.appendChild(el);
		} else {
			alert("FIXME: Unknown toolbar item: " + txt);
		}
		return el;
	};

	var first = true;
	for (var i in this.toolbar) {
		if (!first) {
			createButton("linebreak");
		} else {
			first = false;
		}
		var group = this.toolbar[i];
		for (var j in group) {
			if(typeof group[j] == 'string'){
				var code = group[j];
				if (/^([IT])\[(.*?)\]/.test(code)) {
					// special case, create text label
					var l7ed = RegExp.$1 == "I"; // localized?
					var label = RegExp.$2;
					var tb_cell = document.createElement("td");
					tb_row.appendChild(tb_cell);
					tb_cell.className = "label";
					tb_cell.innerHTML = label;
				} else {
					createButton(code);
				}
			}
		}
	}

	this._rtf.appendChild(toolbar);
};

use_clone_img = false;
RTF.makeBtnImg = function(imgDef, doc)
{
  if(!doc) doc = document;

  if(!doc._rtfImgCache) {
    doc._rtfImgCache = { };
  }

  var i_contain = null;
  if(pxAPI.is_ie && ((!doc.compatMode) || (doc.compatMode && doc.compatMode == "BackCompat"))) {
    i_contain = doc.createElement('span');
  } else {
    i_contain = doc.createElement('div');
    i_contain.style.position = 'relative';
  }

  i_contain.style.overflow = 'hidden';
  i_contain.style.width = "20px";
  i_contain.style.height = "20px";
  i_contain.className    = 'buttonImageContainer';

  var img = null;

  if(typeof imgDef == 'string') {
    if(doc._rtfImgCache[imgDef]) {
      img = doc._rtfImgCache[imgDef].cloneNode();
    } else {
      img = doc.createElement("img");
      img.src = imgDef;
      img.style.width = "20px";
      img.style.height = "20px";
      if(use_clone_img)
        doc._rtfImgCache[imgDef] = img.cloneNode();
    }
  } else {
    if(doc._rtfImgCache[imgDef[0]]) {
      img = doc._rtfImgCache[imgDef[0]].cloneNode();
    } else {
      img = doc.createElement("img");
      img.src = imgDef[0];
      img.width = '200';
      img.height = '200';
      img.style.position = 'relative';
      if(use_clone_img) doc._rtfImgCache[imgDef[0]] = img.cloneNode();
    }
    img.style.top = (imgDef[1]==0) ? '0' : ('-' + (20 * (imgDef[1])) + 'px');
    img.style.left = (imgDef[2]==0) ? '0' : ('-' + (20 * (imgDef[2])) + 'px');
  }
  i_contain.appendChild(img);
  return i_contain;
}

RTF.prototype._createStatusBar = function() {
	var statusbar = document.createElement("div");
	statusbar.className = "statusBar";
	this._rtf.appendChild(statusbar);
	this._statusBar = statusbar;
	div = document.createElement("span");
	div.className = "statusBarTree";
	div.innerHTML = "Path" + ": ";
	this._statusBarTree = div;
	this._statusBar.appendChild(div);
};

// Creates the RTF object and replaces the textarea with it.
RTF.prototype.generate = function () {
	var editor = this;	// we'll need "this" in some nested functions
	// get the textarea
	var textarea = this._textArea;
	if (typeof textarea == "string") {
		// it's not element but ID
		this._textArea = textarea = RTF.getElementById("textarea", textarea);
	}
	this._ta_size = {
		w: textarea.offsetWidth,
		h: textarea.offsetHeight
	};
	textarea.style.display = "none";

	// create the editor framework
	var rtf = document.createElement("div");
	rtf.className = "rtf";
	this._rtf = rtf;

	// insert the editor before the textarea.
	textarea.parentNode.insertBefore(rtf, textarea);

	if (textarea.form) {
		// we have a form, on submit get the RTF content and
		// update original textarea.
		var f = textarea.form;
		if (typeof f.onsubmit == "function") {
			var funcref = f.onsubmit;
			if (typeof f.__msh_prevOnSubmit == "undefined") {
				f.__msh_prevOnSubmit = [];
			}
			f.__msh_prevOnSubmit.push(funcref);
		}
		f.onsubmit = function() {
			editor._textArea.value = RTF.safeSave(editor.getHTML());
			var a = this.__msh_prevOnSubmit;
			// call previous submit methods if they were there.
			if (typeof a != "undefined") {
				for (var i in a) {
					a[i]();
				}
			}
		};
	}

	// add a handler for the "back/forward" case -- on body.unload we save
	// the HTML content into the original textarea.
	window.onunload = function() {
		editor._textArea.value = RTF.safeSave(editor.getHTML());
	};

	// creates & appends the toolbar
	this._createToolbar();

	// create the IFRAME
	var iframe = document.createElement("iframe");
	rtf.appendChild(iframe);

	this._iframe = iframe;

	this._createStatusBar();

	if (!pxAPI.is_ie) iframe.style.borderWidth = "1px";

	// size the IFRAME according to user's prefs or initial textarea
	var height = this._ta_size.h + "px";
	height = parseInt(height);
	var width = this._ta_size.w + "px";
	width = parseInt(width);

	if (!pxAPI.is_ie) { height -= 2; width -= 2; }

	iframe.style.width = "100%"; //width + "px";
	if (height < 0) height = 0;
	iframe.style.height = height + "px";

	textarea.style.width = "94%"; // iframe.style.width;
 	textarea.style.height = iframe.style.height;

	// IMPORTANT: we have to allow Mozilla a short time to recognize the
	// new frame.  Otherwise we get a stupid exception.
	setTimeout(initIframe, 100);

	function initIframe() {
		var doc = editor._iframe.contentWindow.document;
		if (!doc) {
			if (pxAPI.is_gecko) {
				setTimeout(initIframe, 100);
				return false;
			} else {
				alert("ERROR: IFRAME can't be initialized.");
			}
		}
		if (pxAPI.is_gecko) { doc.designMode = "on"; }
		editor._doc = doc;
		doc.open();
		doc.write("<html><head><style>" + editor.pageStyle + "</style></head><body>" + editor._textArea.value + "</body></html>");
		doc.close();

		if (pxAPI.is_ie) doc.body.contentEditable = true;
		editor.focusEditor();

		// intercept some events; for updating the toolbar & keyboard handlers
		RTF._addEvents
			(doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
			 function (event) {
				 return editor._editorEvent(pxAPI.is_ie ? editor._iframe.contentWindow.event : event);
			 });

		// check if any plugins have registered refresh handlers
		for (var i in editor.plugins) {
			var plugin = editor.plugins[i].instance;
			if (typeof plugin.onGenerate == "function")
				plugin.onGenerate();
		}

		setTimeout(function() { editor.updateToolbar(); }, 250);
		if (typeof editor.onGenerate == "function") editor.onGenerate();
	};
};

// Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no
// parameter was passed this function toggles between modes.
RTF.prototype.setMode = function(mode) {
	if (typeof mode == "undefined") mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
	switch (mode) {
		case "textmode":
			this._textArea.value = this.getHTML();
			this._iframe.style.display = "none";
			this._textArea.style.display = "block";
			this._statusBar.innerHTML = "You are in TEXT MODE. Use the [<>] button to switch back to WYSIWYG.";
			break;
		case "wysiwyg":
			if (pxAPI.is_gecko) { try { this._doc.designMode = "off"; } catch(e) {}; }
			this._doc.body.innerHTML = this.getHTML();
			this._iframe.style.display = "block";
			this._textArea.style.display = "none";
			if (pxAPI.is_gecko) { try { this._doc.designMode = "on"; } catch(e) {}; }
				this._statusBar.innerHTML = '';
				this._statusBar.appendChild(document.createTextNode("Path" + ": "));
				this._statusBar.appendChild(this._statusBarTree);
			break;
		default:
		alert("Mode <" + mode + "> not defined!"); return false;
	}
	this._editMode = mode;
	this.focusEditor();
};

/***************************************************
 *  Category: EDITOR UTILITIES
 ***************************************************/

// The following function is a slight variation of the word cleaner code posted
// by Weeezl (user @ InteractiveTools forums).
RTF.prototype._wordClean = function() {
	var D = this.getInnerHTML();
	var oldlen = 0;
	if (D.indexOf('class=Mso') >= 0) {

		// make one line
		D = D.replace(/\r\n/g, ' ').
			replace(/\n/g, ' ').
			replace(/\r/g, ' ').
			replace(/\&nbsp\;/g,' ');

		// keep tags, strip attributes
		D = D.replace(/ class=[^\s|>]*/gi,'').
			//replace(/<p [^>]*TEXT-ALIGN: justify[^>]*>/gi,'<p align="justify">').
			replace(/ style=\"[^>]*\"/gi,'').
			replace(/ align=[^\s|>]*/gi,'');

		//clean up tags
		D = D.replace(/<b [^>]*>/gi,'<b>').
			replace(/<i [^>]*>/gi,'<i>').
			replace(/<li [^>]*>/gi,'<li>').
			replace(/<ul [^>]*>/gi,'<ul>');

		// replace outdated tags
		D = D.replace(/<b>/gi,'<strong>').
			replace(/<\/b>/gi,'</strong>');

		// mozilla doesn't like <em> tags
		D = D.replace(/<em>/gi,'<i>').
			replace(/<\/em>/gi,'</i>');

		// kill unwanted tags
		D = D.replace(/<\?xml:[^>]*>/g, '').       // Word xml
			replace(/<\/?st1:[^>]*>/g,'').     // Word SmartTags
			replace(/<\/?[a-z]\:[^>]*>/g,'').  // All other funny Word non-HTML stuff
			replace(/<\/?font[^>]*>/gi,'').    // Disable if you want to keep font formatting
			replace(/<\/?span[^>]*>/gi,' ').
			replace(/<\/?div[^>]*>/gi,' ').
			replace(/<\/?pre[^>]*>/gi,' ').
			replace(/<\/?h[1-6][^>]*>/gi,' ');

		//remove empty tags
		// D = D.replace(/<strong><\/strong>/gi,'').
		// replace(/<i><\/i>/gi,'').
		//replace(/<P[^>]*><\/P>/gi,'');

		// nuke double tags
		oldlen = D.length + 1;
		while(oldlen > D.length) {
			oldlen = D.length;
			// join us now and free the tags, we'll be free hackers, we'll be free... ;-)
			D = D.replace(/<([a-z][a-z]*)> *<\/\1>/gi,' ').
				replace(/<([a-z][a-z]*)> *<([a-z][^>]*)> *<\/\1>/gi,'<$2>');
		}

		D = D.replace(/<([a-z][a-z]*)><\1>/gi,'<$1>').
			replace(/<\/([a-z][a-z]*)><\/\1>/gi,'<\/$1>');

		// nuke double spaces
		D = D.replace(/  */gi,' ');

		this.setHTML(D);
		this.updateToolbar();
	}
};

RTF.prototype._unlink = function() {
	a = this.getParentElement();
	var sel = this._getSelection();
	var range = this._createRange(sel);
	a = this.getParentElement(range.startContainer);

	a.parentNode.innerHTML=a.innerHTML;

	this.selectNodeContents(range.startContainer);
	this.updateToolbar();
}

RTF.prototype.forceRedraw = function() {
	this._doc.body.style.visibility = "hidden";
	this._doc.body.style.visibility = "visible";
};

// focuses the iframe window.  returns a reference to the editor document.
RTF.prototype.focusEditor = function() {
	switch (this._editMode) {
	    case "wysiwyg" : this._iframe.contentWindow.focus(); break;
	    case "textmode": this._textArea.focus(); break;
	    default	   : alert("ERROR: mode " + this._editMode + " is not defined");
	}
	return this._doc;
};

RTF.prototype.undo = function() {
	if (this._undoPos > 0) {
		var txt = this._undoQueue[--this._undoPos];
		if (txt) this.setHTML(txt);
		else ++this._undoPos;
	}
};

RTF.prototype.redo = function() {
	if (this._undoPos < this._undoQueue.length - 1) {
		var txt = this._undoQueue[++this._undoPos];
		if (txt) this.setHTML(txt);
		else --this._undoPos;
	}
};

// updates enabled/disable/active state of the toolbar elements
RTF.prototype.updateToolbar = function(noStatus) {
	var doc = this._doc;
	var text = (this._editMode == "textmode");
	var ancestors = null;
	if (!text) {
		ancestors = this.getAllAncestors();
		if (!noStatus) {
			this._statusBarTree.innerHTML = "Path" + ": "; // clear
			for (var i = ancestors.length; --i >= 0;) {
				var el = ancestors[i];
				if (!el) continue;
				var a = document.createElement("a");
				a.href = "#";
				a.el = el;
				a.editor = this;
				a.onclick = function() {
					this.blur();
					this.editor.selectNodeContents(this.el);
					this.editor.updateToolbar(true);
					return false;
				};
				a.oncontextmenu = function() {
					// TODO: add context menu here
					this.blur();
					var info = "Inline style:\n\n";
					info += this.el.style.cssText.split(/;\s*/).join(";\n");
					alert(info);
					return false;
				};
				var txt = el.tagName.toLowerCase();
				a.title = el.style.cssText;
				if (el.id) txt += "#" + el.id;
				if (el.className) txt += "." + el.className;
				a.appendChild(document.createTextNode(txt));
				this._statusBarTree.appendChild(a);
				if (i != 0) this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
			}
		}
	}
	for (var i in this._toolbarObjects) {
		var btn = this._toolbarObjects[i];
		var cmd = i;
		var inContext = true;
		if (btn.context && !text) {
			inContext = false;
			var context = btn.context;
			var attrs = [];
			if (/(.*)\[(.*?)\]/.test(context)) {
				context = RegExp.$1;
				attrs = RegExp.$2.split(",");
			}
			context = context.toLowerCase();
			var match = (context == "*");
			for (var k in ancestors) {
				if (!ancestors[k]) {
					// the impossible really happens.
					continue;
				}
				if (match || (ancestors[k].tagName.toLowerCase() == context)) {
					inContext = true;
					for (var ka in attrs) {
						if (!eval("ancestors[k]." + attrs[ka])) {
							inContext = false;
							break;
						}
					}
					if (inContext) {
						break;
					}
				}
			}
		}
		btn.state("enabled", (!text || btn.text) && inContext);
		if (typeof cmd == "function") {
			continue;
		}
		switch (cmd) {
		    case "textindicator":
			if (!text) {
				try {with (btn.element.style) {
					backgroundColor = RTF._makeColor(
						doc.queryCommandValue(pxAPI.is_ie ? "backcolor" : "hilitecolor"));
					if (/transparent/i.test(backgroundColor)) {
						// Mozilla
						backgroundColor = RTF._makeColor(doc.queryCommandValue("backcolor"));
					}
					color = RTF._makeColor(doc.queryCommandValue("forecolor"));
					fontFamily = doc.queryCommandValue("fontname");
					fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
					fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
				}} catch (e) {
					// alert(e + "\n\n" + cmd);
				}
			}
			break;
		    case "htmlmode": btn.state("active", text); break;
		    default:
			try {
				btn.state("active", (!text && doc.queryCommandState(cmd)));
			} catch (e) {}
		}
	}
	// check if any plugins have registered refresh handlers
	for (var i in this.plugins) {
		var plugin = this.plugins[i].instance;
		if (typeof plugin.onUpdateToolbar == "function")
			plugin.onUpdateToolbar();
	}
};

/** Returns a node after which we can insert other nodes, in the current
 * selection.  The selection is removed.  It splits a text node, if needed.
 */
RTF.prototype.insertNodeAtSelection = function(toBeInserted) {
	if (!pxAPI.is_ie) {
		var sel = this._getSelection();
		var range = this._createRange(sel);
		// remove the current selection
		sel.removeAllRanges();
		range.deleteContents();
		var node = range.startContainer;
		var pos = range.startOffset;
		switch (node.nodeType) {
		    case 3: // Node.TEXT_NODE
			// we have to split it at the caret position.
			if (toBeInserted.nodeType == 3) {
				// do optimized insertion
				node.insertData(pos, toBeInserted.data);
				range = this._createRange();
				range.setEnd(node, pos + toBeInserted.length);
				range.setStart(node, pos + toBeInserted.length);
				sel.addRange(range);
			} else {
				node = node.splitText(pos);
				var selnode = toBeInserted;
				if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
					selnode = selnode.firstChild;
				}
				node.parentNode.insertBefore(toBeInserted, node);
				this.selectNodeContents(selnode);
				this.updateToolbar();
			}
			break;
		    case 1: // Node.ELEMENT_NODE
			var selnode = toBeInserted;
			if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
				selnode = selnode.firstChild;
			}
			node.insertBefore(toBeInserted, node.childNodes[pos]);
			this.selectNodeContents(selnode);
			this.updateToolbar();
			break;
		}
	} else {
		return null;	// this function not yet used for IE <FIXME>
	}
};

// Returns the deepest node that contains both endpoints of the selection.
RTF.prototype.getParentElement = function() {
	var sel = this._getSelection();
	var range = this._createRange(sel);
	if (pxAPI.is_ie) {
		switch (sel.type) {
		    case "Text":
		    case "None":
			return range.parentElement();
		    case "Control":
			return range.item(0);
		    default:
			return this._doc.body;
		}
	} else try {
		var p = range.commonAncestorContainer;
		if (!range.collapsed && range.startContainer == range.endContainer &&
		    range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes())
			p = range.startContainer.childNodes[range.startOffset];
		while (p.nodeType == 3) {
			p = p.parentNode;
		}
		return p;
	} catch (e) {
		return null;
	}
};

// Returns an array with all the ancestor nodes of the selection.
RTF.prototype.getAllAncestors = function() {
	var p = this.getParentElement();
	var a = [];
	while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
		a.push(p);
		p = p.parentNode;
	}
	a.push(this._doc.body);
	return a;
};

// Selects the contents inside the given node
RTF.prototype.selectNodeContents = function(node, pos) {
	this.focusEditor();
	this.forceRedraw();
	var range;
	var collapsed = (typeof pos != "undefined");
	if (pxAPI.is_ie) {
		range = this._doc.body.createTextRange();
		range.moveToElementText(node);
		(collapsed) && range.collapse(pos);
		range.select();
	} else {
		var sel = this._getSelection();
		range = this._doc.createRange();
		range.selectNodeContents(node);
		(collapsed) && range.collapse(pos);
		sel.removeAllRanges();
		sel.addRange(range);
	}
};

/** Call this function to insert HTML code at the current position.  It deletes
 * the selection, if any.
 */
RTF.prototype.insertHTML = function(html) {
	var sel = this._getSelection();
	var range = this._createRange(sel);
	if (pxAPI.is_ie) {
		range.pasteHTML(html);
	} else {
		// construct a new document fragment with the given HTML
		var fragment = this._doc.createDocumentFragment();
		var div = this._doc.createElement("div");
		div.innerHTML = html;
		while (div.firstChild) {
			// the following call also removes the node from div
			fragment.appendChild(div.firstChild);
		}
		// this also removes the selection
		var node = this.insertNodeAtSelection(fragment);
	}
};

/**
 *  Call this function to surround the existing HTML code in the selection with
 *  your tags.  FIXME: buggy!  This function will be deprecated "soon".
 */
RTF.prototype.surroundHTML = function(startTag, endTag) {
	var html = this.getSelectedHTML();
	// the following also deletes the selection
	this.insertHTML(startTag + html + endTag);
};

/// Retrieve the selected block
RTF.prototype.getSelectedHTML = function() {
	var sel = this._getSelection();
	var range = this._createRange(sel);
	var existing = null;
	if (pxAPI.is_ie) {
		existing = range.htmlText;
	} else {
		existing = RTF.getHTML(range.cloneContents(), false, this);
	}
	return existing;
};

/// Return true if we have some selection
RTF.prototype.hasSelectedText = function() {
	// FIXME: come _on_ mishoo, you can do better than this ;-)
	return this.getSelectedHTML() != '';
};

/***************************************************
 *  Category: EVENT HANDLERS
 ***************************************************/

// the execCommand function (intercepts some commands and replaces them with
// our own implementation)
RTF.prototype.execCommand = function(cmdID, UI, param) {
	var editor = this;	// for nested functions
	this.focusEditor();
	cmdID = cmdID.toLowerCase();
	switch (cmdID) {
		case "htmlmode" : this.setMode(); break;
		case "createlink": this._createLink(); break;
		case "undo":
		case "redo":
			this._doc.execCommand(cmdID, UI, param);
			break;
		case "table":
			this._tableOperations.buttonPress(editor, param); break;
		case "inserttable": this._insertTable(); break;
		case "insertimage": this._insertImage(); break;
		case "killword": this._wordClean(); break;
		case "toggleborders": this._toggleBorders(); break;
		case "cut":
		case "copy":
		case "paste":
			try {
				this._wordClean();
				this._doc.execCommand(cmdID, UI, param);
			} catch (e) {
				if (pxAPI.is_gecko) {
					if (confirm("Unprivileged scripts cannot access Cut/Copy/Paste programatically " +
						"for security reasons.  Click OK to see a technical note at mozilla.org " +
						"which shows you how to allow a script to access the clipboard."))
						window.open("http://mozilla.org/editor/midasdemo/securityprefs.html");
				}
			}
			break;
		case "unlink":
			// fix up unlink in Mozilla to unlink the link and not just the selection
			if (pxAPI.is_gecko) this._unlink();
			break;
		default: this._doc.execCommand(cmdID, UI, param);
	}
	this.updateToolbar();
	return false;
};

/** A generic event handler for things that happen in the IFRAME's document.
 * This function also handles key bindings. */
RTF.prototype._editorEvent = function(ev) {
	var editor = this;
	var keyEvent = (pxAPI.is_ie && ev.type == "keydown") || (ev.type == "keypress");
	if (keyEvent) {
		for (var i in editor.plugins) {
			var plugin = editor.plugins[i].instance;
			if (typeof plugin.onKeyPress == "function") plugin.onKeyPress(ev);
		}
	}
	if (keyEvent && ev.ctrlKey) {
		var sel = null;
		var range = null;
		var key = String.fromCharCode(pxAPI.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
		var cmd = null;
		var value = null;
		switch (key) {
		    case 'a':
			if (!pxAPI.is_ie) {
				// KEY select all
				sel = this._getSelection();
				sel.removeAllRanges();
				range = this._createRange();
				range.selectNodeContents(this._doc.body);
				sel.addRange(range);
				RTF._stopEvent(ev);
			}
			break;

			// simple key commands follow

		    case 'b': cmd = "bold"; break;
		    case 'i': cmd = "italic"; break;
		    case 'u': cmd = "underline"; break;
		    case 'z': cmd = "undo"; break;
		    case 'y': cmd = "redo"; break;
		    case 'v': cmd = "paste"; break;

		    case '0': cmd = "killword"; break;

			// headings
		    case '4':
		    case '5':
		    case '6':
			cmd = "formatblock";
			value = "h" + key;
			if (pxAPI.is_ie) {
				value = "<" + value + ">";
			}
			break;
		}
		if (cmd) {
			// execute simple command
			this.execCommand(cmd, false, value);
			RTF._stopEvent(ev);
		}
	}
	// update the toolbar state after some time
	if (editor._timerToolbar) {
		clearTimeout(editor._timerToolbar);
	}
	editor._timerToolbar = setTimeout(function() {
		editor.updateToolbar();
		editor._timerToolbar = null;
	}, 50);
};

// retrieve the HTML
RTF.prototype.getHTML = function() {
	this._wordClean();
	switch (this._editMode) {
		case "wysiwyg" : return RTF.getHTML(this._doc.body, false, this); break;
		case "textmode" : return this._textArea.value; break;
		default : alert("Mode <" + mode + "> not defined!");
	}
	return false;
};

// retrieve the HTML (fastest version, but uses innerHTML)
RTF.prototype.getInnerHTML = function() {
	switch (this._editMode) {
		case "wysiwyg" : return this._doc.body.innerHTML; break;
		case "textmode" : return this._textArea.value; break;
		default : alert("Mode <" + mode + "> not defined!");
	}
	return false;
};

// completely change the HTML inside
RTF.prototype.setHTML = function(html) {
	switch (this._editMode) {
		case "wysiwyg" : this._doc.body.innerHTML = html; break;
		case "textmode" : this._textArea.value = html; break;
		default : alert("Mode <" + mode + "> not defined!");
	}
	return false;
};

RTF.prototype.setDoctype = function(doctype) {
	this.doctype = doctype;
};

/***************************************************
 *  Category: UTILITY FUNCTIONS
 ***************************************************/

// variable used to pass the object to the popup editor window.
RTF._object = null;

// function that returns a clone of the given object
RTF.cloneObject = function(obj) {
	var newObj = new Object;

	// check for array objects
	if (obj.constructor.toString().indexOf("function Array(") == 1) {
		newObj = obj.constructor();
	}

	// check for function objects (as usual, IE is fucked up)
	if (obj.constructor.toString().indexOf("function Function(") == 1) {
		newObj = obj; // just copy reference to it
	} else for (var n in obj) {
		var node = obj[n];
		if (typeof node == 'object') { newObj[n] = RTF.cloneObject(node); }
		else                         { newObj[n] = node; }
	}

	return newObj;
};

// FIXME!!! this should return false for IE < 5.5
RTF.checkSupportedBrowser = function() {
	if (pxAPI.is_gecko) {
		if (navigator.productSub < 20021201) {
			alert("You need at least Mozilla-1.3 Alpha.\n" +
			      "Sorry, your Gecko is not supported.");
			return false;
		}
		if (navigator.productSub < 20030210) {
			alert("Mozilla < 1.3 Beta is not supported!\n" +
			      "I'll try, though, but it might not work.");
		}
	}
	return pxAPI.is_gecko || pxAPI.is_ie;
};

// selection & ranges

// returns the current selection object
RTF.prototype._getSelection = function() {
	if (pxAPI.is_ie) {
		return this._doc.selection;
	} else {
		return this._iframe.contentWindow.getSelection();
	}
};

// returns a range for the current selection
RTF.prototype._createRange = function(sel) {
	if (pxAPI.is_ie) {
		return sel.createRange();
	} else {
		this.focusEditor();
		if (typeof sel != "undefined") {
			try {
				return sel.getRangeAt(0);
			} catch(e) {
				return this._doc.createRange();
			}
		} else {
			return this._doc.createRange();
		}
	}
};

// event handling

RTF._addEvent = function(el, evname, func) {
	if (pxAPI.is_ie) {
		el.attachEvent("on" + evname, func);
	} else {
		el.addEventListener(evname, func, true);
	}
};

RTF._addEvents = function(el, evs, func) {
	for (var i in evs) {
		RTF._addEvent(el, evs[i], func);
	}
};

RTF._removeEvent = function(el, evname, func) {
	if (pxAPI.is_ie) {
		el.detachEvent("on" + evname, func);
	} else {
		el.removeEventListener(evname, func, true);
	}
};

RTF._removeEvents = function(el, evs, func) {
	for (var i in evs) {
		RTF._removeEvent(el, evs[i], func);
	}
};

RTF._stopEvent = function(ev) {
	if (pxAPI.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
};

RTF._removeClass = function(el, className) {
	if (!(el && el.className)) {
		return;
	}
	var cls = el.className.split(" ");
	var ar = new Array();
	for (var i = cls.length; i > 0;) {
		if (cls[--i] != className) {
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

RTF._addClass = function(el, className) {
	// remove the class first, if already there
	RTF._removeClass(el, className);
	el.className += " " + className;
};

RTF._hasClass = function(el, className) {
	if (!(el && el.className)) {
		return false;
	}
	var cls = el.className.split(" ");
	for (var i = cls.length; i > 0;) {
		if (cls[--i] == className) {
			return true;
		}
	}
	return false;
};

RTF.isBlockElement = function(el) {
	var blockTags = " body form textarea fieldset ul ol dl li div " +
		"p h1 h2 h3 h4 h5 h6 quote pre table thead " +
		"tbody tfoot tr td iframe address ";
	return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};

RTF.needsClosingTag = function(el) {
	var closingTags = " head script style div span tr td tbody table em strong font a title ";
	return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};

// performs HTML encoding of some given string
RTF.htmlEncode = function(str) {
	// first change all the html encodes to the correct characters
	str = str.replace(/&/ig, "&amp;");
	str = str.replace(/</ig, "&lt;");
	str = str.replace(/>/ig, "&gt;");
	// \x22 means '"' -- we use hex reprezentation so that we don't disturb
	// JS compressors (well, at least mine fails.. ;)
	str = str.replace(/\x22/ig, "&quot;");
	return str;
};

// performs HTML encoding of some given string
RTF.safeSave = function(str) {
	var h="0123456789ABCDEF";
	var a="";
	var b="";
	for(var i=128;i<256;i++){
		a=i%16;
		r = new RegExp("\\x"+h.charAt((i-a)/16)+h.charAt(a), "g");
		str=str.replace(r,"&#"+i+";");
	}
	return str;
};

// Retrieves the HTML code from the given node.	 This is a replacement for
// getting innerHTML, using standard DOM calls.
RTF.getHTML = function(root, outputRoot, editor) {
	var html = "";
	switch (root.nodeType) {
	    case 1: // Node.ELEMENT_NODE
	    case 11: // Node.DOCUMENT_FRAGMENT_NODE
		var closed;
		var i;
		var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : '';
		if (pxAPI.is_ie && root_tag == "head") {
			if (outputRoot)
				html += "<head>";
			// lowercasize
			var save_multiline = RegExp.multiline;
			RegExp.multiline = true;
			var txt = root.innerHTML.replace(RTF.RE_tagName, function(str, p1, p2) {
				return p1 + p2.toLowerCase();
			});
			RegExp.multiline = save_multiline;
			html += txt;
			if (outputRoot)
				html += "</head>";
			break;
		} else if (outputRoot) {
			closed = (!(root.hasChildNodes() || RTF.needsClosingTag(root)));
			html = "<" + root.tagName.toLowerCase();
			var attrs = root.attributes;
			for (i = 0; i < attrs.length; ++i) {
				var a = attrs.item(i);
				if (!a.specified) {
					continue;
				}
				var name = a.nodeName.toLowerCase();
				if (/_moz|contenteditable|_msh/.test(name)) {
					// avoid certain attributes
					continue;
				}
				var value;
				if (name != "style") {
					// IE5.5 reports 25 when cellSpacing is
					// 1; other values might be doomed too.
					// For this reason we extract the
					// values directly from the root node.
					// I'm starting to HATE JavaScript
					// development.  Browser differences
					// suck.
					//
					// Using Gecko the values of href and src are converted to absolute links
					// unless we get them using nodeValue()
					if (typeof root[a.nodeName] != "undefined" && name != "href" && name != "src") {
						value = root[a.nodeName];
					} else {
						value = a.nodeValue;
						// IE seems not willing to return the original values - it converts to absolute
						// links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href")
						// So we have to strip the baseurl manually -/
						if (pxAPI.is_ie && (name == "href" || name == "src")) {
							value = editor.stripBaseURL(value);
						}
					}
				} else { // IE fails to put style in attributes list
					// FIXME: cssText reported by IE is UPPERCASE
					value = root.style.cssText;
				}
				if (/(_moz|^$)/.test(value)) {
					// Mozilla reports some special tags
					// here; we don't need them.
					continue;
				}
				html += " " + name + '="' + value + '"';
			}
			html += closed ? " />" : ">";
		}
		for (i = root.firstChild; i; i = i.nextSibling) {
			html += RTF.getHTML(i, true, editor);
		}
		if (outputRoot && !closed) {
			html += "</" + root.tagName.toLowerCase() + ">";
		}
		break;
	    case 3: // Node.TEXT_NODE
			// If a text node is alone in an element and all spaces, replace it with an non breaking one
			// This partially undoes the damage done by moz, which translates '&nbsp;'s into spaces in the data element
			if ( !root.previousSibling && !root.nextSibling && root.data.match(/^\s*$/i) ) html = '&nbsp;';
			else html = RTF.htmlEncode(root.data);
		break;
	    case 8: // Node.COMMENT_NODE
			html = "<!--" + root.data + "-->";
		break;		// skip comments, for now.
	}
	return html;
};

RTF.prototype.stripBaseURL = function(string) {
	var baseurl = this.baseURL;

	// strip to last directory in case baseurl points to a file
	baseurl = baseurl.replace(/[^\/]+$/, '');
	var basere = new RegExp(baseurl);
	string = string.replace(basere, "");

	// strip host-part of URL which is added by MSIE to links relative to server root
	baseurl = baseurl.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1');
	basere = new RegExp(baseurl);
	return string.replace(basere, "");
};

// creates a rgb-style color from a number
RTF._makeColor = function(v) {
	if (typeof v != "number") {
		// already in rgb (hopefully); IE doesn't get here.
		return v;
	}
	// IE sends number; convert to rgb.
	var r = v & 0xFF;
	var g = (v >> 8) & 0xFF;
	var b = (v >> 16) & 0xFF;
	return "rgb(" + r + "," + g + "," + b + ")";
};

// returns hexadecimal color representation from a number or a rgb-style color.
RTF._colorToRgb = function(v) {
	if (!v)
		return '';

	// returns the hex representation of one byte (2 digits)
	function hex(d) {
		return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
	};

	if (typeof v == "number") {
		// we're talking to IE here
		var r = v & 0xFF;
		var g = (v >> 8) & 0xFF;
		var b = (v >> 16) & 0xFF;
		return "#" + hex(r) + hex(g) + hex(b);
	}

	if (v.substr(0, 3) == "rgb") {
		// in rgb(...) form -- Mozilla
		var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
		if (v.match(re)) {
			var r = parseInt(RegExp.$1);
			var g = parseInt(RegExp.$2);
			var b = parseInt(RegExp.$3);
			return "#" + hex(r) + hex(g) + hex(b);
		}
		// doesn't match RE?!  maybe uses percentages or float numbers
		// -- FIXME: not yet implemented.
		return null;
	}

	if (v.substr(0, 1) == "#") {
		// already hex rgb (hopefully :D )
		return v;
	}

	// if everything else fails ;)
	return null;
};

// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).

// receives an URL to the popup dialog and a function that receives one value;
// this function will get called after the dialog is closed, with the return
// value of the dialog.
RTF.prototype._popupDialog = function(url, action, init) {
	Dialog(this.popupURL(url), action, init);
};

RTF.prototype.imgURL = function(File) {
		return _editor_url + file;
};

RTF.prototype.popupURL = function(file) {
	return this.popupFolder + file;
};

RTF.getElementById = function(tag, id) {
	var el, i, objs = document.getElementsByTagName(tag);
	for (i = objs.length; --i >= 0 && (el = objs[i]);)
		if (el.id == id)
			return el;
	return null;
};

/***************************************************************************
 *		POPUP WINDOWS
 */

function PopupWin(editor, title, handler, initFunction) {
	this.editor = editor;
	this.handler = handler;
	var dlg = window.open("", "__ha_dialog", "toolbar=no,menubar=no,personalbar=no,width=600,height=600,left=20,top=40,scrollbars=no,resizable=no");
	this.window = dlg;
	var doc = dlg.document;
	this.doc = doc;
	var self = this;

	var base = document.baseURI || document.URL;
	if (base && base.match(/(.*)\/([^\/]+)/)) {
		base = RegExp.$1 + "/";
	}
	if (typeof _editor_url != "undefined" && !/^\//.test(_editor_url)) { base += _editor_url; } else { base = _editor_url; }
	if (!/\/$/.test(base)) base += '/';
	this.baseURL = base;

	doc.open();
	var html = "<html><head><title>" + title + "</title><style type='text/css'> \
			html{background:ButtonFace;font:76% Verdana,Arial,Helvetica,sans-serif;margin:0;padding:0;border:0} \
			body{color:ButtonText;font-size:0.9em;margin:0;padding:0;border:0} \
			table{font:1em Verdana,Arial,Helvetica,sans-serif} \
			select,input,button{font:1em Verdana,Arial,Helvetica,sans-serif} \
			button{width:7em;margin:2px} \
			table .label{text-align:right;width:8em} \
			#buttons{padding:2px;text-align:right} \
			fieldset div{padding:2px 8px} \
 \
			/* CSS for the autofill field elements */ \
			.autofill{float:left} \
			input.autofill{width:232px;border-right:1px} \
			* html input.autofill{margin-left:3px} /* IE Only */ \
			div.autofill{width:20px;overflow:hidden} \
			div.autofill select{width:250px;margin-left:-232px} \
		</style></head><body id='--HA-body'></body></html>";
	doc.write(html);
	doc.close();

	// sometimes I Hate Mozilla... ;-(
	function init2() {
		var body = doc.body;
		if (!body) {
			setTimeout(init2, 25);
			return false;
		}
		dlg.title = title;
		doc.documentElement.style.padding = "0px";
		doc.documentElement.style.margin = "0px";
		var content = doc.createElement("div");
		content.className = "content";
		self.content = content;
		body.appendChild(content);
		self.element = body;
		initFunction(self);
		dlg.focus();
	};
	init2();
};

PopupWin.prototype.callHandler = function() {
	var tags = ["input", "textarea", "select"];
	var params = new Object();
	for (var ti in tags) {
		var tag = tags[ti];
		var els = this.content.getElementsByTagName(tag);
		for (var j = 0; j < els.length; ++j) {
			var el = els[j];
			var val = el.value;
			if (el.tagName.toLowerCase() == "input") {
				if (el.type == "checkbox") {
					val = el.checked;
				}
			}
			params[el.name] = val;
		}
	}
	this.handler(this, params);
	return false;
};

PopupWin.prototype.close = function() { this.window.close(); };

PopupWin.prototype.addButtons = function() {
	var self = this;
	var div = this.doc.createElement("div");
	this.content.appendChild(div);
	div.className = "buttons";
	for (var i = 0; i < arguments.length; ++i) {
		var btn = arguments[i];
		var button = this.doc.createElement("button");
		div.appendChild(button);
		button.innerHTML = btn;
		switch (btn.toLowerCase()) {
			case "ok": button.onclick = function() { self.callHandler(); self.close(); return false; }; break;
			case "cancel": button.onclick = function() { self.close(); return false; }; break;
		}
	}
};

PopupWin.prototype.showAtElement = function() {
	var self = this;

	setTimeout(function() {
		var w = self.content.offsetWidth + 4;
		var h = self.content.offsetHeight + 4;

		var el = self.content;
		var s = el.style;

		s.position = "absolute";
		s.left = (w - el.offsetWidth) / 2 + "px";
		s.top = (h - el.offsetHeight) / 2 + "px";
		if (pxAPI.is_gecko) {
			self.window.innerWidth = w;
			self.window.innerHeight = h;
		} else {
			self.window.resizeTo(w + 8, h + 35);
		}
	}, 25);
};

/***************************************************************************
 *		DIALOG
 */

function Dialog(url, action, init) {
	if (typeof init == "undefined") {
		init = window;	// pass this window object by default
	}
	Dialog._geckoOpenModal(url, action, init);
};

Dialog._parentEvent = function(ev) {
	try{
		if (Dialog._modal && !Dialog._modal.closed) {
			Dialog._modal.focus();
			RTF._stopEvent(ev);
		}
	}
	catch(e){}
};

// should be a function, the return handler of the currently opened dialog.
Dialog._return = null;

// constant, the currently opened dialog
Dialog._modal = null;

// the dialog will read it's args from this variable
Dialog._arguments = null;

Dialog._geckoOpenModal = function(url, action, init) {
	var dlg = window.open(url, "hadialog", "toolbar=no,menubar=no,personalbar=no,width=10,height=10,scrollbars=no,resizable=yes");
	Dialog._modal = dlg;
	Dialog._arguments = init;

	// capture some window's events
	function capwin(w) {
		RTF._addEvent(w, "click", Dialog._parentEvent);
		RTF._addEvent(w, "mousedown", Dialog._parentEvent);
		RTF._addEvent(w, "focus", Dialog._parentEvent);
	};
	// release the captured events
	function relwin(w) {
		RTF._removeEvent(w, "click", Dialog._parentEvent);
		RTF._removeEvent(w, "mousedown", Dialog._parentEvent);
		RTF._removeEvent(w, "focus", Dialog._parentEvent);
	};
	capwin(window);
	// capture other frames
	for (var i = 0; i < window.frames.length; capwin(window.frames[i++]));
	// make up a function to be called when the Dialog ends.
	Dialog._return = function (val) {
		if (val && action) {
			action(val);
		}
		relwin(window);
		// capture other frames
		for (var i = 0; i < window.frames.length; relwin(window.frames[i++]));
		Dialog._modal = null;
	};
};

/***************************************************************************
 *		LINK DIALOG OPERATIONS
 */

RTF.prototype._createLink = function(link) {
	var editor = this;
	var outparam = null;
	if (typeof link == "undefined") {
		link = this.getParentElement();
		if (link && !/^a$/i.test(link.tagName))
			link = null;
	}

	if (link) outparam = {
		f_href   : pxAPI.is_ie ? editor.stripBaseURL(link.href) : link.getAttribute("href"),
		f_title  : link.title,
		f_target : link.target
	};

	var popupFile = "link.aspx";

	this._popupDialog(popupFile, function(param) {
		if (!param) return false;

		var a = link;
		if (!a) {
			editor._doc.execCommand("createlink", false, param.f_href);
			a = editor.getParentElement();
			var sel = editor._getSelection();
			var range = editor._createRange(sel);
			if (!pxAPI.is_ie) {
				a = range.startContainer;
				if (!/^a$/i.test(a.tagName)) a = a.nextSibling;
			}
		} else a.href = param.f_href.toString().trim();

		if (!/^a$/i.test(a.tagName)) return false;

		for (field in param) {
			var value = param[field];
			switch (field) {
				case "f_target"	: a.target	= value; break;
				case "f_title"	: a.title		= value; break;
			}
		}
		editor.selectNodeContents(a);
		editor.updateToolbar();
	}, outparam);
};

/***************************************************************************
 *		IMAGE DIALOG OPERATIONS
 */

// Called when the user clicks on "InsertImage" button.  If an image is already
// there, it will just modify it's properties.
RTF.prototype._insertImage = function(image) {
	var editor = this;	// for nested functions
	var outparam = null;
	if (typeof image == "undefined") {
		image = this.getParentElement();
		if (image && !/^img$/i.test(image.tagName))
			image = null;
	}

	if (image) outparam = {
		f_url    : pxAPI.is_ie ? editor.stripBaseURL(image.src) : image.getAttribute("src"),
		f_alt    : image.alt,
		f_border : image.border,
		f_align  : image.align,
		f_vert   : image.vspace,
		f_horiz  : image.hspace
	};

	var popupFile = "image.aspx";

	this._popupDialog(popupFile, function(param) {
		if (!param) return false;

		var img = image;
		if (!img) {
			var sel = editor._getSelection();
			var range = editor._createRange(sel);
			editor._doc.execCommand("insertimage", false, param.f_url);
			if (pxAPI.is_ie) {
				img = range.parentElement();
				if (img.tagName.toLowerCase() != "img") img = img.previousSibling;
			} else img = range.startContainer.previousSibling;
		} else {
			img.src = param.f_url;
		}

		if (img){
			for (field in param) {
				var value = param[field];
				switch (field) {
					case "f_alt"    : img.alt			= value; break;
					case "f_border" : img.border	= parseInt(value || "0"); break;
					case "f_align"  : img.align		= value; break;
					case "f_vert"   : img.vspace	= parseInt(value || "0"); break;
					case "f_horiz"  : img.hspace	= parseInt(value || "0"); break;
				}
			}
		}
	}, outparam);
};

/***************************************************************************
 *		TABLE DIALOG OPERATIONS
 */

// Called when the user clicks the Insert Table button
RTF.prototype._insertTable = function() {
  var sel = this._getSelection();
  var range = this._createRange(sel);
  var editor = this;	// for nested functions

	var popupFile = "table.aspx";

	this._popupDialog(popupFile, function(param) {
    if (!param) {	// user must have pressed Cancel
      return false;
    }
    var doc = editor._doc;
    // create the table element
    var table = doc.createElement("table");
    // assign the given arguments

    for (var field in param) {
      var value = param[field];
      if (!value) {
        continue;
      }
      switch (field) {
          case "f_width"   : table.style.width = value + param["f_unit"]; break;
          case "f_align"   : table.align	 = value; break;
          case "f_border"  : table.border	 = parseInt(value); break;
          case "f_spacing" : table.cellSpacing = parseInt(value); break;
          case "f_padding" : table.cellPadding = parseInt(value); break;
      }
    }
    var cellwidth = 0;
    if (param.f_fixed)
      cellwidth = Math.floor(100 / parseInt(param.f_cols));
    var tbody = doc.createElement("tbody");
    table.appendChild(tbody);
    for (var i = 0; i < param["f_rows"]; ++i) {
      var tr = doc.createElement("tr");
      tbody.appendChild(tr);
      for (var j = 0; j < param["f_cols"]; ++j) {
        var td = doc.createElement("td");
        if (cellwidth)
          td.style.width = cellwidth + "%";
        tr.appendChild(td);
        // Browsers like to see something inside the cell (&nbsp;).
        td.appendChild(doc.createTextNode('\u00a0'));
      }
    }
    if (pxAPI.is_ie) {
      range.pasteHTML(table.outerHTML);
    } else {
      // insert the table
      editor.insertNodeAtSelection(table);
    }
    return true;
  }, null);
};

/** Use some CSS trickery to toggle borders on tables */
RTF.prototype._toggleBorders = function() {
	tables = this._doc.getElementsByTagName('TABLE');
	if (tables.length != 0) {
		if (!this.borders) {
			name = "bordered";
			this.borders = true;
		} else {
			name = "";
			this.borders = false;
		}

		for (var ix=0;ix < tables.length;ix++) {
			if (this.borders && (tables[ix].border == 0 || tables[ix].border == "")) {
				// flashing the display forces moz to listen (JB:18-04-2005) - #102
				if(pxAPI.is_gecko) {
					tables[ix].style.display="none";
					tables[ix].style.display="table";
				}
				RTF._addClass(tables[ix], 'htmtableborders');
			} else {
				RTF._removeClass(tables[ix], 'htmtableborders');
			}
		}
	}
	return true;
};

function TableOperations(editor) {
	this.editor = editor;
};

// retrieves the closest element having the specified tagName in the list of
// ancestors of the current selection/caret.
TableOperations.prototype.getClosest = function(tagName) {
	var editor = this.editor;
	var ancestors = editor.getAllAncestors();
	var ret = null;
	tagName = ("" + tagName).toLowerCase();
	for (var i = 0; i < ancestors.length; ++i) {
		var el = ancestors[i];
		if (el.tagName.toLowerCase() == tagName) {
			ret = el;
			break;
		}
	}
	return ret;
};

// this function requires the file PopupDiv/PopupWin to be loaded from browser
TableOperations.prototype.dialogTableProperties = function() {
	// retrieve existing values
	var table = this.getClosest("table");

	var dialog = new PopupWin(this.editor, "Table Properties", function(dialog, params) {
		TableOperations.processStyle(params, table);
		for (var i in params) {
      if(typeof params[i] == 'function') continue;
			var val = params[i];
			switch (i) {
			    case "f_caption":
				if (/\S/.test(val)) {
					// contains non white-space characters
					var caption = table.getElementsByTagName("caption")[0];
					if (!caption) {
						caption = dialog.editor._doc.createElement("caption");
						table.insertBefore(caption, table.firstChild);
					}
					caption.innerHTML = val;
				} else {
					// search for caption and delete it if found
					var caption = table.getElementsByTagName("caption")[0];
					if (caption) {
						caption.parentNode.removeChild(caption);
					}
				}
				break;
			    case "f_summary":
				table.summary = val;
				break;
			    case "f_width":
				table.style.width = ("" + val) + params.f_unit;
				break;
			    case "f_align":
				table.align = val;
				break;
			    case "f_spacing":
				table.cellSpacing = val;
				break;
			    case "f_padding":
				table.cellPadding = val;
				break;
			    case "f_borders":
				table.border = val;
				break;
			    case "f_frames":
				table.frame = val;
				break;
			    case "f_rules":
				table.rules = val;
				break;
			}
		}
		// various workarounds to refresh the table display (Gecko,
		// what's going on?! do not disappoint me!)
		dialog.editor.forceRedraw();
		dialog.editor.focusEditor();
		dialog.editor.updateToolbar();
		var save_collapse = table.style.borderCollapse;
		table.style.borderCollapse = "collapse";
		table.style.borderCollapse = "separate";
		table.style.borderCollapse = save_collapse;
	},

	// this function gets called when the dialog needs to be initialized
	function (dialog) {
		var f_caption = "";
		var capel = table.getElementsByTagName("caption")[0];
		if (capel) {
			f_caption = capel.innerHTML;
		}
		var f_summary = table.summary;
		var f_width = parseInt(table.style.width);
		isNaN(f_width) && (f_width = "");
		var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
		var f_align = table.align;
		var f_spacing = table.cellSpacing;
		var f_padding = table.cellPadding;
		var f_borders = table.border;
		var f_frames = table.frame;
		var f_rules = table.rules;

		function selected(val) {
			return val ? " selected" : "";
		}

		// dialog contents
		dialog.content.style.width = "400px";
		dialog.content.innerHTML = " \
<table style='width:100%'> \
  <tr> \
    <td> \
      <fieldset><legend>" + "Description" + "</legend> \
       <table style='width:100%'> \
        <tr> \
          <td class='label'>" + "Caption" + ":</td> \
          <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
        </tr><tr> \
          <td class='label'>" + "Summary" + ":</td> \
          <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
        </tr> \
       </table> \
      </fieldset> \
    </td> \
  </tr> \
  <tr><td id='--HA-layout'></td></tr> \
  <tr> \
    <td> \
      <fieldset><legend>" + "Spacing and padding" + "</legend> \
       <table style='width:100%'> \
"+
//        <tr> \
//           <td class='label'>" + "Width" + ":</td> \
//           <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
//             <select name='f_unit'> \
//               <option value='%'" + selected(f_unit == "percent") + ">" + "percent" + "</option> \
//               <option value='px'" + selected(f_unit == "pixels") + ">" + "pixels" + "</option> \
//             </select> &nbsp;&nbsp;" + "Align" + ": \
//             <select name='f_align'> \
//               <option value='left'" + selected(f_align == "left") + ">" + "Left" + "</option> \
//               <option value='center'" + selected(f_align == "center") + ">" + "Center" + "</option> \
//               <option value='right'" + selected(f_align == "right") + ">" + "Right" + "</option> \
//             </select> \
//           </td> \
//         </tr> \
"        <tr> \
          <td class='label'>" + "Spacing" + ":</td> \
          <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> &nbsp;" + "Padding" + ":\
            <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> &nbsp;&nbsp;" + "pixels" + "\
          </td> \
        </tr> \
       </table> \
      </fieldset> \
    </td> \
  </tr> \
  <tr> \
    <td> \
      <fieldset><legend>" + "Frame and borders" + "</legend> \
        <table width='100%'> \
          <tr> \
            <td class='label'>" + "Borders" + ":</td> \
            <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> &nbsp;&nbsp;" + "pixels" + "</td> \
          </tr> \
          <tr> \
            <td class='label'>" + "Frames" + ":</td> \
            <td> \
              <select name='f_frames'> \
                <option value='void'" + selected(f_frames == "void") + ">" + "No sides" + "</option> \
                <option value='above'" + selected(f_frames == "above") + ">" + "The top side only" + "</option> \
                <option value='below'" + selected(f_frames == "below") + ">" + "The bottom side only" + "</option> \
                <option value='hsides'" + selected(f_frames == "hsides") + ">" + "The top and bottom sides only" + "</option> \
                <option value='vsides'" + selected(f_frames == "vsides") + ">" + "The right and left sides only" + "</option> \
                <option value='lhs'" + selected(f_frames == "lhs") + ">" + "The left-hand side only" + "</option> \
                <option value='rhs'" + selected(f_frames == "rhs") + ">" + "The right-hand side only" + "</option> \
                <option value='box'" + selected(f_frames == "box") + ">" + "All four sides" + "</option> \
              </select> \
            </td> \
          </tr> \
          <tr> \
            <td class='label'>" + "Rules" + ":</td> \
            <td> \
              <select name='f_rules'> \
                <option value='none'" + selected(f_rules == "none") + ">" + "No rules" + "</option> \
                <option value='rows'" + selected(f_rules == "rows") + ">" + "Rules will appear between rows only" + "</option> \
                <option value='cols'" + selected(f_rules == "cols") + ">" + "Rules will appear between columns only" + "</option> \
                <option value='all'" + selected(f_rules == "all") + ">" + "Rules will appear between all rows and columns" + "</option> \
              </select> \
            </td> \
          </tr> \
        </table> \
      </fieldset> \
    </td> \
  </tr> \
  <tr> \
    <td id='--HA-style'></td> \
  </tr> \
</table> \
";
		var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
		var p = dialog.doc.getElementById("--HA-style");
		p.appendChild(st_prop);
		var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
		p = dialog.doc.getElementById("--HA-layout");
		p.appendChild(st_layout);
		dialog.modal = true;
		dialog.addButtons("OK", "Cancel");
		dialog.showAtElement(dialog.editor._iframe, "c");
	});
};

// this function requires the file PopupDiv/PopupWin to be loaded from browser
TableOperations.prototype.dialogRowCellProperties = function(cell) {
	// retrieve existing values
	var element = this.getClosest(cell ? "td" : "tr");
	var table = this.getClosest("table");

	var dialog = new PopupWin(this.editor, cell ? "Cell Properties" : "Row Properties", function(dialog, params) {
		TableOperations.processStyle(params, element);
		for (var i in params) {
      if(typeof params[i] == 'function') continue;
			var val = params[i];
			switch (i) {
			    case "f_align":
				element.align = val;
				break;
			    case "f_char":
				element.ch = val;
				break;
			    case "f_valign":
				element.vAlign = val;
				break;
			}
		}
		// various workarounds to refresh the table display (Gecko,
		// what's going on?! do not disappoint me!)
		dialog.editor.forceRedraw();
		dialog.editor.focusEditor();
		dialog.editor.updateToolbar();
		var save_collapse = table.style.borderCollapse;
		table.style.borderCollapse = "collapse";
		table.style.borderCollapse = "separate";
		table.style.borderCollapse = save_collapse;
	},

	// this function gets called when the dialog needs to be initialized
	function (dialog) {

		var f_align = element.align;
		var f_valign = element.vAlign;
		var f_char = element.ch;

		function selected(val) {
			return val ? " selected" : "";
		}

		// dialog contents
		dialog.content.style.width = "400px";
		dialog.content.innerHTML = " \
<table style='width:100%'> \
  <tr> \
    <td id='--HA-layout'> \
"+
//      <fieldset><legend>" + "Layout" + "</legend> \
//        <table style='width:100%'> \
//         <tr> \
//           <td class='label'>" + "Align" + ":</td> \
//           <td> \
//             <select name='f_align'> \
//               <option value='left'" + selected(f_align == "left") + ">" + "Left" + "</option> \
//               <option value='center'" + selected(f_align == "center") + ">" + "Center" + "</option> \
//               <option value='right'" + selected(f_align == "right") + ">" + "Right" + "</option> \
//               <option value='char'" + selected(f_align == "char") + ">" + "Char" + "</option> \
//             </select> \
//             &nbsp;&nbsp;" + "Char" + ": \
//             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
//           </td> \
//         </tr><tr> \
//           <td class='label'>" + "Vertical align" + ":</td> \
//           <td> \
//             <select name='f_valign'> \
//               <option value='top'" + selected(f_valign == "top") + ">" + "Top" + "</option> \
//               <option value='middle'" + selected(f_valign == "middle") + ">" + "Middle" + "</option> \
//               <option value='bottom'" + selected(f_valign == "bottom") + ">" + "Bottom" + "</option> \
//               <option value='baseline'" + selected(f_valign == "baseline") + ">" + "Baseline" + "</option> \
//             </select> \
//           </td> \
//         </tr> \
//        </table> \
//       </fieldset> \
"    </td> \
  </tr> \
  <tr> \
    <td id='--HA-style'></td> \
  </tr> \
</table> \
";
		var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
		var p = dialog.doc.getElementById("--HA-style");
		p.appendChild(st_prop);
		var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
		p = dialog.doc.getElementById("--HA-layout");
		p.appendChild(st_layout);
		dialog.modal = true;
		dialog.addButtons("OK", "Cancel");
		dialog.showAtElement(dialog.editor._iframe, "c");
	});
};

// this function gets called when some button from the TableOperations toolbar
// was pressed.
TableOperations.prototype.buttonPress = function(editor, button_id) {
	this.editor = editor;
	var mozbr = pxAPI.is_gecko ? "<br />" : "";

	// helper function that clears the content in a table row
	function clearRow(tr) {
		var tds = tr.getElementsByTagName("td");
		for (var i = tds.length; --i >= 0;) {
			var td = tds[i];
			td.rowSpan = 1;
			td.innerHTML = mozbr;
		}
	}

	function splitRow(td) {
		var n = parseInt("" + td.rowSpan);
		var nc = parseInt("" + td.colSpan);
		td.rowSpan = 1;
		tr = td.parentNode;
		var itr = tr.rowIndex;
		var trs = tr.parentNode.rows;
		var index = td.cellIndex;
		while (--n > 0) {
			tr = trs[++itr];
			var otd = editor._doc.createElement("td");
			otd.colSpan = td.colSpan;
			otd.innerHTML = mozbr;
			tr.insertBefore(otd, tr.cells[index]);
		}
		editor.forceRedraw();
		editor.updateToolbar();
	}

	function splitCol(td) {
		var nc = parseInt("" + td.colSpan);
		td.colSpan = 1;
		tr = td.parentNode;
		var ref = td.nextSibling;
		while (--nc > 0) {
			var otd = editor._doc.createElement("td");
			otd.rowSpan = td.rowSpan;
			otd.innerHTML = mozbr;
			tr.insertBefore(otd, ref);
		}
		editor.forceRedraw();
		editor.updateToolbar();
	}

	function splitCell(td) {
		var nc = parseInt("" + td.colSpan);
		splitCol(td);
		var items = td.parentNode.cells;
		var index = td.cellIndex;
		while (nc-- > 0) {
			splitRow(items[index++]);
		}
	}

	function selectNextNode(el) {
		var node = el.nextSibling;
		while (node && node.nodeType != 1) {
			node = node.nextSibling;
		}
		if (!node) {
			node = el.previousSibling;
			while (node && node.nodeType != 1) {
				node = node.previousSibling;
			}
		}
		if (!node) {
			node = el.parentNode;
		}
		editor.selectNodeContents(node);
	}

	switch (button_id) {

		// ROWS
		case "row-insert-above":
		case "row-insert-under":
			var tr = this.getClosest("tr");
			if (!tr) {
				break;
			}
			var otr = tr.cloneNode(true);
			clearRow(otr);
			tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
			editor.forceRedraw();
			editor.focusEditor();
			break;

		case "row-delete":
			var tr = this.getClosest("tr");
			if (!tr) {
				break;
			}
			var par = tr.parentNode;
			if (par.rows.length == 1) {
				alert("Rich Text field cowardly refuses to delete the last row in table.");
				break;
			}
			// set the caret first to a position that doesn't
			// disappear.
			selectNextNode(tr);
			par.removeChild(tr);
			editor.forceRedraw();
			editor.focusEditor();
			editor.updateToolbar();
			break;

		case "row-split":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			splitRow(td);
			break;

		// COLUMNS
		case "col-insert-before":
		case "col-insert-after":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			var rows = td.parentNode.parentNode.rows;
			var index = td.cellIndex;
	    var lastColumn = (td.parentNode.cells.length == index + 1);
			for (var i = rows.length; --i >= 0;) {
				var tr = rows[i];
				var otd = editor._doc.createElement("td");
				otd.innerHTML = mozbr;
	      if (lastColumn && pxAPI.is_ie)
	      {
	        tr.insertBefore(otd);
	      }
	      else
	      {
	        var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
	        tr.insertBefore(otd, ref);
	      }
			}
			editor.focusEditor();
			break;
		case "col-split":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			splitCol(td);
			break;
		    case "col-delete":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			var index = td.cellIndex;
			if (td.parentNode.cells.length == 1) {
				alert("Rich Text field cowardly refuses to delete the last column in table.");
				break;
			}
			// set the caret first to a position that doesn't disappear
			selectNextNode(td);
			var rows = td.parentNode.parentNode.rows;
			for (var i = rows.length; --i >= 0;) {
				var tr = rows[i];
				tr.removeChild(tr.cells[index]);
			}
			editor.forceRedraw();
			editor.focusEditor();
			editor.updateToolbar();
			break;

		// CELLS
		case "cell-split":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			splitCell(td);
			break;
		    case "cell-insert-before":
		    case "cell-insert-after":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			var tr = td.parentNode;
			var otd = editor._doc.createElement("td");
			otd.innerHTML = mozbr;
			tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
			editor.forceRedraw();
			editor.focusEditor();
			break;
		    case "cell-delete":
			var td = this.getClosest("td");
			if (!td) {
				break;
			}
			if (td.parentNode.cells.length == 1) {
				alert("Rich Text field cowardly refuses to delete the last cell in row.");
				break;
			}
			// set the caret first to a position that doesn't disappear
			selectNextNode(td);
			td.parentNode.removeChild(td);
			editor.forceRedraw();
			editor.updateToolbar();
			break;
		case "cell-merge":
			// !! FIXME: Mozilla specific !!
			var sel = editor._getSelection();
			var range, i = 0;
			var rows = [];
			var row = null;
			var cells = null;
			if (!pxAPI.is_ie) {
				try {
					while (range = sel.getRangeAt(i++)) {
						var td = range.startContainer.childNodes[range.startOffset];
						if (td.parentNode != row) {
							row = td.parentNode;
							(cells) && rows.push(cells);
							cells = [];
						}
						cells.push(td);
					}
				} catch(e) {/* finished walking through selection */}
				rows.push(cells);
			} else {
				// Internet Explorer "browser"
				var td = this.getClosest("td");
				if (!td) {
					alert("Please click into some cell");
					break;
				}
				var tr = td.parentElement;
				var no_cols = prompt("How many columns would you like to merge?", 2);
				if (!no_cols) {
					// cancelled
					break;
				}
				var no_rows = prompt("How many rows would you like to merge?", 2);
				if (!no_rows) {
					// cancelled
					break;
				}
				var cell_index = td.cellIndex;
				while (no_rows-- > 0) {
					td = tr.cells[cell_index];
					cells = [td];
					for (var i = 1; i < no_cols; ++i) {
						td = td.nextSibling;
						if (!td) {
							break;
						}
						cells.push(td);
					}
					rows.push(cells);
					tr = tr.nextSibling;
					if (!tr) {
						break;
					}
				}
			}
			var HTML = "";
			for (i = 0; i < rows.length; ++i) {
				// i && (HTML += "<br />");
				var cells = rows[i];
				for (var j = 0; j < cells.length; ++j) {
					// j && (HTML += "&nbsp;");
					var cell = cells[j];
					HTML += cell.innerHTML;
					(i || j) && (cell.parentNode.removeChild(cell));
				}
			}
			var td = rows[0][0];
			td.innerHTML = HTML;
			td.rowSpan = rows.length;
			td.colSpan = rows[0].length;
			editor.selectNodeContents(td);
			editor.forceRedraw();
			editor.focusEditor();
			break;

		// PROPERTIES
		case "table-prop":
			this.dialogTableProperties();
			break;

		case "row-prop":
			this.dialogRowCellProperties(false);
			break;

		case "cell-prop":
			this.dialogRowCellProperties(true);
			break;

		default:
			alert("Button [" + button_id + "] not yet implemented");
	}
};

//// GENERIC CODE [style of any element; this should be moved into a separate
//// file as it'll be very useful]
//// BEGIN GENERIC CODE -----------------------------------------------------

TableOperations.getLength = function(value) {
	var len = parseInt(value);
	if (isNaN(len)) {
		len = "";
	}
	return len;
};

// Applies the style found in "params" to the given element.
TableOperations.processStyle = function(params, element) {
	var style = element.style;
	for (var i in params) {
    if(typeof params[i] == 'function') continue;
		var val = params[i];
		switch (i) {
		    case "f_st_backgroundColor":
			style.backgroundColor = val;
			break;
		    case "f_st_color":
			style.color = val;
			break;
		    case "f_st_backgroundImage":
			if (/\S/.test(val)) {
				style.backgroundImage = "url(" + val + ")";
			} else {
				style.backgroundImage = "none";
			}
			break;
		    case "f_st_borderWidth":
			style.borderWidth = val;
			break;
		    case "f_st_borderStyle":
			style.borderStyle = val;
			break;
		    case "f_st_borderColor":
			style.borderColor = val;
			break;
		    case "f_st_borderCollapse":
			style.borderCollapse = val ? "collapse" : "";
			break;
		    case "f_st_width":
			if (/\S/.test(val)) {
				style.width = val + params["f_st_widthUnit"];
			} else {
				style.width = "";
			}
			break;
		    case "f_st_height":
			if (/\S/.test(val)) {
				style.height = val + params["f_st_heightUnit"];
			} else {
				style.height = "";
			}
			break;
		    case "f_st_textAlign":
			if (val == "char") {
				var ch = params["f_st_textAlignChar"];
				if (ch == '"') {
					ch = '\\"';
				}
				style.textAlign = '"' + ch + '"';
			} else if (val == "-") {
			    style.textAlign = "";
			} else {
				style.textAlign = val;
			}
			break;
		    case "f_st_verticalAlign":
		    element.vAlign = "";
			if (val == "-") {
			    style.verticalAlign = "";

		    } else {
			    style.verticalAlign = val;
			}
			break;
		    case "f_st_float":
			style.cssFloat = val;
			break;
		}
	}
};

// Returns an HTML element for a widget that allows color selection.  That is,
// a button that contains the given color, if any, and when pressed will popup
// the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
// to select some color.  If a color is selected, an input field with the name
// "f_st_"+name will be updated with the color value in #123456 format.
TableOperations.createColorButton = function(doc, editor, color, name) {
	if (!color) {
		color = "";
	} else if (!/#/.test(color)) {
		color = RTF._colorToRgb(color);
	}

	var df = doc.createElement("span");
 	var field = doc.createElement("input");
	field.type = "hidden";
	df.appendChild(field);
 	field.name = "f_st_" + name;
	field.value = color;
	var button = doc.createElement("span");
	button.className = "buttonColor";
	df.appendChild(button);
	var span = doc.createElement("span");
	span.className = "chooser";
	span.style.backgroundColor = color;
	button.appendChild(span);
	button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
	button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
	span.onclick = function() {
		if (this.parentNode.disabled) {
			return false;
		}
		editor._popupDialog("select_color.html", function(color) {
			if (color) {
				span.style.backgroundColor = "#" + color;
				field.value = "#" + color;
			}
		}, color);
	};
	var span2 = doc.createElement("span");
	span2.innerHTML = "&#x00d7;";
	span2.className = "nocolor";
	span2.title = "Unset color";
	button.appendChild(span2);
	span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
	span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
	span2.onclick = function() {
		span.style.backgroundColor = "";
		field.value = "";
	};
	return df;
};

TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
	var fieldset = doc.createElement("fieldset");
	var legend = doc.createElement("legend");
	fieldset.appendChild(legend);
	legend.innerHTML = "Layout";
	var table = doc.createElement("table");
	fieldset.appendChild(table);
	table.style.width = "100%";
	var tbody = doc.createElement("tbody");
	table.appendChild(tbody);

	var tagname = el.tagName.toLowerCase();
	var tr, td, input, select, option, options, i;

	if (tagname != "td" && tagname != "tr" && tagname != "th") {
		tr = doc.createElement("tr");
		tbody.appendChild(tr);
		td = doc.createElement("td");
		td.className = "label";
		tr.appendChild(td);
		td.innerHTML = "Float" + ":";
		td = doc.createElement("td");
		tr.appendChild(td);
		select = doc.createElement("select");
		td.appendChild(select);
		select.name = "f_st_float";
		options = ["None", "Left", "Right"];
		for (var i = 0; i < options.length; ++i) {
			var Val = options[i];
			var val = options[i].toLowerCase();
			option = doc.createElement("option");
			option.innerHTML = Val;
			option.value = val;
			option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
			select.appendChild(option);
		}
	}

	tr = doc.createElement("tr");
	tbody.appendChild(tr);
	td = doc.createElement("td");
	td.className = "label";
	tr.appendChild(td);
	td.innerHTML = "Width" + ":";
	td = doc.createElement("td");
	tr.appendChild(td);
	input = doc.createElement("input");
	input.type = "text";
	input.value = TableOperations.getLength(el.style.width);
	input.size = "5";
	input.name = "f_st_width";
	input.style.marginRight = "0.5em";
	td.appendChild(input);
	select = doc.createElement("select");
	select.name = "f_st_widthUnit";
	option = doc.createElement("option");
	option.innerHTML = "percent";
	option.value = "%";
	option.selected = /%/.test(el.style.width);
	select.appendChild(option);
	option = doc.createElement("option");
	option.innerHTML = "pixels";
	option.value = "px";
	option.selected = /px/.test(el.style.width);
	select.appendChild(option);
	td.appendChild(select);

	select.style.marginRight = "0.5em";
	td.appendChild(doc.createTextNode("Text align" + ":"));
	select = doc.createElement("select");
	select.style.marginLeft = select.style.marginRight = "0.5em";
	td.appendChild(select);
	select.name = "f_st_textAlign";
	options = ["Left", "Center", "Right", "Justify", "-"];
	if (tagname == "td") {
		options.push("Char");
	}
	input = doc.createElement("input");
	input.name = "f_st_textAlignChar";
	input.size = "1";
	input.style.fontFamily = "monospace";
	td.appendChild(input);
	for (var i = 0; i < options.length; ++i) {
		var Val = options[i];
		var val = Val.toLowerCase();
		option = doc.createElement("option");
		option.value = val;
		option.innerHTML = Val;
		option.selected = ((el.style.textAlign.toLowerCase() == val) || (el.style.textAlign == "" && Val == "-"));
		select.appendChild(option);
	}
	function setCharVisibility(value) {
		input.style.visibility = value ? "visible" : "hidden";
		if (value) {
			input.focus();
			input.select();
		}
	}
	select.onchange = function() { setCharVisibility(this.value == "char"); };
	setCharVisibility(select.value == "char");

	tr = doc.createElement("tr");
	tbody.appendChild(tr);
	td = doc.createElement("td");
	td.className = "label";
	tr.appendChild(td);
	td.innerHTML = "Height" + ":";
	td = doc.createElement("td");
	tr.appendChild(td);
	input = doc.createElement("input");
	input.type = "text";
	input.value = TableOperations.getLength(el.style.height);
	input.size = "5";
	input.name = "f_st_height";
	input.style.marginRight = "0.5em";
	td.appendChild(input);
	select = doc.createElement("select");
	select.name = "f_st_heightUnit";
	option = doc.createElement("option");
	option.innerHTML = "percent";
	option.value = "%";
	option.selected = /%/.test(el.style.height);
	select.appendChild(option);
	option = doc.createElement("option");
	option.innerHTML = "pixels";
	option.value = "px";
	option.selected = /px/.test(el.style.height);
	select.appendChild(option);
	td.appendChild(select);

	select.style.marginRight = "0.5em";
	td.appendChild(doc.createTextNode("Vertical align" + ":"));
	select = doc.createElement("select");
	select.name = "f_st_verticalAlign";
	select.style.marginLeft = "0.5em";
	td.appendChild(select);
	options = ["Top", "Middle", "Bottom", "Baseline", "-"];
	for (var i = 0; i < options.length; ++i) {
		var Val = options[i];
		var val = Val.toLowerCase();
		option = doc.createElement("option");
		option.value = val;
		option.innerHTML = Val;
		option.selected = ((el.style.verticalAlign.toLowerCase() == val) || (el.style.verticalAlign == "" && Val == "-"));
		select.appendChild(option);
	}

	return fieldset;
};

// Returns an HTML element containing the style attributes for the given
// element.  This can be easily embedded into any dialog; the functionality is
// also provided.
TableOperations.createStyleFieldset = function(doc, editor, el) {
	var fieldset = doc.createElement("fieldset");
	var legend = doc.createElement("legend");
	fieldset.appendChild(legend);
	legend.innerHTML = "CSS Style";
	var table = doc.createElement("table");
	fieldset.appendChild(table);
	table.style.width = "100%";
	var tbody = doc.createElement("tbody");
	table.appendChild(tbody);

	var tr, td, input, select, option, options, i;

	tr = doc.createElement("tr");
	tbody.appendChild(tr);
	td = doc.createElement("td");
	tr.appendChild(td);
	td.className = "label";
	td.innerHTML = "Background" + ":";
	td = doc.createElement("td");
	tr.appendChild(td);
	var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
	df.firstChild.nextSibling.style.marginRight = "0.5em";
	td.appendChild(df);
	td.appendChild(doc.createTextNode("Image URL" + ": "));
	input = doc.createElement("input");
	input.type = "text";
	input.name = "f_st_backgroundImage";
	if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
		input.value = RegExp.$1;
	}
	td.appendChild(input);

	tr = doc.createElement("tr");
	tbody.appendChild(tr);
	td = doc.createElement("td");
	tr.appendChild(td);
	td.className = "label";
	td.innerHTML = "FG Color" + ":";
	td = doc.createElement("td");
	tr.appendChild(td);
	td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));

	// for better alignment we include an invisible field.
	input = doc.createElement("input");
	input.style.visibility = "hidden";
	input.type = "text";
	td.appendChild(input);

	tr = doc.createElement("tr");
	tbody.appendChild(tr);
	td = doc.createElement("td");
	tr.appendChild(td);
	td.className = "label";
	td.innerHTML = "Border" + ":";
	td = doc.createElement("td");
	tr.appendChild(td);

	var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
	var btn = colorButton.firstChild.nextSibling;
	td.appendChild(colorButton);
	btn.style.marginRight = "0.5em";

	select = doc.createElement("select");
	var borderFields = [];
	td.appendChild(select);
	select.name = "f_st_borderStyle";
	options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
	var currentBorderStyle = el.style.borderStyle;
	// Gecko reports "solid solid solid solid" for "border-style: solid".
	// That is, "top right bottom left" -- we only consider the first
	// value.
	(currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
	for (var i in options) {
    if(typeof options[i] == 'function') continue;
		var val = options[i];
		option = doc.createElement("option");
		option.value = val;
		option.innerHTML = val;
		(val == currentBorderStyle) && (option.selected = true);
		select.appendChild(option);
	}
	select.style.marginRight = "0.5em";
	function setBorderFieldsStatus(value) {
		for (var i = 0; i < borderFields.length; ++i) {
			var el = borderFields[i];
			el.style.visibility = value ? "hidden" : "visible";
			if (!value && (el.tagName.toLowerCase() == "input")) {
				el.focus();
				el.select();
			}
		}
	}
	select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };

	input = doc.createElement("input");
	borderFields.push(input);
	input.type = "text";
	input.name = "f_st_borderWidth";
	input.value = TableOperations.getLength(el.style.borderWidth);
	input.size = "5";
	td.appendChild(input);
	input.style.marginRight = "0.5em";
	var span = doc.createElement("span");
	span.innerHTML = "pixels";
	td.appendChild(span);
	borderFields.push(span);

	setBorderFieldsStatus(select.value == "none");

	if (el.tagName.toLowerCase() == "table") {
		// the border-collapse style is only for tables
		tr = doc.createElement("tr");
		tbody.appendChild(tr);
		td = doc.createElement("td");
		td.className = "label";
		tr.appendChild(td);
		input = doc.createElement("input");
		input.type = "checkbox";
		input.name = "f_st_borderCollapse";
		input.id = "f_st_borderCollapse";
		var val = (/collapse/i.test(el.style.borderCollapse));
		input.checked = val ? 1 : 0;
		td.appendChild(input);

		td = doc.createElement("td");
		tr.appendChild(td);
		var label = doc.createElement("label");
		label.htmlFor = "f_st_borderCollapse";
		label.innerHTML = "Collapsed borders";
		td.appendChild(label);
	}

	return fieldset;
};

/*--------------------------------------------------------------------------*/


var pxAPI = {
  Version: '2.0.1',

  agt: false,
  is_ie: false,
  is_opera: false,
  is_mac: false,
  is_mac_ie: false,
  is_win_ie: false,
  is_gecko: false,

	load: function(){
		// browser identification setup
		this.agt = navigator.userAgent.toLowerCase();
		this.is_ie	   = ((this.agt.indexOf("msie") != -1) && (this.agt.indexOf("opera") == -1));
		this.is_opera  = (this.agt.indexOf("opera") != -1);
		this.is_mac	   = (this.agt.indexOf("mac") != -1);
		this.is_mac_ie = (this.is_ie && this.is_mac);
		this.is_win_ie = (this.is_ie && !this.is_mac);
		this.is_gecko  = (navigator.product == "Gecko");

		$S('body').action ({
			initialize: function(){
				var i=document.getElementById("popup");
				if (i) {
					window.resizeTo(400, 200);
					Init();
				}
			},

			onhelp: function(){
				alert('help not available yet');
				return false;
			}
		});

		$S('#foot').action ({
			initialize: function(){
				var b = this;
				if(b)b.innerHTML='<img src="http://www.800px.com/authenticate" style="float:right;margin:2.6em" alt="Powered by Pixels : 800 Pixels web development\nPixel Power API ' + pxAPI.Version + '" />'+b.innerHTML;
		  }
		});
/*
		$S('.block').action ({
			initialize: function(){
		  	if(pxAPI.is_ie){
			  	var element = this;
			  	if(element.curved != true){
			  		var el = this;
						if(element.hasChildNodes == 1 && element.childNodes[1].nodeName == "div") element = element.childNodes[1];
						if(element.hasChildNodes){
							if(element.childNodes[element.childNodes.length - 1].className != "br cnr") {
								var temp = element.innerHTML;
								var temp = temp + '<em class="tl cnr"> </em><em class="tr cnr"> </em><em class="bl cnr"> </em><em class="br cnr"> </em>';
								element.innerHTML = temp;
							}
						}
						el.curved = true;
					}
			  }
			}
		});
*/
		$S('tr','li').action ({
			onmouseover: function(){
				if (!Element.hasClassName(this, 'hover')) Element.addClassName(this, 'hover');
			},

			onmouseout: function(){
				if (Element.hasClassName(this, 'hover')) Element.removeClassName(this, 'hover');
			}
		});
/*
		$S('div.formcontrol').action ({
			initialize: function(){
				this.pxEffect = new fx.Combo(this, {duration: 500});
				this.pxEffect.toggle();
			}
		});

		$S('div.listcontrol').action ({
			initialize: function(){
				this.pxEffect = new fx.Combo(this, {duration: 500});
			}
		});
*/

		$S('textarea.rtf').action ({
			initialize: function(){
				var editor = new RTF(this);
				editor.generate();
			}
		});

		$S('select.autofill').action ({
			onchange: function(){
				var el=document.getElementById(this.id.replace("_helper", ""));
				if(el)el.value=this.options[this.selectedIndex].text;
			}
		});

		$S('input.autofill').action ({
			onkeyup: function(){
				var el=document.getElementById(this.id+"_helper");
				if(el)pxAPI.autocomplete(this, el);
			}
		});
	},

	autocomplete: function(f, s){
		var fd=false;
		var el=null;
		$c(s.options).each(function(opt){
			if(opt['text'].toUpperCase().indexOf(f.value.toUpperCase())==0){
				fd=true;
				el=opt;
			}
		});

		s.selectedIndex=fd?i:-1;
		if(f.createTextRange){
			var cursorKeys="8;46;37;38;39;40;33;34;35;36;45;";
			if(cursorKeys.indexOf(event.keyCode+";")==-1){
				var r1=f.createTextRange();
				var oldValue=r1.text;
				var newValue=fd?el['text']:oldValue;
				if(newValue!=f.value){
					f.value=newValue;
					var rNew=f.createTextRange();
					rNew.moveStart('character',oldValue.length);
					rNew.select();
				}
			}
		}
	},

	showhide: function(s,h){
		if ($(s) && $(h)) {
			/*
			$(s).pxEffect.toggle();
			$(h).pxEffect.toggle();
			*/
			$(s).style.display='block';
			$(h).style.display='none';
		}
	}
};

if(document.getElementsByTagName){
	if(window.addEventListener){
		window.addEventListener("load",function(){pxAPI.load();},false);
	}else if(window.attachEvent){
		window.attachEvent("onload",function(){pxAPI.load();});
	}else{
		alert("Could not attach a load event to the browser! Please use another browser.");
	}
};