/*
 * When included, this file will replace all textareas in the including
 * document that have the "replace-with-zeditor" class with a JavaScript-enhanced HTML
 * editor.
 *
 *
 * When the form is submitted, the edited text will POST in the same variable name
 * specified in the replaced textarea. The textarea isn't going away, it's just getting
 * moved around.
 *
 * The goal of this editor is not to be a WYSIWYG editor but rather an
 * HTML editing tool.
 *
 * This tool requires version 1.4.0 of the prototype.js library.
 *                    version 1.5.1 of the builder.js library.
 *
 * @author Nicholas Piasecki <nick@zincastle.com>
 */
 
 
/*
 * Set the following URLs to the path to the browser.php file from the document
 * root. Typical values are already filled in.
 */
 
var ZEditorParams = {};

ZEditorParams.Development =
{
	sFileBrowserUrl: '',
	sFileDir: '',
	sImageBrowserUrl: '',
	sImageDir: '',
	sUploadUrl: ''
}

ZEditorParams.Live =
{
	sFileBrowserUrl: '???',
	sFileDir: '???',
	sImageBrowserUrl: '???',
	sImageDir: '???',
	sUploadUrl: '???'
}

/*******************************************************************************
 *
 * Do not edit below this line
 *
 ******************************************************************************/

/*
 * The aEditors global array is provided in case, for whatever reason, you'd
 * like to access the applied editor objects.
 */
var aEditors = $A( Array() );

/*
 * On window load, this function creates editors for each of the textareas on
 * the page that have the replace-with-zeditor class applied. (They'll also
 * size themselves to the dimension of the textarea, so if you have a specific
 * size in mind, set your textarea to it beforehand.
 */
Event.observe
( 
	window,
	'load',
	function() 
	{
		document.getElementsByClassName( 'replace-with-zeditor', 'TEXTAREA' ).each
		( 
			function( oValue, iIdx ) 
			{
				aEditors.push
				( 
					new ZEditor
					(
						oValue,
						{
							iWidth: oValue.offsetWidth + 'px',
							iHeight: oValue.offsetHeight + 'px',
							sFileBrowserUrl: ( window.location.toString().match( /zincastle/i ) || window.location.toString().match( /zssweb/i ) ? ZEditorParams.Development.sFileBrowserUrl : ZEditorParams.Live.sFileBrowserUrl ),
							sFileDir: ( window.location.toString().match( /zincastle/i ) || window.location.toString().match( /zssweb/i ) ? ZEditorParams.Development.sFileDir : ZEditorParams.Live.sFileDir ),
							sImageBrowserUrl: ( window.location.toString().match( /zincastle/i ) || window.location.toString().match( /zssweb/i ) ? ZEditorParams.Development.sImageBrowserUrl : ZEditorParams.Live.sImageBrowserUrl ),
							sImageDir: ( window.location.toString().match( /zincastle/i ) || window.location.toString().match( /zssweb/i ) ? ZEditorParams.Development.sImageDir : ZEditorParams.Live.sImageDir ),
							sUploadUrl: ( window.location.toString().match( /zincastle/i ) || window.location.toString().match( /zssweb/i ) ? ZEditorParams.Development.sUploadUrl : ZEditorParams.Live.sUploadUrl )
						}
					)
				);
				Event.observe(
					oValue,
					'keydown',
					function( oEvent )
					{
						if( oEvent.keyCode == Event.KEY_TAB )
						{
							Event.stop( oEvent );
							var oTextArea = this;
							Try.these(
								function()
								{
									document.selection.createRange().text = '\t';
								},
								function()
								{
									new ZSelection( oTextArea ).replaceSelectedText( '\t' );
									oTextArea.selectionStart = oTextArea.selectionEnd;
								},
								function()
								{
									alert( 'crap' );
								}
							);
						}
					}.bindAsEventListener( oValue ),
					false
				);
			} 
		);
	},
	false
);

/******************************************************************************/

