// JavaScript Document

function pretty_txt(elem,type,fit,fit_offsets){
	
	/* 
	When using callbacks there are two ways to bind this to the current object instance
	1. var _this = this; -> binds the object instace to _this -> _this can then be referenced in a function like this
	2. curry(func,this) -> function has to be set up to use thisArg as this reference and accept thisArg as the first argument.
	*/
	
	// Set Vars
	this.stylesheets = Array("/ptxt/resources/editor.css");
	this.editor;
	this.txtarea;
	this.doc;
	this.win;
	this.plain_txt;
	this.elem_id = elem;
	this.type = (type && typeof type != 'undefined') ? type : '' ;
	this.fit = (fit && typeof fit != 'undefined') ? fit : false ;
	this.fit_offsets = (fit_offsets && typeof fit_offsets != 'undefined') ? fit_offsets : {'left':0,'top':0,'right':0,'bottom':0} ;	
	this.swatches = Array(
		"#ff0000","#cc0000","#990000","#660000","#330000",
		"#ffff00","#ccff00","#99ff00","#66ff00","#33ff00",
		"#00ff00","#00cc00","#009900","#006600","#003300",
		"#00ffff","#00ccff","#0099ff","#0066ff","#0033ff",
		"#0000ff","#0000cc","#000099","#000066","#000033",
		"#ff00ff","#ff00cc","#ff0099","#ff0066","#ff0033",
		"#ffcc00","#ff9900","#ff6600","#ff3300","#000000",
		"#111111","#222222","#333333","#444444","#555555",
		"#666666","#777777","#888888","#999999","#aaaaaa",
		"#bbbbbb","#cccccc","#dddddd","#eeeeee","#ffffff"	
	);
	this.fonts = Array("arial","courier new","georgia","impact","verdana");
	this.sizes = { 1 : "x-small", 2 : "small", 3 : "medium", 4 : "large", 5 : "x-large", 6 : "xx-large" }
	this.blocks = {
		0:{'title':'Paragraph','value':'p'},
		1:{'title':'Heading 1','value':'h1'},
		2:{'title':'Heading 2','value':'h2'},
		3:{'title':'Heading 3','value':'h3'},
		4:{'title':'Heading 4','value':'h4'},
		5:{'title':'Heading 5','value':'h5'},
		6:{'title':'Heading 6','value':'h6'}
	}
	
	this.make_pretty = function(elem){
		
		/* Get DOM Node Object */
		/* ------------------------------------------- */
		
		if(!elem){ // Use Stored Id 
			this.textarea = (typeof this.elem_id == 'object') ? this.elem_id : document.getElementById(this.elem_id) ;		
		}else{ // Attach to New Id
			this.textarea = (typeof elem == 'object') ? elem : document.getElementById(elem) ;
			this.elem_id = elem;
		}
		
		/* Container + Control Box */
		/* ------------------------------------------- */
		
		//Generate Container
		var container = document.createElement("div");
		container.style.width = this.textarea.offsetWidth + "px";
		container.style.textAlign = "center";
		container.style.margin = "auto";
		
		//Control Box
		var ctrl = document.createElement("div");
		ctrl.style.width = this.textarea.offsetWidth - 12 + "px";
		ctrl.style.margin = "auto";
		
		//Auto Fit Params
		if(this.fit){
			//Control Box
			(this.is_ie()) ? delete(ctrl.style.width) : ctrl.style.width = null ;
			ctrl.style.position = "absolute";
			ctrl.style.left = 0 + 'px';			
			ctrl.style.top = 0 + 'px';			
			ctrl.style.right = 0 + 'px';			
			//Container
			(this.is_ie()) ? delete(container.style.width) : container.style.width = null ;
			(this.is_ie()) ? delete(container.style.textAlign) : container.style.textAlign = null ;
			(this.is_ie()) ? delete(container.style.margin) : container.style.margin = null ;			
			container.style.position = "absolute";
			container.style.left = 0 + this.fit_offsets.left + 'px';			
			container.style.top = 0 + this.fit_offsets.top + 'px';			
			container.style.right = 0 + this.fit_offsets.right + 'px';			
			container.style.bottom = 0 + this.fit_offsets.bottom + 'px';			
		}
		
		//Button Template
		var button = document.createElement("a");
		button.setAttribute("href","javascript:void(0);");
		
		/* Dropdowns */
		/* ------------------------------------------- */		
		
		//menu onclick hide wrapper
		this.menu_hide_wrap = this.curry(this.hide_dd,this);
		
		//Font colour
		this.swatch_content = document.createElement("div");
		for( i in this.swatches ){
			var opt = document.createElement("a");
			opt.style.display = "block";
			opt.style.cssFloat = "left";
			opt.style.styleFloat = "left";
			opt.style.width = "8px";
			opt.style.height = "8px";
			opt.style.margin = "1px";
			opt.style.border = "1px solid #333333";
			opt.style.backgroundColor = this.swatches[i];
			opt.setAttribute("alt",this.swatches[i]);
			opt.setAttribute("title",this.swatches[i]);
			this['fc_'+i] = this.curry(this.rich_cmd,this,"foreColor",this.swatches[i]);		
			this.add_e_handler(opt,'mousedown',this['fc_'+i]);
			this.add_e_handler(opt,'click',this.menu_hide_wrap);	
			this.swatch_content.appendChild(opt);
		}		
		
		//font colour button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_fc";
		but.setAttribute("alt","Text Colour");
		but.setAttribute("title","Text Colour");		
		this.do_dd_fc = this.curry(this.show_dd,this,this.swatch_content);				
		this.add_e_handler(but,'click',this.do_dd_fc);
		ctrl.appendChild(but);
		
		//Format Block
		this.block_content = document.createElement("div");
		for( i in this.blocks ){
			var b = this.blocks[i];
			var b_val = (this.is_ie()) ? "<"+b.value+">" : b.value ;
			var opt = document.createElement("a");
			opt.style.display = "block";
			opt.style.margin = "1px";
			opt.setAttribute("href","javascript:void(0);");
			opt.innerHTML = b.title;
			this['fb_'+i] = this.curry(this.rich_cmd,this,"formatBlock",b_val);
			this.add_e_handler(opt,'mousedown',this['fb_'+i]);
			this.add_e_handler(opt,'click',this.menu_hide_wrap);	
			this.block_content.appendChild(opt);
		}		
		
		//block format button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_fb";
		but.setAttribute("alt","Block Style / Heading");
		but.setAttribute("title","Block Style / Heading");		
		this.do_dd_fb = this.curry(this.show_dd,this,this.block_content);				
		this.add_e_handler(but,'click',this.do_dd_fb);
		ctrl.appendChild(but);				
		
		//Font Size
		this.size_content = document.createElement("div");
		for( i in this.sizes ){
			var opt = document.createElement("a");
			opt.style.display = "block";
			opt.style.margin = "1px";
			opt.setAttribute("href","javascript:void(0);");
			opt.innerHTML = this.sizes[i];
			this['fs_'+i] = this.curry(this.rich_cmd,this,"fontSize",i);	
			this.add_e_handler(opt,'mousedown',this['fs_'+i]);
			this.add_e_handler(opt,'click',this.menu_hide_wrap);
			this.size_content.appendChild(opt);
		}		
		
		//font size button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_fs";
		but.setAttribute("alt","Font Size");
		but.setAttribute("title","Font Size");		
		this.do_dd_fs = this.curry(this.show_dd,this,this.size_content);				
		this.add_e_handler(but,'click',this.do_dd_fs);
		ctrl.appendChild(but);
		
		//Font Type
		this.font_content = document.createElement("div");
		for( i in this.fonts ){
			var opt = document.createElement("a");
			opt.style.display = "block";
			opt.style.margin = "1px";
			opt.setAttribute("href","javascript:void(0);");
			opt.innerHTML = this.fonts[i];
			this['ft_'+i] = this.curry(this.rich_cmd,this,"fontName",this.fonts[i]);		
			this.add_e_handler(opt,'mousedown',this['ft_'+i]);
			this.add_e_handler(opt,'click',this.menu_hide_wrap);
			this.font_content.appendChild(opt);
		}		
		
		//Font Type button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_ft";
		but.setAttribute("alt","Font Type");
		but.setAttribute("title","Font Type");		
		this.do_dd_ft = this.curry(this.show_dd,this,this.font_content);				
		this.add_e_handler(but,'click',this.do_dd_ft);
		ctrl.appendChild(but);
		
		/* Standard Buttons */
		/* ------------------------------------------- */		
			
		//Bold
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_b";
		but.setAttribute("alt","Bold");
		but.setAttribute("title","Bold");
		this.do_b = this.curry(this.rich_cmd,this,"bold");		
		this.add_e_handler(but,'click',this.do_b);
		ctrl.appendChild(but);
		
		//Italic Button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_i";
		but.setAttribute("alt","Italic");
		but.setAttribute("title","Italic");		
		this.do_i = this.curry(this.rich_cmd,this,"italic");		
		this.add_e_handler(but,'click',this.do_i);
		ctrl.appendChild(but);

		//Underline Button
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_u";
		but.setAttribute("alt","Underline");
		but.setAttribute("title","Underline");		
		this.do_u = this.curry(this.rich_cmd,this,"underline");		
		this.add_e_handler(but,'click',this.do_u);
		ctrl.appendChild(but);

		//Align - L
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_align_l";
		but.setAttribute("alt","Align Left");
		but.setAttribute("title","Align Left");
		this.do_align_l = this.curry(this.rich_cmd,this,"justifyLeft");		
		this.add_e_handler(but,'click',this.do_align_l);
		ctrl.appendChild(but);
		
		//Align - C
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_align_c";
		but.setAttribute("alt","Align Centre");
		but.setAttribute("title","Align Centre");		
		this.do_align_c = this.curry(this.rich_cmd,this,"justifyCenter");		
		this.add_e_handler(but,'click',this.do_align_c);
		ctrl.appendChild(but);

		//Align - R
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_align_r";
		but.setAttribute("alt","Align Right");
		but.setAttribute("title","Align Right");		
		this.do_align_r = this.curry(this.rich_cmd,this,"justifyRight");		
		this.add_e_handler(but,'click',this.do_align_r);
		ctrl.appendChild(but);
		
		//UL
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_ul";
		but.setAttribute("alt","Unordered List");
		but.setAttribute("title","Unordered List");		
		this.do_ul = this.curry(this.rich_cmd,this,"insertUnorderedList");		
		this.add_e_handler(but,'click',this.do_ul);
		ctrl.appendChild(but);

		//OL
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_ol";
		but.setAttribute("alt","Ordered List");
		but.setAttribute("title","Ordered List");		
		this.do_ol = this.curry(this.rich_cmd,this,"insertOrderedList");		
		this.add_e_handler(but,'click',this.do_ol);
		ctrl.appendChild(but);		
		
		//Link
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_link";
		but.setAttribute("alt","Link");
		but.setAttribute("title","Link");		
		this.do_a = this.curry(this.rich_link,this,"createLink","Link URL: ");		
		this.add_e_handler(but,'click',this.do_a);
		ctrl.appendChild(but);
		
		//Image
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_img";
		but.setAttribute("alt","Image");
		but.setAttribute("title","Image");		
		this.do_img = this.curry(this.rich_img,this,"insertImage","Image URL: ");		
		this.add_e_handler(but,'click',this.do_img);
		ctrl.appendChild(but);
		
		//Table
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_table";
		but.setAttribute("alt","Table");
		but.setAttribute("title","Table");		
		this.do_img = this.curry(this.rich_table,this,"insertImage","Image URL: ");		
		this.add_e_handler(but,'click',this.do_img);
		ctrl.appendChild(but);		
		
		//Remove Format
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_r_format";
		but.setAttribute("alt","Remove Formatting");
		but.setAttribute("title","Remove Formatting");
		this.do_r_format = this.curry(this.r_format_wrap,this);			
		this.add_e_handler(but,'click',this.do_r_format);
		ctrl.appendChild(but);
		
		//Strip MS Word
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_strip_word";
		but.setAttribute("alt","Strip MS Word Formatting from Document");
		but.setAttribute("title","Strip MS Word Formatting from Document");
		this.do_strip_word = this.curry(this.strip_word_wrap,this);			
		this.add_e_handler(but,'click',this.do_strip_word);
		ctrl.appendChild(but);		
				
		//Toggle Plain Text
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_src";
		but.setAttribute("alt","Toggle Source Code");
		but.setAttribute("title","Toggle Source Code");		
		this.do_pt = this.curry(this.toggle_plain_txt,this,false);				
		this.add_e_handler(but,'click',this.do_pt);
		ctrl.appendChild(but);
		
		if(!this.fit){
			//Expand Editor Area
			var but = button.cloneNode(true);
			but.className = "ptxt_button ptxt_expand_editor";
			but.setAttribute("alt","Expand Editor Area");
			but.setAttribute("title","Expand Editor Area");		
			this.do_ea = this.curry(this.change_editor_size,this,{ 'h' : 200 , 'w' : 0 });				
			this.add_e_handler(but,'click',this.do_ea);
			ctrl.appendChild(but);
									
			//Reduce Editor Area
			var but = button.cloneNode(true);
			but.className = "ptxt_button ptxt_reduce_editor";
			but.setAttribute("alt","Reduce Editor Area");
			but.setAttribute("title","Reduce Editor Area");		
			this.do_ra = this.curry(this.change_editor_size,this,{ 'h' : -200 , 'w' : 0 });				
			this.add_e_handler(but,'click',this.do_ra);
			ctrl.appendChild(but);
		}
		
		//Help
		var but = button.cloneNode(true);
		but.className = "ptxt_button ptxt_help";
		but.setAttribute("alt","Help");
		but.setAttribute("title","Help");		
		this.do_help = this.curry(this.pop_up,"/ptxt/resources/help.html",750,500,"ptxt_pop_up");				
		this.add_e_handler(but,'click',this.do_help);
		ctrl.appendChild(but);
								
		//Float Clear
		var cb = document.createElement("div");
		cb.style.clear = "both";
		cb.style.height = "1px";
		ctrl.appendChild(cb);
				
		//Insert Control Box into Container
		container.appendChild(ctrl);
		
		/* Editor Window */
		/* ------------------------------------------------ */
		
		//Generate editor div - Styles ( iframe document accessed via element.document in IE and element.contentDocument in w3c )
		var edit_box = document.createElement("iframe");
		var edit_id = this.textarea.id+"_ptxt";
		var edit_wrap_box = document.createElement("div");
		var edit_wrap_id = this.textarea.id+"_ptxt_wrap";
		edit_wrap_box.id = edit_wrap_id;
		edit_box.id = edit_id;
		edit_box.style.width = this.textarea.offsetWidth + "px";
		edit_box.style.height = this.textarea.offsetHeight + "px";
		edit_box.style.overflow = "auto";
		edit_box.style.margin = "auto";
		edit_box.className = "ptxt_editor";
		
		//Auto Fit Params
		if(this.fit){
			edit_box.style.width = "100%";
			edit_box.style.height = "100%";
			(this.is_ie()) ? delete(edit_box.style.margin) : edit_box.style.margin = null ;			
			edit_box.style.position = "absolute";
			edit_box.style.left = 0 + 'px';			
			edit_box.style.top = 0 + 'px';			
			edit_box.style.right = 0 + 'px';			
			edit_box.style.bottom = 0 + 'px';
			
			edit_wrap_box.style.position = "absolute";
			edit_wrap_box.style.left = 1 + 'px';			
			edit_wrap_box.style.top = 25 + 'px';			
			edit_wrap_box.style.right = 1 + 'px';			
			edit_wrap_box.style.bottom = 1 + 'px';			
		}

		//Insert Control Box into Container
		edit_wrap_box.appendChild(edit_box);
		container.appendChild(edit_wrap_box);

		//Insert Editor
		var ta_tmp = this.textarea.cloneNode(true);
		container.appendChild(ta_tmp);
		this.textarea.parentNode.replaceChild(container,this.textarea);
		this.textarea = document.getElementById(this.elem_id);
		this.editor = document.getElementById(edit_id);
		this.editor_wrap = document.getElementById(edit_wrap_id);
		
		//Auto Fit Params
		if(this.fit){
			//this.textarea.style.width = "auto";
			//this.textarea.style.height = "auto";
			this.textarea.style.display = "block";
			this.textarea.style.position = "absolute";
			this.textarea.style.left = 1 + 'px';			
			this.textarea.style.top = 25 + 'px';			
			this.textarea.style.right = 1 + 'px';			
			this.textarea.style.bottom = 1 + 'px';			
		}		
			
		//Initialise
		//Initialise has to be attached to the onload event in mozilla as designMode doesn't get assigned unless the element already exists
		//Webkit wont fire the onload unless it is attached before element is added to the document which means that designMode assignment fails
		//However Webkit allows the designMode property to be assigned instantly so calling the init function manually as well solves the issue
		this.c_init = this.curry(this.init,this);
		if(!this.add_e_handler(this.editor,'load',this.c_init)){ // Accounts for delay in loading the iframe - also IE returns true and w3c return undefined
			this.c_init();
		}
		
		//Attach prep src to form submission
		this.prepare_src = this.curry(this.toggle_plain_txt,this,true);	
		this.add_e_handler(this.textarea.form,'submit',this.prepare_src)
		
		//Hide Text Area
		this.textarea.style.display = "none";		
	}
	
	/* Call Back Functions */
	/* ------------------------------------------- */

	this.init = function(_this){
		//Set Win and Doc Vars
		_this.win = (_this.editor.contentWindow) ? _this.editor.contentWindow : _this.editor.window; //obj.contentWindow -> ie5.5+
		_this.doc = (_this.editor.contentDocument) ? _this.editor.contentDocument : _this.win.document; //obj.contentDocument -> ie8+ || obj.contentWindow.document -> ie5.5+
		//Get Text Area Content
		_this.doc.body.innerHTML = _this.textarea.value;
		//Turn on designMode
		_this.doc.designMode = 'On';
		//Attach stylesheets
		_this.insert_stylesheets();
	}
	
	this.rich_cmd = function (_this, cmd, arg){
		//alert("this:"+_this+" cmd:"+cmd+" arg:"+arg)
		//_this.win.onfocus = function(){ alert("!"); }
		_this.win.focus();
		_this.doc.execCommand(cmd, false, arg);
	}

	this.rich_cmd_select_wrap = function (_this, cmd, selector_id, e){
		// Call Command
		if( document.getElementById(selector_id).value!=0 ){ _this.rich_cmd(_this, cmd, document.getElementById(selector_id).value); }
		// Reset Selector
		if (!e) var e = window.event;
		if(e.target){
			e.target.selectedIndex=0;
		}else if(e.srcElement){
			e.srcElement.selectedIndex=0;
		}
	}
	
	this.rich_cmd_prompt_wrap = function (_this, cmd, msg, vars){
		var response = _this.cb_prompt(msg,"",vars);
		if(response===false){ return false }
		_this.rich_cmd(_this, cmd, response);
	}
	
	this.rich_link = function (_this, cmd, msg){
		//Get Selection
		var rng = _this.get_range_obj( _this.win , _this.doc );
		var container = document.createElement("div");
		(rng.cloneContents) ? container.appendChild(rng.cloneContents()) : container.innerHTML = rng.htmlText ;
		//Find Links + generate output
		var href = container.innerHTML.match( /<a[^>]+?>/gi );
		var links = Array();
		var targets = Array();
		if(href){
			msg = "\n" + msg;
			for( i=0 ; i < href.length ; i++ ){
				if(typeof href[i] == "string"){
					var m = href[i].match( /href=(")?([^"]+)(")?/i );
					var m2 = href[i].match( /target=(")?([^"]+)(")?/i );
					msg = m[2] + "\n" + msg
					links.push(m[2]);
					targets.push(m2[2]);
				}
			}
			msg = "Replacing these links:\n\n" + msg;
		}
		var vars = {'mode':'link','type':_this.type,'w':550,'h':550,'links':links,'targets':targets,'dialog':'rich_prompt.php?mode=link'}
		
		// this passes the result of the prompt to execCommand - no target support
		//_this.rich_cmd_prompt_wrap(_this, cmd, msg, vars);
		
		// grab result and process
		var response = _this.cb_prompt(msg,"",vars);
		if(response){
			if(typeof response == "object"){
				//find out what is selected
				var rng = _this.get_range_obj( _this.win , _this.doc );
				var container = document.createElement("div");
				(rng.extractContents) ? container.appendChild(rng.extractContents()) : container.innerHTML = rng.htmlText ;
				//remove a tags
				var cleaned = container.innerHTML.replace(/<a[^>]+?>/gi,"");
				cleaned = cleaned.replace(/<\/a>/gi,"");
				if(response.href){
					//create new link
					var a = _this.doc.createElement("a");
					a.setAttribute("target",response.target);
					a.setAttribute("href",response.href);
					//insert cleaned content
					a.innerHTML = cleaned;					
				}else{
					//insert cleaned content
					var a = _this.doc.createTextNode(cleaned);				
				}
				//insert new link
				if(rng.insertNode){ // w3c
					rng.insertNode(a);
				}else{ // ie
					var d = _this.doc.createElement("div");
					d.appendChild(a);
					if(rng.htmlText){
						rng.pasteHTML(d.innerHTML);
					}else{
						_this.rich_cmd(_this,"insertParagraph","placeholder");
						rng = _this.doc.selection.createRange();
						rng.moveToElementText(_this.doc.getElementById("placeholder"));
						rng.pasteHTML(d.innerHTML);
					}
				}			
			}else{
				_this.rich_cmd(_this, cmd, response);	
			}
			//cleanup
			var m = _this.doc.getElementsByTagName("a");
			for( i in m ){
				if( m[i].innerHTML == "" || typeof m[i].innerHTML == "undefined" ){
					if(m[i].parentNode){
						m[i].parentNode.removeChild(m[i]);
					}
				}
			}
		}
	}	

	this.rich_img = function (_this, cmd, msg){
		//Get Selection
		var rng = _this.get_range_obj( _this.win , _this.doc );
		var container = document.createElement("div");
		(rng.cloneContents) ? container.appendChild(rng.cloneContents()) : container.innerHTML = rng.htmlText ;
		//Find Links + generate output -> doesn't work in ie because text range object isn't w3c compliant
		var href = container.innerHTML.match( /<img[^>]+?src=(")?[^"]+(")?/gi );
		var imgs = Array(); 
		if(href){
			msg = "\n" + msg;
			for( i=0 ; i < href.length ; i++ ){
				if(typeof href[i] == "string"){
					var m = href[i].match( /src=(")?([^"]+)(")?/i );
					msg = m[2] + "\n" + msg
					imgs.push(m[2]);
				}
			}
			msg = "Replacing these Images:\n\n" + msg;
		}
		var vars = {'mode':'img','type':_this.type,'w':550,'h':550,'dialog':'rich_prompt.php?mode=img','imgs':imgs}
		_this.rich_cmd_prompt_wrap(_this, cmd, msg, vars);
	}
	
	this.rich_table = function (_this, cmd, msg){
		//Get Selection
		var rng = _this.get_range_obj( _this.win , _this.doc );
		/*
		//Grab the current selection - USE THIS TO ALLOW MODS TO EXISTING TABLES
		var container = document.createElement("div");
		(rng.cloneContents) ? container.appendChild(rng.cloneContents()) : container.innerHTML = rng.htmlText ;
		*/
		var vars = {'mode':'table','type':_this.type,'modal_only':true,'dialog':'rich_prompt.php?mode=table','w':350,'h':250}
		var response = _this.cb_prompt(msg,"",vars);
		if(response){
			// Build Table
			var tbl = _this.doc.createElement("table"); // webkit packs a sad if you use document.function() etc (wrong document error) presumedly because of sandboxing
			tbl.setAttribute("width",response.width);
			tbl.setAttribute("cellspacing",response.cellspacing);
			tbl.setAttribute("cellpadding",response.cellpadding);
			tbl.setAttribute("border",response.border);
			var tbody = _this.doc.createElement("tbody");
			for(i=0 ; i<response.rows ; i++){
				var tr = _this.doc.createElement("tr");
				for(ii=0 ; ii<response.columns ; ii++){
					var td = _this.doc.createElement("td");
					td.innerHTML = "&nbsp";
					tr.appendChild(td);
				}
				tbody.appendChild(tr);
			}
			tbl.appendChild(tbody);
			// Insert Table
			if(rng.insertNode){ // w3c
				rng.insertNode(tbl);
			}else{ // ie
				var d = _this.doc.createElement("div");
				d.appendChild(tbl);
				if(rng.htmlText){
					rng.pasteHTML(d.innerHTML);
				}else{
					_this.rich_cmd(_this,"insertParagraph","placeholder");
					rng = _this.doc.selection.createRange();
					rng.moveToElementText(_this.doc.getElementById("placeholder"));
					rng.pasteHTML(d.innerHTML);
				}
			}			
		}
	}
	
	this.r_format_wrap = function(_this){
		
		// Run basic Remove format
		_this.rich_cmd(_this,"removeFormat");
		
		//find out what is selected
		var rng = _this.get_range_obj( _this.win , _this.doc );
		var container = _this.doc.createElement("span");
		(rng.extractContents) ? container.appendChild(rng.extractContents()) : container.innerHTML = rng.htmlText ;
		
		//Process
		container.innerHTML = _this.strip_ms_word(container.innerHTML,true);
		container.innerHTML = container.innerHTML.replace(/<([a-zA-Z0-9]+?[^>]*?)style="[^"]*?"([^>]*?)>/ig, "<$1$2>");
		container.innerHTML = container.innerHTML.replace(/<([a-zA-Z0-9]+?[^>]*?)class="[^"]*?"([^>]*?)>/ig, "<$1$2>");
		
		// Insert
		if(rng.insertNode){ // w3c
			var d = container;
			rng.insertNode(d);
		}else{ // ie
			var d = container;
			if(rng.htmlText){
				rng.pasteHTML(d.innerHTML);
			}else{
				_this.rich_cmd(_this,"insertParagraph","placeholder");
				rng = _this.doc.selection.createRange();
				rng.moveToElementText(_this.doc.getElementById("placeholder"));
				rng.pasteHTML(d.innerHTML);
			}
		}			
		
	}
	
	this.strip_word_wrap = function (_this){
			_this.doc.body.innerHTML = _this.strip_ms_word(_this.doc.body.innerHTML,true);
	}	

	this.toggle_plain_txt = function (_this, xhtml){
		if(!_this.plain_txt){
			_this.textarea.style.display = "inline";
			_this.textarea.value = (xhtml) ? _this.process_content(_this.doc.body.innerHTML, true) : _this.process_content(_this.doc.body.innerHTML, false) ;
			_this.textarea.value = _this.strip_ms_word(_this.textarea.value);
			_this.editor_wrap.style.display = "none";
			_this.textarea.focus();
			_this.plain_txt = true;
		}else{
			_this.editor_wrap.style.display = "block";
			_this.doc.body.innerHTML = _this.textarea.value;
			_this.textarea.style.display = "none";			
			_this.win.focus();
			_this.plain_txt = false;
		}
	}

	this.change_editor_size = function(_this, data){
		var current = { "h" : parseFloat(_this.editor.style.height.substring(0,_this.editor.style.height.length-2)), "w" : parseFloat(_this.editor.style.width.substring(0,_this.editor.style.width.length-2)) };
		var hn = ( (current.h + data.h) > 0 ) ? (current.h + data.h) : current.h ;
		var wn = ( (current.w + data.w) > 0 ) ? (current.w + data.w) : current.w ;		
		_this.editor.style.height = hn + "px";
		_this.editor.style.width = wn + "px";
	}
	
	this.refocus = function (_this){
		_this.win.focus();
	}
	
	this.attach_stylesheet = function(stylesheet){
		this.stylesheets.push(stylesheet);
	}

	this.insert_stylesheets = function(){
		for(i in this.stylesheets){
			var css = document.createElement('link');
			css.type = 'text/css';
			css.rel = 'stylesheet';
			css.href = this.stylesheets[i];
			css.media = 'screen';
			var head = this.doc.getElementsByTagName("head")[0];
			if(head && typeof head != 'undefined'){
				this.doc.getElementsByTagName("head")[0].appendChild(css);
			}
			
		}
	}

	/* HTML to XHTML + Supporting Replacement Functions */
	/* ------------------------------------------- */
	
	this.process_content = function(str, xhtml){
		
		//&nbsp; over kill
		str = str.replace(/&nbsp;/gi, " ");
		
		//remove spans with no attributes
		str = str.replace(/<span>((.|\t|\r|\n|\v|\f)*?)<\/span>/ig, "$1");
			
		//remove excess line breaks
		str = str.replace(/\n+/ig, "\n");
		
		//remove empty inline elements
		str = str.replace(/<(span|b|i|u|em|strong)><\/(span|b|i|u|em|strong)>/ig, "");					
		
		//convert to standard output 
		if(xhtml){ str = this.html_to_xhtml(str); }
		
		//Rogue Line Breaks
		if(str=="<br>" || str=="<br />"){ str = ""; }
			
		return str;
		
	}
	
	this.html_to_xhtml = function(str){
		
		//<br>
		str = str.replace(/<br>/gi, "<br />");

		//<img>
		str = str.replace(/<img([^>]*)>/gi, "<img$1 />");

		//Uppercase Tags
		str = str.replace(/<(\/?)([A-Z]+)([^>]*)>/g, this.tags_to_lower);
		
		//Font Tags
		var pft = this.curry(this.process_font_tags,this);
		str = str.replace(/<(\/?)font([^>]*)>/ig, pft);		
		
		//Strip MS Word
		str = this.strip_ms_word(str);
	
		return str;
		
	}
	
	this.strip_ms_word = function(str,fullstrip){
		if(fullstrip){
			str = str.replace(/<!--\[if gte mso [0-9]+\]>(.|\t|\r|\n|\v|\f)*?<!\[endif\]-->/ig, "");
			str = str.replace(/<p[^>]*?class=["']?Mso[A-Za-z]*?["']?[^>]*?>/ig, "<p>");
		}else{
			str = str.replace(/<!--\[if gte mso [0-9]+\]>\n*?<xml>(.|\t|\r|\n|\v|\f)*?<\/xml><!\[endif\]-->/ig, "");					
		}
		str = str.replace(/<?xml[^>]\/>/ig, ""); // msie	
		str = str.replace(/<[\/]?o:p>/ig, "");	// msie
		str = str.replace(/<span[^>]*?style="[^"]*?(&|&amp;|&quot;)[^"]*?"[^>]*?>((.|\t|\r|\n|\v|\f)*?)<\/span>/ig, "$2");
		str = str.replace(/<span[^>]*?(;=["']?.*?["']?|lang=["']?.*?["']?|,=["']?.*?["']?)[^>]*?>((.|\t|\r|\n|\v|\f)*?)<\/span>/ig, "$2");			
		str = str.replace(/<span style="">((.|\t|\r|\n|\v|\f)*?)<\/span>/ig, "$1");
		return str;	
	}
	
	this.tags_to_lower = function(str, p1, p2, p3, offset, s){
		p2 = p2.toLowerCase();
		p3 = p3.toLowerCase();
		return "<"+p1+p2+p3+">";
	}
		
	this.process_font_tags = function(_this, str, p1, p2, offset, s){
		var style = "";
		if(p1){ 
			var out = "</span>";
		}else{
			//size
			var size = p2.match( /size=(")?([0-9]+)(")?/i )
			if( size ){ style+= "font-size:"+_this.sizes[size[2]]+";"; }
			//face
			var face = p2.match( /face=(")?([^"]+)(")?/i )
			if( face ){ style+= "font-family:"+face[2]+";"; }
			//color
			var color = p2.match( /color=(")?([^"]+)(")?/i )
			if( color ){ style+= "color:"+color[2]+";"; }			
			//out
			var out = "<span style='"+ style +"'>";
		}
		return out;
	}
	
	/* Drop Menu */
	/* ------------------------------------------- */		
	
	//Show Menu
	this.show_dd = function(_this, content, e){
		if (!e) var e = window.event;
		var target = (e.target) ? e.target : e.srcElement ;
		var pos = _this.get_abs_pos(target);
		var x = pos['x'] + "px";
		var y = ( pos['y'] + target.offsetHeight + 5 ) + "px";
		if(document.getElementById("ptxt_dd")){
			var dd = document.getElementById("ptxt_dd");
			dd.style.display = "block";
			dd.style.left = x;
			dd.style.top = y;
			_this.clear_child_nodes(dd);
			dd.appendChild(content);
		}else{
			var dd = document.createElement("div");
			dd.id = "ptxt_dd";
			dd.style.display = "block";
			dd.style.position = "absolute";
			dd.style.padding = "2px";
			dd.style.left = x;
			dd.style.top = y;
			dd.appendChild(content);
			document.body.appendChild(dd);
			_this.c_act_dd = _this.curry( _this.act_dd, _this)
			_this.add_e_handler(dd, "mouseover", _this.c_act_dd);
		}
	}
	
	//Activate Menu
	this.act_dd = function(_this){
		var dd = document.getElementById("ptxt_dd");
		_this.c_hide_dd = _this.curry( _this.hide_dd, _this);
		_this.add_e_handler(dd, "mouseout", _this.c_hide_dd);
	}
	
	//Hide Menu
	this.hide_dd = function(_this,e){
		var dd = document.getElementById("ptxt_dd");
		//Find what the mouse come from + has moved to
		if (!e) var e = window.event;
		//if the event is a mouseover then do some checks
		if(e.type=="mouseout" || e.type=="onmouseout"){
			var tg = (window.event) ? e.srcElement : e.target;
			var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
			//Kill function if not triggered by dd
			if(tg!=dd) return ;
			//Move up the dom until we hit the element that has mouseout attached
			while (reltg != tg && reltg.nodeName != 'BODY'){
				reltg = reltg.parentNode;
			}
			//if we hit the mouseout attached elem -> reltg is inside tg -> kill func
			if (reltg == tg) return;
		}
		//Hide
		dd.style.display = "none";
		//Deactivate menu
		_this.remove_e_handler(dd, "mouseout", _this.c_hide_dd);		
	}
	
	/* Supporting Methods */
	/* ------------------------------------------- */
	
	this.pop_up = function(url,w,h,name) {
		if(w==null || w==0 || w==''){w='500'}
		if(h==null || h==0 || h==''){h='575'}
		var LeftPosition = (screen.width) ? (screen.width-w) / 2 : 100 ;
		var TopPosition = (screen.height) ? (screen.height-h) / 2 : 100 ;
		var settings = 'height='+h+',width='+w+',top='+TopPosition+',left='+LeftPosition+',menubar=no,toolbar=no,location=no,resizable=yes,scrollbars=yes,status=no,personalbar=no';
		newwindow = window.open(url,'pop',settings);
		newwindow.focus();
		return false;
	}
	
	this.resize = function(w,h){
		if(w==null || w==0 || w==''){w='400'}
		if(h==null || h==0 || h==''){h='400'}
		window.resizeTo(w,h)
	}
		
	this.ie_prompt = function(str,vars){
		str = str.replace(/\n/gi, "<br />");
		if(vars){
			var w = vars.w;
			var h = vars.h;
		}else{
			var m = str.match( /<br \/>/gi );
			var w = "290"
			var h = (m) ? 15 * m.length + 100 : 100 ;
		}
		var settings = "dialogWidth: "+w+"px; dialogHeight: "+h+"px; center: yes; edge: raised; scroll: no; status: no; resizable: yes;";
		str = {'str':str,'vars':vars}
		var dialog = (vars.dialog) ? vars.dialog : "ie_prompt.html"
		return window.showModalDialog("http://"+window.location.hostname+"/ptxt/resources/"+dialog, str, settings);
	}
	
	this.cb_prompt = function(str,dflt,vars){
		try{
			if(window.showModalDialog && !/AppleWebKit\/530\.5/.test(navigator.userAgent) ){ // AppleWebKit/530.5 has a broken showModalDialogue
				return this.ie_prompt(str, vars); 
			}else{
				if(vars.modal_only){
					alert("Your browser doesn't support this action. \n\n For table support try upgrading or using Firefox (firefox.com).");
				}else{
					return prompt(str, dflt);
				}
			}
		}catch(e){ 
			return false; 
		}
	}
	
	this.get_abs_pos = function(elem){
		var x = 0;
		var y = 0;
		for (var offMark = elem; offMark; offMark = offMark.offsetParent) {
			x += offMark.offsetLeft;
		}
		for (var offMark = elem; offMark; offMark = offMark.offsetParent) {
			y += offMark.offsetTop;
		}
		return { "x":x, "y":y };
	}

	this.clear_child_nodes = function(element){
		if ( element.hasChildNodes() ){
			while ( element.childNodes.length >= 1 ){
				element.removeChild( element.firstChild );       
			} 
		}
	}
		
	this.add_e_handler = function(obj, e, func){
		//test if func exists - prevents problems in IE
		if(typeof func != "undefined"){		
			if(obj.attachEvent){
				return obj.attachEvent('on' + e, func);
			}else if(obj.addEventListener){
				return obj.addEventListener(e, func, false);
			}else{
				obj['on' + e] = func;
			}
		}
	}
	
	this.remove_e_handler = function(obj, e, func){
		//test if func exists - prevents problems in IE		
		if(typeof func != "undefined"){
			if (obj.detachEvent){
				return obj.detachEvent('on' + e, func);
			}else if(obj.removeEventListener){
				return obj.removeEventListener(e, func, false);
			}else{
				obj['on' + e] = null;
			}
		}
	}

	this.curry = function(method){
		var curried = [];
		for (var i = 1; i < arguments.length; i++) {
			curried.push(arguments[i]);
		}
		return function() {
			var args = [];
			for (var i = 0; i < curried.length; i++) {
				args.push(curried[i]);
			}
			for (var i = 0; i < arguments.length; i++) {
				args.push(arguments[i]);
			}
			return method.apply(null, args);
		}
	}
	
	this.get_range_obj = function(win_a,doc_a){

		//Mozilla - mozilla selection object / w3c range object
		//Opera - mozilla selection object / w3c range object / MS textRange object
		//IE - MS textRange object
		
		var wkreg = /AppleWebKit/
		var win = (win_a) ? win_a : window ;
		var doc = (doc_a) ? doc_a : document ;
		
		win.focus();
		
		var selObj;
		var rangeObj;
		if(win.getSelection){ //Moz/Opera/Webkit
			selObj = win.getSelection(); //Get Selection Object
			if(selObj.getRangeAt){ //Moz/Opera - web kit had broken implementation
				rangeObj =  selObj.getRangeAt(0); //Get w3c range object
			}else{ //Webkit
				var rangeObj = doc.createRange(); //Get w3c range object (no getRangeAt support)
				rangeObj.setStart(selObj.anchorNode,selObj.anchorOffset);
				rangeObj.setEnd(selObj.focusNode,selObj.focusOffset);			
			}
		}else if(doc.selection){ //IE
			selObj = doc.selection.createRange(); 	//Get ms textRange Object
			rangeObj =  selObj;
		}
		return rangeObj;
	}	
	
	this.dumpObj = function(obj, name, indent, depth){
		var MAX_DUMP_DEPTH = 10;
		if(depth > MAX_DUMP_DEPTH){
			return indent + name + ": <Maximum Depth Reached>\n";
		}
		if (typeof obj == "object"){
			var child = null;
			var output = indent + name;
			var total = 0;
			if(obj instanceof Array){
				total = obj.length;
				output += " (Array)\n";
			}else{
				for(var item in obj){
				   total++;
				}
				output += " (Object)\n";
			}
			output += indent + "Total item: " + total + "\n";
			indent += "\t";
			if(obj instanceof Array){
				for(var i = 0; i < obj.length; i++){
				   child = obj[i];
				   output += dumpObj(child, i, indent, depth + 1);
				}
			}else{
				for(var item in obj){
				   try{
					   child = obj[item];
				   }catch(e){
					   child = "<Unable to Evaluate>";
				   }
				   if(typeof child == "object"){
					   output += this.dumpObj(child, item, indent, depth + 1);
				   }else{
					   output += indent + item + ": " + child + "\n";
				   }
				}
			}
			return output;
		}else{
			return obj + " is not an object.";
		}
	}
	
	this.is_ie = function(){
		var re = new RegExp("MSIE","i");
		var is_ie = re.test(navigator.userAgent);
		return is_ie;
	}

}
