function insertTags(theField , szTagOpen ,  szTagClose , szSampleText , trim)
{
	var obj = new Textarea(theField) ;
	if(obj.field) obj.insertAroundSelection(szTagOpen ,  szTagClose , szSampleText , trim ) ;
}

function insertAtCarret(theField , value)
{
	var obj = new Textarea(theField) ;
	if(obj.field) obj.replaceSelectionByValue(value) ;
}

// SECTION 2 : BEGIN THE REAL OO STUFF :
function Textarea(theField)
{
	this.field = ('string'==typeof(theField)) ? document.getElementById(theField) : theField ;
	if('object'!=typeof(this.field))
	{
		this.field = false ;
		alert(Textarea.A_MSG_1) ;
	}
	this.objUndoRedo = null ;
}

Textarea.prototype.addUndoRedoCapability = function(maxUndoChain,u,ux,r,rx,zBlock)
{
	if('undefined' == typeof UndoRedo) alert(Textarea.A_MSG_4) ;
	else this.objUndoRedo = new UndoRedo(this,maxUndoChain,u,ux,r,rx,zBlock) ;
};

// SECTION 2.4 : constants (do not modify!) :
Textarea.VERSION = '0.4' ;
Textarea.BUILD = 20080109.0921 ;
Textarea.r1 = new RegExp("^[\\s]*") ; // leading spaces
Textarea.r2 = new RegExp("[\\s]*$") ; // trailing spaces

// SECTION 2.6 : ALERT ERROR MESSAGES (to be redefined in your page for localization) :
Textarea.A_MSG_1 = "Invalid field descriptor passed to Textarea" ;
Textarea.A_MSG_2 = "Sorry, your browser does not support Textarea functionnalities\n" ;
Textarea.A_MSG_2 += "We suggest you install a recent version of Firefox\n" ;
Textarea.A_MSG_3 = "No undo/redo capability added to this textarea!" ;

// SECTION 3 : THE REAL Textarea STUFF : PUBLIC METHODS :
Textarea.prototype.insertAroundSelection = function(szTagOpen,szTagClose,szSampleText,trim,comment,back)
{
	if(this.field && (szTagOpen || szTagClose) )
	{
		if('string'!=typeof(szSampleText)) szSampleText = '' ;
		if('string'!=typeof(szTagClose)) szTagClose = szTagOpen ;
		if('undefined' == typeof trim) trim = 1 ;
		var com = comment ? comment : szTagOpen ;
		com = com ? com : szTagClose ;
		this.store(com,1,0,back) ;
		if(this.field.selectionStart >= '0') // gecko, konqueror, safari, opera
		this.gecko_insertAroundSelection(szTagOpen,szTagClose,szSampleText,trim) ;
		else if(document.selection && document.selection.createRange ) // msie, opera
		this.msie_insertAroundSelection(szTagOpen,szTagClose,szSampleText,trim) ;
		else this.others_insertAroundSelection(szTagOpen,szTagClose,szSampleText) ;
		this.valueHasChanged(back) ;
		this.store(com,0,0,back) ;
	}
};

Textarea.prototype.replaceSelectionByValue = function(value,comment,back)
{
	if(this.field)
	{
		value = '' + value ; // make sure it's a string
		// if(value.length >= 1) // accept length == 0 ===> will erase the selection
		var com = comment ? comment : ("replaceSelectionByValue ("+ value.length +" chars)") ;
		this.store(com,1,0,back) ;
		if(this.field.selectionStart >= '0')
		this.gecko_replaceSelectionByValue(value) ;
		else if(document.selection && document.selection.createRange )
		this.msie_replaceSelectionByValue(value) ;
		else this.others_replaceSelectionByValue(value) ;
		this.valueHasChanged(back) ;
		this.store(com,0,0,back) ;
	}
};

Textarea.prototype.deleteSelection = function(comment,back)
{
	if(this.field) this.replaceSelectionByValue('',comment,back) ;
};

Textarea.prototype.deleteAll = function(comment,back)
{
	if(this.field)
	{
		this.field.select() ;
		this.replaceSelectionByValue('',comment,back) ;
	}
};

Textarea.prototype.setText = function(value,comment,back)
{
	if(this.field)
	{
		var com = comment ? comment : ("setText("+ value.length +" chars)") ;
		this.store(com,1,0,back) ;
		this.field.value = value ;
		this.valueHasChanged(back) ;
		this.store(com,0,0,back) ;
	}
};

Textarea.prototype.selectAll = function() { if(this.field) this.field.select() ; };
Textarea.prototype.getSelectedText = function()
{
	var t ; // = '' ; // returns null if not supported
	if(this.field)
	{
		if(this.field.selectionStart >= '0')
		t = this.gecko_getSelectedText() ;
		else if(document.selection && document.selection.createRange )
		t = this.msie_getSelectedText() ;
	}
	return t ;
};

Textarea.prototype.msie_getSelectedText = function()
{
	var t ;
	if(document.selection && document.selection.createRange)
	{
		t = document.selection.createRange().text ;
	}
	return t ;
};