Object.extend(
	Builder,
	{
		getFormRow: function( sLabel, sName, oInput, aAdditional )
		{
			oInput = oInput || Builder.node(
				'input',
				{
					type: 'text',
					size: '60',
					style: 'float: left; margin-right: 1em;'
				}
			);
			aAdditional = aAdditional || '';
			oInput.name = sName;
			return Builder.node(
				'div',
				{ style: 'padding: 0.5em 0;' },
				[
					Builder.node(
						'label',
						{ 
							style: 'float: left; width: 10em;'
						},
						[ sLabel ]
					),
					oInput,
					aAdditional,
					new ZClearNode().getElement()
				]
			);
		}
	}
);

/******************************************************************************/

Object.extend(
	String.prototype,
	{
		trim: function()
		{
			if ( this.length < 1 )
			{
				return '';
			}
			else
			{
				var sTrim = this.rtrim();
				sTrim = sTrim.ltrim();
				return sTrim;
			}
		},
		
		ltrim: function()
		{
			if ( this.length < 1 )
			{
				return '';
			}
			else
			{
				var sTemp = '';
				var iTemp = 0;
				while ( iTemp < this.length )
				{
					if ( !this.charAt( iTemp ).match( /\s/ ) )
					{
						sTemp = this.substring( iTemp, this.length );
						break;
					}
					++iTemp;
				}
				return sTemp;
			}
		},
		
		rtrim: function()
		{
			var sTemp = '';
			if ( this.length < 0 )
			{
				return '';
			}
			else
			{
				var iIdx = this.length - 1;
				while ( iIdx > -1 )
				{
					if (!this.charAt( iIdx ).match( /\s/ ))
					{
						sTemp = this.substring( 0, iIdx + 1 );
						break;
					}
					--iIdx;
				}
				return sTemp;
			}
		}
	}
);

/******************************************************************************/

var ZNode = Class.create();

ZNode.prototype =
{
	initialize: function()
	{
		
	},
	
	getElement: function()
	{
		return this.oNode;
	},
	
	oNode: null
}

/******************************************************************************/

var ZClearNode = Class.create();

ZClearNode.prototype = Object.extend(
	new ZNode(),
	{
		initialize: function()
		{
			this.oNode = Builder.node(
				'br',
				{
					style: 'line-height: 0; height: 0; ' +
					       'clear: both;'
				}
			);
		}
	}
);

/******************************************************************************/

var ZEditor = Class.create();

ZEditor.prototype = 
{
	initialize: function( oTextArea, aOpts )
	{
		
		this.sFileBrowserUrl = aOpts.sFileBrowserUrl || false;
		this.sFileDir = aOpts.sFileDir || false;
		this.sImageBrowserUrl = aOpts.sImageBrowserUrl || false;
		this.sImageDir = aOpts.sImageDir || false;
		this.sUploadUrl = aOpts.sUploadUrl || false;
		
		// The main div in which all of the editors components get shoved.
		this.oMainDiv = Builder.node( 
			'div',
			{
				className: 'zeditor',
				style: 'width: ' + ( aOpts.iWidth || 100 ) + '; ' +
				       'height: ' + ( aOpts.iHeight || 100 ) + '; '
			}
		);
		
		// Add the main div to the document in the textarea's stead
		oTextArea.parentNode.replaceChild( this.oMainDiv, oTextArea );
		
		this.oTextArea = oTextArea;
		Element.addClassName( this.oTextArea, 'textarea' );
		this.oTextArea.style.width = this.oMainDiv.style.width;
		this.oTextArea.style.overflow = 'scroll';
		this.oTextArea.value = $F( this.oTextArea ).trim();
		
		// The toolbar in which the buttons are placed
		this.oToolBarDiv = Builder.node( 
			'div', 
			{ 
				className: 'toolbar',
				style: 'overflow: auto; width: ' + this.oTextArea.style.width + '; display: block;'
			} 
		);
		this.oToolBarDiv.appendChild( 
			new ZWrapButton( 
				this.oTextArea, 
				'p', 
				{ 
					sLabel: 'paragraph',
					sClassName: 'zeditor-paragraph-button'
				} 
			).getElement() 
		);
		this.oToolBarDiv.appendChild( 
			new ZWrapButton( 
				this.oTextArea, 
				'strong', 
				{ 
					sLabel: 'bold',
					sClassName: 'zeditor-bold-button'
				} 
			).getElement() 
		);
		this.oToolBarDiv.appendChild( 
			new ZWrapButton( 
				this.oTextArea, 
				'em', 
				{ 
					sLabel: 'emphasis',
					sClassName: 'zeditor-italic-button'
				} 
			).getElement() 
		);
		this.oToolBarDiv.appendChild( 
			new ZWrapButton( 
				this.oTextArea, 
				'br', 
				{ 
					sLabel: 'line break',
					sClassName: 'zeditor-break-button',
					bSelfClosing: true
				} 
			).getElement() 
		);
		this.oToolBarDiv.appendChild( 
			new ZWrapButton( 
				this.oTextArea, 
				'hr', 
				{ 
					sLabel: 'horizontal rule',
					sClassName: 'zeditor-hr-button',
					bSelfClosing: true
				} 
			).getElement() 
		);
		this.oToolBarDiv.appendChild(
			new ZPreviewDialogButton(
				this.oMainDiv,
				this.oTextArea,
				{
					sLabel: 'preview html',
					sClassName: 'zeditor-preview-button',
					sTitle: 'Preview HTML Page'
				}
			).getElement()
		);
		
		this.oMainDiv.appendChild( this.oToolBarDiv );
		this.oMainDiv.appendChild( this.oTextArea );
		this.oTextArea.style.height = ( parseInt( this.oMainDiv.style.height ) - parseInt( this.oToolBarDiv.clientHeight ) ) + 'px';
		
	}
};

/******************************************************************************/

var ZSelection = Class.create();

ZSelection.prototype =
{
	initialize: function( oTextArea )
	{
		this.oTextArea = oTextArea
	},
	
	fixCaretPosIE: function()
	{
		this.oTextArea.focus();
		if ( document.selection )
			this.oTextArea.caretPos = document.selection.createRange().duplicate();
		return this.oTextArea;
	},
	
	getCaretPos: function()
	{
		var oTextArea = this.oTextArea;
		oTextArea.focus();
		if ( document.selection )
		{
			oTextArea.focus( oTextArea.caretPos );
			return Math.abs( oTextArea.caretPos );
		}
		else if ( oTextArea.selectionStart )
			return oTextArea.selectionStart;
		else
			alert( 'Your browser won\'t let me get to the textarea.' );
	},
	
	getSelectedText: function()
	{
		var oTextArea = this.oTextArea;
		return Try.these(
			function() /* Internet Explorer */ 
			{
				oTextArea.focus();
				return document.selection.createRange().text; 
			},
			function() /* Mozilla, Safari, Konqui */
			{  
				var iStartIdx = oTextArea.selectionStart; 
				var iStopIdx = oTextArea.selectionEnd; 
				return oTextArea.value.substring( iStartIdx, iStopIdx );
			},
			function() /* Old Mozillas */
			{
				return document.getSelection(); 
			},
			function() /* What in the blue hell are you using? */ 
			{ 
				alert( 'Your browser does not support this feature.' ); 
				return false 
			}
		);
	},
	
	replaceSelectedText: function( sReplacement )
	{
		var oTextArea = this.oTextArea;
		return Try.these(
			function() /* Internet Explorer */
			{
				oTextArea.focus( oTextArea.caretPos );
				oTextArea.focus();
				if ( oTextArea.caretPos )
				{
					oTextArea.caretPos.text =
						oTextArea.caretPos.text.charAt(oTextArea.caretPos.text.length - 1) == ' ' 
						? sReplacement + ' ' 
						: sReplacement;
				}
				else
				{
					oTextArea.caretPos = document.selection.createRange().duplicate();
					oTextArea.caretPos.text = sReplacement;
					oTextArea.caretPos.moveStart( 'character', sReplacement.length * -1 );
					oTextArea.caretPos.select();
				}
			},
			function() /* Mozilla, Safari, Konqui */
			{
				oTextArea.focus();
				var iStartIdx = oTextArea.selectionStart;
				var iStopIdx = oTextArea.selectionEnd;
				
				/* User might have started highlighting backwards */
				if ( iStopIdx < iStartIdx )
				{
					var iTempIdx = iStopIdx;
					iStopIdx = iStartIdx;
					iStartIdx = iTempIdx;
				}
				
				/*
				 * We're replacing the whole damned textarea, grab the text preceding
				 * and postceding the selection. 
				 */
				var sPreText = oTextArea.value.substring( 0, iStartIdx );
				var sPostText = oTextArea.value.substring( iStopIdx, oTextArea.textLength );
				
				var iScroll = oTextArea.scrollTop;
				oTextArea.value = sPreText + sReplacement + sPostText;
				oTextArea.scrollTop = iScroll;
				oTextArea.selectionStart = iStartIdx;
				oTextArea.selectionEnd = iStartIdx + sReplacement.length;
			},
			function() /* Crap */
			{
				alert( 'Your browser does not support this feature.' );
				return false;
			}
		);
	}
}