Textarea.prototype.msie_insertAroundSelection = function(szTagOpen ,  szTagClose , szSampleText , trim)
{
	if(document.selection && document.selection.createRange)
	{
		var szTheSelection = document.selection.createRange().text ;
		var zReplaced = true ;
		if(!szTheSelection)
		{
			zReplaced = false ;
			szTheSelection = szSampleText ;
		}
		this.field.focus() ;
		var uTheSelectionLength = szTheSelection.length ; // before the trim stuff
		var range = document.selection.createRange();
		var leadingSpaces = '' ;
		var trailingSpaces = '' ;
		if(trim)
		{
			var a = Textarea.r1.exec(szTheSelection) ;
			leadingSpaces = (a && a[0]) ? a[0] : '' ;
			a = Textarea.r2.exec(szTheSelection) ;
			trailingSpaces = (a && a[0]) ? a[0] : '' ;
			szTheSelection = szTheSelection.substr(leadingSpaces.length,szTheSelection.length-(leadingSpaces.length+trailingSpaces.length)) ;
		}
		range.text =  leadingSpaces + szTagOpen  + szTheSelection +  szTagClose + trailingSpaces ;
		if(!zReplaced)
		{
			range.moveStart('character', - uTheSelectionLength - szTagClose.length );
			range.moveEnd('character', - szTagClose.length );
		}
		range.select();
		if(this.field.createTextRange) { this.field.caretPos = document.selection.createRange().duplicate(); }
	}
};

Textarea.prototype.msie_replaceSelectionByValue = function(value)
{
	if(document.selection && document.selection.createRange)
	{
		this.field.focus();
		var sel = document.selection.createRange();
		if(sel) sel.text = value;
	}
};

Textarea.prototype.gecko_getSelectedText = function()
{
	var t ;
	if(this.field.selectionStart >= 0)
	{
		var startPos = this.field.selectionStart ;
		var endPos   = this.field.selectionEnd ;
		t = this.field.value.substring(startPos, endPos)  ;
	}
	return t ;
};

Textarea.prototype.gecko_insertAroundSelection = function(szTagOpen ,  szTagClose , szSampleText , trim)
{
	if(this.field.selectionStart >= 0)
	{
		var startPos = this.field.selectionStart ;
		var endPos   = this.field.selectionEnd ;
		var zReplaced = (endPos - startPos) ? true : false ;
		var scrollTop = this.field.scrollTop ;
		var szTheSelection = this.field.value.substring(startPos, endPos) ;
		if(!szTheSelection) szTheSelection = szSampleText ;
		var leadingSpaces = '' ;
		var trailingSpaces = '' ;
		if(trim)
		{
			var a = Textarea.r1.exec(szTheSelection) ;
			leadingSpaces = (a && a[0]) ? a[0] : '' ;
			a = Textarea.r2.exec(szTheSelection) ;
			trailingSpaces = (a && a[0]) ? a[0] : '' ;
			szTheSelection = szTheSelection.substr(leadingSpaces.length,szTheSelection.length-(leadingSpaces.length+trailingSpaces.length)) ;
		}
		var szTheReplacement = leadingSpaces + szTagOpen  + szTheSelection +  szTagClose + trailingSpaces ;
		this.field.value = this.field.value.substring(0, startPos) + szTheReplacement + this.field.value.substring(endPos, this.field.value.length);
		this.field.focus();
		if(zReplaced)
		{
			var cPos = startPos + szTheReplacement.length ;
			this.field.selectionStart=cPos;
			this.field.selectionEnd=cPos;
		}
		else
		{
			this.field.selectionStart = startPos + leadingSpaces.length + szTagOpen.length ;
			this.field.selectionEnd = this.field.selectionStart + szTheSelection.length;
		}
		this.field.scrollTop=scrollTop;
	}
};

Textarea.prototype.gecko_replaceSelectionByValue = function(value)
{
	if(this.field.selectionStart >= 0)
	{
		var startPos  = this.field.selectionStart;
		var endPos    = this.field.selectionEnd;
		var scrollTop = this.field.scrollTop;
		this.field.value = this.field.value.substring(0, startPos) + value + this.field.value.substring(endPos, this.field.value.length);
		this.field.focus();
		var cPos=startPos+(value.length);
		this.field.selectionStart=cPos;
		this.field.selectionEnd=cPos;
		this.field.scrollTop=scrollTop;
	}
};
Textarea.prototype.others_replaceSelectionByValue = function(value) { this._notSupportedBrowser() ; };
Textarea.prototype.others_insertAroundSelection = function(szTagOpen ,  szTagClose , szSampleText) { this._notSupportedBrowser() ; };
Textarea.prototype._notSupportedBrowser = function() { alert(Textarea.A_MSG_2) ; };
Textarea.prototype.valueHasChanged = function(back) {} ;
Textarea.prototype.store = function(comment,zBefore,zDoAlert,backStored)
{
	if(this.field)
	{
		if(this.objUndoRedo)
		{
			var node = this.objUndoRedo.store(this.field.value,comment,zBefore,1) ;
			if(node) this.valueHasBeenStored(backStored) ;
		}
		else if(('undefined' == typeof zDoAlert) ? 1 : zDoAlert) alert(Textarea.A_MSG_3) ;
	}
};