/******************************************************************************/

var ZToolbarButton = Class.create();

ZToolbarButton.prototype = Object.extend(
	new ZNode(),
	{
		initializeToolbarButton: function( aOpts )
		{
			this.oNode = Builder.node(
				'div',
				{
					className: 'toolbar-button ' + ( aOpts.sClassName || '' ),
					title: ( aOpts.sLabel || '' )
				},
				[
					( ( aOpts.bShowLabel ) ? ( aOpts.sLabel || '' ) : '\u00A0' )
				]
			);
		}
	}
);

/******************************************************************************/

var ZDialogButton = Class.create();

ZDialogButton.prototype = Object.extend(
	new ZToolbarButton(),
	{
		oBodyNode: null,
		
		oTitleNode: null,
		
		initializeDialogButton: function( oMainDiv, oTextArea, aOpts )
		{
			this.initializeToolbarButton( aOpts || {} );
			this.oMainDiv = oMainDiv;
			this.oTextArea = oTextArea;
			this.sTitle = aOpts.sTitle || {};
			
			var oDialog = this;
			this.oNode.onmousedown = function( oEvent )
			{
				if ( this.oTextArea )
					this.oTextArea = new ZSelection( this.oTextArea ).fixCaretPosIE();
				Event.stop( oEvent );
				/*
				 * Create a dialog to show to the user
				 */
				this.showDialog();
			}.bindAsEventListener( this );
		},
		
		closeDialog: function()
		{
			new Effect.Fade( 
				this.oDialogNode,
				{
					afterFinish: function()
					{
						if ( this.oDialogNode.parentNode )
							this.oDialogNode.parentNode.removeChild( this.oDialogNode );
					}.bindAsEventListener( this )
				}
			);
		},
		
		showDialog: function()
		{
			// Sample implementation
			var aMainDivPos = Position.cumulativeOffset( this.oMainDiv );
			this.iDialogWidth = parseInt( this.oMainDiv.clientWidth );
			this.iDialogHeight = parseInt( this.oMainDiv.clientHeight );
			this.oDialogNode = Builder.node(
				'div',
				{
					className: 'zeditor-dialog',
					style: 'position: absolute; ' +
					       'width: ' + this.iDialogWidth + 'px; ' +
					       'height: ' + this.iDialogHeight + 'px; ' +
					       'top: ' + aMainDivPos[1] + 'px; ' +
					       'left: ' + aMainDivPos[0] + 'px; ' +
					       'display: none; '
				},
				[
					this.oTitleNode = Builder.node(
						'div',
						{
							
						},
						[
							Builder.node(
								'h1',
								{ },
								[ this.sTitle ]
							)
						]
					),
					this.oBodyNode = Builder.node( 
						'div',
						{ style: 'overflow: auto;' },
						[
							 Builder.node(
								'div', 
								{ 
									className: 'dialog-body'
								}, 
								[
									this.getDialogContent()
								]
							)
						]
					)
				]
			);
			
			var oCloseButton = Builder.node(
				'div',
				{
					className: 'close-button',
					style: 'position: absolute; ' +
								 'width: 16px; ' +
								 'height: 16px; ' +
								 'top: 3px; ' +
								 'left: ' + ( this.iDialogWidth - 19 ) + 'px; '
				},
				[
					'\u00A0'
				]
			);
			oCloseButton.onclick = this.closeDialog.bindAsEventListener( this );

			this.oDialogNode.appendChild( oCloseButton );
			document.body.appendChild( this.oDialogNode );

			new Effect.Appear( 
				this.oDialogNode,
				{
					afterSetup: function()
					{
						this.oBodyNode.style.height = this.iDialogHeight - this.oTitleNode.offsetHeight + 'px';
					}.bindAsEventListener( this )
				}
			);
		},
		
		getDialogContent: null
	}
);

/******************************************************************************/

var ZAnchorDialogButton = Class.create();

ZAnchorDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function()
		{
			var args = $A( arguments );
			this.sFileBrowserUrl = args[2].sFileBrowserUrl || false;
			this.sFileDir = args[2].sFileDir || false;
			this.sUploadUrl = args[2].sUploadUrl || false;
			this.initializeDialogButton( args[0], args[1], args[2] || {} );
		},
		
		getDialogContent: function()
		{
			this.oContentNode = Builder.node( 
				'div', 
				{ },
				[
					'This dialog helps you create a link to another Web page. For example, ',
					Builder.node( 'a', { href: 'http://www.google.com' }, [ 'this is a link to Google.' ] ),
					' A link is the Web address to the page; remember to prefix it with http:// if you\'re ' +
					'linking to an external Web site.'
				]
			);
			
			if ( this.sFileBrowserUrl )
			{
				var oFileButton = new ZFileBrowserDialogButton(
					this,
					{
						sLabel: 'browse files',
						sTitle: 'Browse Files',
						sClassName: 'zeditor-file-browser-button'
					}
				);
				var oUploadButton = new ZUploadButton(
					this.sUploadUrl,
					'file',
					{
						sLabel: 'upload files',
						sTitle: 'Upload Files',
						sClassName: 'zeditor-upload-button'
					}
				);
			}
			this.oLinkForm = Builder.node(
				'form',
				{ },
				[
					Builder.getFormRow( 'Link', 'LinkUrl', null, [ oFileButton ? oFileButton.getElement() : null, oUploadButton ? oUploadButton.getElement() : null ] ),
					Builder.getFormRow( 'Title', 'LinkTitle' ),
					Builder.getFormRow( 'Open in new window?', 'IsNewWindow', Builder.node( 'input', { type: 'checkbox' } ) )
				]
			);
			this.oContentNode.appendChild( this.oLinkForm );
			
			var oInsertButton = Builder.node(
				'input',
				{
					type: 'button',
					value: 'Insert Hyperlink'
				}
			);
			oInsertButton.onClickEvent = this.insertHyperlink.bindAsEventListener( this );
			Event.observe( oInsertButton, 'click', oInsertButton.onClickEvent, false );
			
			this.oContentNode.appendChild( oInsertButton );
			return this.oContentNode;
		},
		
		insertHyperlink: function( oEvent )
		{
			// IE won't update the named elements array for forms, so we have to refer
			// to them by explicit index number
			var sLink = '';
			sLink = '\n<a href="' + this.oLinkForm.elements[0].value + '" '; // 0 == LinkUrl
			if ( this.oLinkForm.elements[2].checked ) // 2 == IsNewWindow
			{
				// target attribute is deprecated
				sLink += 'onclick="window.open( this.href ); return false;"';
			}
			sLink += '>\n';
			sLink += this.oLinkForm.elements[1].value + '\n'; // 1 == LinkTitle
			sLink += '</a>\n';
			new ZSelection( this.oTextArea ).replaceSelectedText( sLink );
			this.closeDialog();
		}
		
	}
);

/******************************************************************************/

var ZImageDialogButton = Class.create();

ZImageDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function()
		{
			var args = $A( arguments );
			this.sImageBrowserUrl = args[2].sImageBrowserUrl || false;
			this.sImageDir = args[2].sImageDir || false;
			this.sUploadUrl = args[2].sUploadUrl || false;
			this.initializeDialogButton( args[0], args[1], args[2] || {} );
		},
		
		getDialogContent: function()
		{
			this.oContentNode = Builder.node( 
				'div', 
				{ },
				[
					'This dialog helps you create a link to an image on this or another Web site. ',
					' The src is the Web address for the image; remember to prefix it with http:// ' +
					' if you\'re using an image on another Web site. Alternate text is what gets ' +
					' displayed if the image is unavailable (and, sometimes, while the image is loading).'
				]
			);
			
			if ( this.sImageBrowserUrl )
			{
				var oImageButton = new ZImageBrowserDialogButton(
					this,
					{
						sLabel: 'browse images',
						sTitle: 'Browse Images',
						sClassName: 'zeditor-image-browser-button'
					}
				);
				var oUploadButton = new ZUploadButton(
					this.sUploadUrl,
					'image',
					{
						sLabel: 'upload images',
						sTitle: 'Upload Images',
						sClassName: 'zeditor-upload-button'
					}
				);
			}
			this.oImageForm = Builder.node(
				'form',
				{ },
				[
					Builder.getFormRow( 'Image (Src)', 'Src', null, [ oImageButton ? oImageButton.getElement() : null, oUploadButton ? oUploadButton.getElement() : null ]  ),
					Builder.getFormRow( 'Alternate Text', 'Alt' )
				]
			);
			this.oContentNode.appendChild( this.oImageForm );

			var oInsertButton = Builder.node(
				'input',
				{
					type: 'button',
					value: 'Insert Image'
				}
			);
			oInsertButton.onClickEvent = this.insertImage.bindAsEventListener( this );
			Event.observe( oInsertButton, 'click', oInsertButton.onClickEvent, false );
			
			this.oContentNode.appendChild( oInsertButton );
			return this.oContentNode;
		},
		
		insertImage: function( oEvent )
		{
			// IE won't update the named elements array for forms, so we have to refer
			// to them by explicit index number
			var sLink = '';
			sLink = '\n<img src="' + this.oImageForm.elements[0].value + '" '; // 0 == Src
			sLink +=' alt="' + this.oImageForm.elements[1].value + '" />\n';
			new ZSelection( this.oTextArea ).replaceSelectedText( sLink );
			this.closeDialog();
		}
		
	}
);

/******************************************************************************/

var ZFileBrowserDialogButton = Class.create();

ZFileBrowserDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function( oAnchorDialog )
		{
			var args = $A( arguments );
			this.oAnchorDialog = oAnchorDialog;
			this.sFileBrowserUrl = oAnchorDialog.sFileBrowserUrl;
			this.sFileDir = oAnchorDialog.sFileDir;
			this.initializeDialogButton( this.oAnchorDialog.oMainDiv, null, args[1] || { } );
		},
		
		getDialogContent: function()
		{
			this.oContentNode = Builder.node( 'div', { style: 'display: none;' }, [ ] );
			this.onCompleteEvent = this.registerEventHandlers.bindAsEventListener( this );
			new Ajax.Updater(
				this.oContentNode,
				this.sFileBrowserUrl,
				{
					method: 'get',
					parameters: 'fetch=files',
					onComplete: this.onCompleteEvent,
					onFailure: function( oRequest )
					{
						this.oContentNode.appendChild( document.createTextNode( 'The request failed.' ) );
						new Effect.Shake( this.oContentNode );
					}
				}
			);
			
			return this.oContentNode;
		},
		
		registerEventHandlers: function( oEvent )
		{
			new Effect.Appear( this.oContentNode );
			var self = this;
			document.getElementsByClassName( 'zeditor-file-browser-file', this.oContentNode ).each(
				function( oValue, iIdx )
				{
					oValue.onclick = function()
					{
						this.oAnchorDialog.oLinkForm.elements[0].value = this.oAnchorDialog.sFileDir + oValue.title;
						this.closeDialog();
					}.bindAsEventListener( self );
				}
			);
		}
	}
);

/******************************************************************************/

var ZImageBrowserDialogButton = Class.create();

ZImageBrowserDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function( oImageDialog )
		{
			var args = $A( arguments );
			this.oImageDialog = oImageDialog;
			this.sImageBrowserUrl = oImageDialog.sImageBrowserUrl;
			this.sImageDir = oImageDialog.sImageDir;
			this.initializeDialogButton( this.oImageDialog.oMainDiv, null, args[1] || { } );
		},
		
		getDialogContent: function()
		{
			this.oContentNode = Builder.node( 'div', { style: 'display: none;' }, [ ] );
			this.onCompleteEvent = this.registerEventHandlers.bindAsEventListener( this );

			new Ajax.Updater(
				this.oContentNode,
				this.sImageBrowserUrl,
				{
					method: 'get',
					parameters: 'fetch=images&dir=' + this.sImageDir,
					onComplete: this.onCompleteEvent,
					onFailure: function( oRequest )
					{
						this.oContentNode.appendChild( document.createTextNode( 'The request failed.' ) );
						new Effect.Shake( this.oContentNode );
					}
				}
			);
			
			return this.oContentNode;
		},
		
		registerEventHandlers: function( oEvent )
		{
			new Effect.Appear( this.oContentNode );
			var self = this;
			document.getElementsByClassName( 'zeditor-image-browser-file', this.oContentNode ).each(
				function( oValue, iIdx )
				{
					oValue.onclick = function()
					{
						this.oImageDialog.oImageForm.elements[0].value = this.oImageDialog.sImageDir + oValue.title;
						this.closeDialog();
					}.bindAsEventListener( self );
				}
			);
		}
	}
);

/******************************************************************************/

var ZListDialogButton = Class.create();

ZListDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function( sType )
		{
			this.sType = sType;
			var args = $A( arguments );
			this.initializeDialogButton( args[1], args[2], args[3] || {} );
		},
		
		getDialogContent: function()
		{
			var oContentNode = Builder.node( 'div' );
			oContentNode.appendChild(
				Builder.node( 'div', { }, [ 'If you don\'t need a list item, just leave it blank.' ] )
			);
			
			var oListNode = Builder.node( 'div' );
			
			var oItemNode = Builder.getFormRow( 'List Item', 'ListItem[]' );
			for ( var i = 1; i <= 5; ++i )
			{
				oListNode.appendChild( oItemNode.cloneNode( true ) );
			}
			oContentNode.appendChild( oListNode );
			
			var oInsertListItemButton = Builder.node(
				'input',
				{
					type: 'button',
					value: 'Insert Another List Item'
				},
				[ ]
			);
			oInsertListItemButton.onclick = function()
			{
				oListNode.appendChild( oItemNode.cloneNode( true ) );
				
			}.bindAsEventListener( oInsertListItemButton );
			oContentNode.appendChild( oInsertListItemButton );
			oContentNode.appendChild( new ZClearNode().getElement() );
			var oInsertButton = Builder.node( 
				'input', 
				{ 
					type: 'button',
					value: 'Insert List'
				}, 
				[ ] 
			);
			oInsertButton.onclick = function()
			{
				var sList = '\n<' + this.sType + '>\n';
				$A( oContentNode.getElementsByTagName( 'input' ) ).each(
					function( oInput, iIdx )
					{
						if ( oInput.type == 'text' && $F( oInput ) != '' )
						{
							sList += '\t<li>' + $F( oInput ) + '</li>\n';
						}
					}
				);
				sList += '</' + this.sType + '>\n';
				new ZSelection( this.oTextArea ).replaceSelectedText( sList );
				this.closeDialog();
			}.bindAsEventListener( this );
			oContentNode.appendChild( oInsertButton );
			return oContentNode;
		}
	}
);

/******************************************************************************/

var ZPreviewDialogButton = Class.create();

ZPreviewDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function()
		{
			var args = $A( arguments );
			this.initializeDialogButton( args[0], args[1], args[2] || {} );
		},
		
		getDialogContent: function()
		{
			var oContentNode = Builder.node(
				'div',
				{
					style: 'background-color: white;' + 
					       'padding: 1em;'
				},
				[ ]
			);
			/*
			 * Parse the innerHTML for well-formedness
			 */
			if ( window.ActiveXObject ) /* Internet Explorer */
			{
				var oXmlDoc = new ActiveXObject( 'Microsoft.XMLDOM' ); 
				oXmlDoc.async = 'false'; 
				oXmlDoc.loadXML( '<xml>' + $F( this.oTextArea ) + '</xml>' );
				if ( oXmlDoc.parseError != 0 )
				{
					var oError = oXmlDoc.parseError;
					oContentNode.appendChild(
						Builder.node(
							'div',
							{ },
							[
								Builder.node( 'h2', { }, [ 'There is an error in your document' ] ),
								Builder.node( 'p', { }, [ 'Code ' + oError.errorCode ] ),
								Builder.node( 'p', { }, [ 'at line ' + oError.line ] ),
								Builder.node( 'p', { }, [ 'at column ' + oError.linepos ] ),
								Builder.node( 'p', { }, [ 'with reason: ' + oError.reason ] )
							]
						)
					);
				}
				else
				{
					oContentNode.innerHTML = $F( this.oTextArea );
				}
			}
			else if ( document.implementation.createDocument ) /* Mozilla */
			{
				var oParser = new DOMParser(); 
				try
				{
					var oXmlDoc = oParser.parseFromString( 
						'<xml>' + $F( this.oTextArea ) + '</xml>', 
						"text/xml"
					);
				}
				catch ( oException )
				{
					/*
					 * Opera
					 */
					oContentNode.appendChild(
						Builder.node(
							'div',
							{ },
							[
								Builder.node( 'h2', { }, [ 'There is an error in your document' ] ),
								Builder.node( 'p', { }, [ 'Code: ' + oException.code ] )
							]
						)
					);
					return oContentNode;
				}
				if ( oXmlDoc.documentElement.tagName == "parsererror" )
				{
					/*
					 * Firefox
					 */
					oContentNode.appendChild(
						Builder.node(
							'div',
							{ },
							[
								Builder.node( 'h2', { }, [ 'There is an error in your document' ] ),
								Builder.node( 'p', { }, [ oXmlDoc.documentElement.firstChild.data ] ),
								Builder.node( 'pre', { }, [ oXmlDoc.documentElement.firstChild.nextSibling.firstChild.data ] )
							]
						)
					);
					
				}
				else
				{
					oContentNode.innerHTML = $F( this.oTextArea );
				}
			}
			else
			{
				oContentNode.innerHTML = $F( this.oTextArea );
			}
			return oContentNode;
		}
	}
);

/******************************************************************************/

var ZHighlightDialogButton = Class.create();

ZHighlightDialogButton.prototype = Object.extend(
	new ZDialogButton(),
	{
		initialize: function()
		{
			var args = $A( arguments );
			this.initializeDialogButton( args[0], args[1], args[2] || {} );
		},
		
		getDialogContent: function()
		{
			var oContentNode = Builder.node(
				'div',
				{
					style: 'background-color: white;' + 
					       'padding: 1em;'
				},
				[ ]
			);
			
			var oHighlighter = new dp.sh.Brushes['Xml']();
			oHighlighter.addGutter = true;
			oHighlighter.addControls = false;
			oHighlighter.collapse = false;
			oHighlighter.firstLine = 1;
			oHighlighter.Highlight( this.oTextArea.value );

			oContentNode.appendChild( 
				Builder.node(
					'div',
					{
						className: 'dp-highlighter'
					},
					[
						oHighlighter.table
					]
				)
			);
			
			oHighlighter.table.style.height = oContentNode.clientHeight;
			
			return oContentNode;
		}
	}
);
		
/******************************************************************************/

var ZUploadButton = Class.create();

ZUploadButton.prototype = Object.extend( 
	new ZToolbarButton(),
	{
		initialize: function( sUrl, sType, aOpts )
		{
			this.initializeToolbarButton( aOpts || {} );
			this.oNode.onmousedown = function( oEvent )
			{
				window.open( 
					sUrl + '?type=' + sType,
					'_blank',
					'width=400,height=400,scrollbars=yes,dependent=yes,toolbar=no,status=no'
				);
			}.bindAsEventListener( this.oNode );
		}
	}
);

/******************************************************************************/

var ZWrapButton = Class.create();

ZWrapButton.prototype = Object.extend( 
	new ZToolbarButton(),
	{
		initialize: function( oTextArea, sStartTag, aOpts )
		{
			this.initializeToolbarButton( aOpts || {} );
			this.oNode.sStartTag = sStartTag;
			this.oNode.bSelfClosing = aOpts && aOpts.bSelfClosing || false;
			this.oNode.sEndTag = aOpts && aOpts.sEndTag || sStartTag;
			this.oNode.onmousedown = function( oEvent )
			{
				Event.stop( oEvent );
				var oSelector = new ZSelection( oTextArea );
				var sSelectedText = oSelector.getSelectedText();
				if ( this.bSelfClosing )
				{
					var sReplacementText = '<' + this.sStartTag + ' />';
				}
				else
				{
					var sReplacementText = '<' + this.sStartTag + '>' + 
					                       sSelectedText + 
					                       '</' + this.sEndTag + '>';
				}
				oSelector.replaceSelectedText( sReplacementText );
				oTextArea.caretPos = null;
			}.bindAsEventListener( this.oNode );
		}
	}
);

/******************************************************************************/



