/* ***************************************************************
 *
 * Copyright (c) TimeFrame, Inc. 2005, 2007
 * 
 * File: jswl-core.js
 * Created: January 05, 2006 (20060501)
 * Description:
 *
 * JavaScript Widget Library widget definitions.
 * 
 * ***************************************************************
 * CHANGE HISTORY
 * ***************************************************************
 * DATE     USER       RECORD       DESCRIPTION
 * ---------------------------------------------------------------
 * 20060105 dewittsc   ------       Initial version.
 * 
 * *************************************************************** */

/* **********************************************************************

 Implied IDL Models for interfaces referenced in this file.
 
 interface Writer {
 	void						write( in string data );
 	void						writeln( in string data );
 }
 
 interface Renderable {
 	void						renderTo( in Writer out );
 	void						render();
 }
  
 interface ControlPeer {
 	void						hookPeer();
 	void						unhookPeer();
 }
 
 interface EventProducer {
 	void               			addEventListener( in string type, in EventListener listener );
 	sequence<EventListener>   	getEventListeners( in string type );
 	void               			removeEventListener( in string type, in EventListener listener );
 	void               			fireEvent( in Event event );
 }
 
 interface EventListener {
 	boolean               			handleEvent( in Event event, in string type );
 }
 
 interface InputChangeProducer {
 	void               				addInputChangeListener( in InputChangeListener listener );
 	sequence<InputChangeListener>   getInputChangeListeners();
 	void               				removeInputChangeListener( in InputChangeListener listener );
 	void               				fireInputChange( in Event event );
 }
 
 interface InputChangeListener {
 	boolean						handleInputChange( in Event event );
 }
 
 interface SelectionChangeProducer {
 	void               					addSelectionChangeListener( in SelectionChangeListener listener );
 	sequence<SelectionChangeListener>   getSelectionChangeListeners();
 	void               					removeSelectionChangeListener( in SelectionChangeListener listener );
 	void               					fireSelectionChange( in Event event );
 }
 
 interface SelectionChangeListener {
 	boolean						handleSelectionChange( in Event event );
 }
  
 interface ActionProducer {
 	void               					addActionListener( in ActionListener listener );
 	sequence<ActionListener>   			getActionListeners();
 	void               					removeActionListener( in ActionListener listener );
 	void               					fireAction( in Event event );
 }
 
 interface ActionListener {
 	boolean						handleAction( in Event event );
 }
  
 interface Action {
 	attribute string			id;
	attribute string			title;
	attribute string			icon;
	attribute string			sizeHint;
 	void						execute();
 }
  
 interface ListModelChangeListener {
 	boolean				handleListModelChange( in Event event );
 }
 	
 interface ListModelChangeProducer {
 	void               						addListModelChangeListener( in string type, in ListModelChangeListener listener );
 	sequence<ListModelChangeListener>   	getListModelChangeListeners( in string type );
 	void               						removeListModelChangeListener( in string type, in ListModelChangeListener listener );
 	void               						fireModelChange( in Event event );
 }
 
 interface ListModel:ListModelChangeProducer {
 	boolean                     indexOf( in long index );
 	any                      	getElementAt( in long index );
 	long						size();
 }
 
 In addition, the following function pointer types are inferred.
 
 layoutProc * void ( in Writer out, in Container container );
 
 eventProc * boolean ( in Event event, in Widget widget, in long type );
 
 ajaxSuccessProc * void ( in XMLHttpRequest req, in Action action );
 
 ajaxErrorProc * void ( in XMLHttpRequest req, in Action action ); 
 
 * ********************************************************************** */
 
/* **********************************************************************
 * Constant Definitions
 * ********************************************************************** */
 
var JS_EVENT_KEYS = new Array( 'onmousedown',
							   'onmouseout',
							   'onmouseover', 
							   'onclick', 
							   'onmouseup', 
							   'onkeydown', 
							   'onkeypress', 
							   'onkeyup',
							   'onchange',
							   'onfocus',
							   'onblur' );			   
var SINGLE_SELECT_TYPE		= 0x01;
var MULTI_SELECT_TYPE 		= 0x02; 
var DIV_CONTAINER_TYPE		= 0x01;
var SPAN_CONTAINER_TYPE		= 0x02;
var BLOCK_BOX_TYPE			= 0x01;
var INLINE_BOX_TYPE			= 0x02;
var COMPACT_BOX_TYPE		= 0x04;
var RUNIN_BOX_TYPE			= 0x08;
var HALIGN_LEFT				= 0x01;
var HALIGN_CENTER			= 0x02;
var HALIGN_RIGHT			= 0x04;
var VALIGN_TOP				= 0x10;
var VALIGN_MIDDLE			= 0x20;
var VALIGN_BOTTOM			= 0x40;
var REFRESH_WAIT_DELAY		= 250;
var DO_DEFAULT_ACTION		= true;
var CANCEL_DEFAULT_ACTION	= false;

/**
 * Constructor for a simple point object representing a screen (x,y)
 * coordinate.
 *
 * <idl>
 * interface Point 
 * {
 *	 attribute	long	x;
 *   attribute	long	y;
 * }
 * </idl>
 *
 * @param x   the screen X coordinate.
 * @param y   the screen Y coordinate.
 */
 
function Point( x, y )
{
	this.x = x;
	this.y = y;
}

/**
 * Constructor for a simple rectangle object representing a screen (x,y)
 * coordinate, a width, and a height.
 *
 * <idl>
 * interface Rect 
 * {
 *	 attribute	long	top;
 *   attribute	long	left;
 *   attribute	long	width;
 *   attribute	long	height;
 * }
 * </idl>
 * 
 * @param p        the screen point representing the (top,left) of the rectangle.
 * @param width    the rectangle width.
 * @param height   the rectangle height.
 */
 
function Rect( p, width, height )
{
	this.top = p.x;
	this.left = p.y;
	this.width = width;
	this.height = height;
}

Rect.prototype.toCssString =
	function()
	{
		var css = "rect( ";
		
		if ( this.top == -1 ) { css += 'auto'; }
		else { css += this.top + 'px'; }
		
		css += ',';
		
		if ( this.left == -1 || this.width == -1 ) { css += 'auto'; }
		else { css += ( this.left + this.width ) + 'px'; }
		
		css += ',';
		
		if ( this.top == -1 || this.height == -1 ) { css += 'auto'; }
		else { css += ( this.top + this.height ) + 'px'; }
		
		css += ',';
		
		if ( this.left == -1 ) { css += 'auto'; }
		else { css += this.left + 'px'; }
		
		css += ' )';
		
		return css;
	};
	
/**
 * Constructor for a simple color object representing a screen (r,g,b)
 * color.
 *
 * <idl>
 * interface Rgb 
 * {
 *	 attribute	long	r;
 *   attribute	long	g;
 *   attribute	long	b;
 * }
 * </idl>
 *
 * @param s   an RGB value in hexadecimal string format (RRGGBB).
 */
 
function Rgb( s )
{
	var r = s.substring( 0, 2 );
	var g = s.substring( 2, 4 );
	var b = s.substring( 4, 6 );
	
	this.r = parseInt( r, 16 );
	this.g = parseInt( g, 16 );
	this.b = parseInt( b, 16 );
}

/**
 * Constructor for JSWL writer which collects its output as a string.
 *
 * <idl>
 * interface StringWriter:Writer 
 * {
 *	 attribute	string			output;
 *	 void						write( in string data );
 *   void						writeln( in string data );
 * }
 * </idl>
 *
 */
 
function StringWriter()
{
	this.output = '';
}

StringWriter.prototype.write =
	function ( data )
	{
		this.output += data;
	};
	
StringWriter.prototype.writeln =
	function ( data )
	{
		this.output += data;
		this.output += '\n';
	};
	
StringWriter.prototype.reset =
	function ()
	{
		this.output = '';
	};
	
/**
 * Constructor for all JSWL widgets.
 *
 * <idl>
 * interface JsWidget:ControlPeer,Renderable,EventProducer 
 * {
 *   attribute  string          wid;
 *   attribute  long			layoutType;
 *	 attribute	string			styleClass;
 *	 attribute	boolean			isVisible;
 *	 attribute	boolean			enabled;
 *   attribute  long            alignment;
 *   attribute  Object          peer;   
 *	 Rect						getBounds();
 *   void						setLayoutType( in long type );
 *   void						setEnabled( in boolean enabled );
 *	 void						setVisible( in boolean visibility );
 *   void                       setFocus( in boolean focus );
 *	 void						setStyleClass( in string clazz );
 *   void                       setLocation( in Point origin );
 *   void                       setAlignment( in long alignment );
 *   void                       invalidate();
 *   void                       link();
 *   void                       unlink();
 * }
 * </idl>
 *
 * @param wid   a unique widget id.
 */
 
function JsWidget( wid )
{
	this.wid = wid;
	this.layoutType = BLOCK_BOX_TYPE;
	this.styleClass = "";
	this.isVisible = true;
	this.enabled = true;
	this.alignment = ( HALIGN_CENTER | VALIGN_MIDDLE );
	this.eventListeners = new Array();
}

JsWidget.prototype.getBounds = 	
	function ()
	{
		if ( this.peer )
		{
			return new Rect( getScreenOrigin( this.peer ), this.peer.offsetWidth, this.peer.offsetHeight );
		}
		return null;
	};
	
JsWidget.prototype.setLayoutType =
	function ( layoutType )
	{
		this.layoutType = layoutType;
		if ( this.peer )
		{
			this.setVisible( true );
		}
	};
	
JsWidget.prototype.setEnabled =
	function ( enabled )
	{
		this.enabled = enabled;
	};
		
JsWidget.prototype.setVisible =
	function ( visibility, transition )
	{
		this.isVisible = visibility;
		if ( this.peer )
		{
			var 	boxType = 'block';
			
			if ( this.isVisible )
			{
				switch ( this.layoutType )
				{
					case BLOCK_BOX_TYPE:
						boxType = 'block';
						break;
						
					case INLINE_BOX_TYPE:
						boxType = 'inline';
						break;
						
					case COMPACT_BOX_TYPE:
						boxType = 'compact';
						break;
					
					case RUNIN_BOX_TYPE:
						boxType = 'run-in';
						break;
				}
			}
			else
			{
				boxType = 'none';
			}
			
			if ( this.isVisible )
			{
				showBox( this.peer, boxType, transition );
			}
			else if ( !this.isVisible )
			{
				hideBox( this.peer, transition );
			}
		}
	};

JsWidget.prototype.setFocus =
	function ( focus )
	{
		if ( this.peer )
		{
			setFocus( this.peer, focus );
		}
	};
	
JsWidget.prototype.setStyleClass =	
	function ( styleClass )
	{
		this.styleClass = styleClass;
		if ( this.peer )
		{
			this.peer.className = styleClass;
		}	
	};
	
JsWidget.prototype.setLocation =
	function ( origin )
	{
		if ( this.peer )
		{
			this.peer.style.top = origin.y + 'px';
			this.peer.style.left = origin.x + 'px';
		}
	};	
	
JsWidget.prototype.setAlignment =
	function ( alignment )
	{
		this.alignment = alignment;
	};
	
JsWidget.prototype.invalidate =
	function ()
	{
		this.unhookPeer();
	};
		
JsWidget.prototype.link =
	function ()
	{
	};
	
JsWidget.prototype.unlink =
	function ()
	{
	};
			
/* **********************************************************************
 * Implement ControlPeer
 * ********************************************************************** */
				
JsWidget.prototype.hookPeer =	
	function () 
	{ 
		peer = getElement(this.wid);
		if ( peer != null )
		{
			this.peer = peer;
			this.peer.jsWidget = this;
			
			for ( var i = 0; i < JS_EVENT_KEYS.length; i++ )
			{
				var key = JS_EVENT_KEYS[i];
				this.peer[key] = function( event ) { return this.jsWidget.fireEvent( getEvent( event ) ); };
			}
			
			this.link();			
		}
	};

JsWidget.prototype.unhookPeer =
	function ()
	{
		this.unlink();
		
		for ( var i = 0; i < JS_EVENT_KEYS.length; i++ )
		{
			var key = JS_EVENT_KEYS[i];
			this.peer[key] = null;
		}		
		
		this.peer.jsWidget = null;
		this.peer = null;
	};
	
/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */
	
JsWidget.prototype.renderTo =
	function ( out )
	{
	};
		
JsWidget.prototype.render =
	function ( out )
	{
		if ( this.peer )
		{
			throw "Peer already rendered.";
		}	
		
		this.renderTo( ( out === undefined ? document : out ) );	
		this.hookPeer();		
	};
		
/* **********************************************************************
 * Implement EventProducer
 * ********************************************************************** */

JsWidget.prototype.addEventListener =
	function ( key, listener )
	{
		if ( !this.eventListeners[key] ) { this.eventListeners[key] = new Array(); }
		var count = this.eventListeners[key].length;
		this.eventListeners[key][count] = listener;
	};
	
JsWidget.prototype.getEventListeners =
	function ( key )
	{
		return this.eventListeners[key];
	};
	
JsWidget.prototype.removeEventListener =
	function ( key, listener )
	{
		if ( !this.eventListeners[key] ) { this.eventListeners[key] = new Array(); }
		var index = this.eventListeners[key].indexOf( listener );
		if ( index != -1 )
		{
			this.eventListeners[key].splice( index, 1 );
		} 
	};
	
JsWidget.prototype.fireEvent =
	function ( event )
	{
		var type = getEventType( event );
		var listeners = this.getEventListeners( type );
		var rc = DO_DEFAULT_ACTION;
			
		if ( listeners && listeners.length > 0 )
		{
			for ( var i = 0; i < listeners.length; i++ )
			{
				var listener = listeners[i];
				
				if ( listener.handleEvent )
				{
					rc = listener.handleEvent( event, type );
				}
				else if ( listener )
				{ 
					rc = listener( event, type, this );
				}
			}
		}	
		
		return rc;		
	};
	
/**
 * Constructor for JSWL panel widgets.  The container widget is rendered
 * by an HTML DIV element.
 *
 * <idl>
 * interface JsContainer:JsWidget,Renderable,ControlPeer
 * {
 *   attribute  long            type;
 *	 attribute	boolean			align;
 *   void                       refresh();
 *	 void						add( in Renderable renderable );
 *   void                       remove( in Renderable renderable );
 *   void                       removeAll();
 *	 void						setLayout( in Layout layout );
 * }
 * </idl>
 *
 * @param wid    a unique widget id.
 * @param type   a container type.
 */
 
function JsContainer( wid, type )
{
	this.base = JsWidget;
	this.base(wid);
	this.type = type;
	this.layoutType = ( this.type == DIV_CONTAINER_TYPE ? BLOCK_BOX_TYPE : INLINE_BOX_TYPE );
	this.children = new Array();	
	this.layout = null;
}

JsContainer.prototype = new JsWidget;

JsContainer.prototype.finishRefresh =
	function ()
	{
		var childCount = this.children.length;
		
		for ( var i = 0; i < childCount; i++ )
		{
			var child = this.children[i];
			if ( child.hookPeer )
			{
				child.hookPeer();
			}
		}	
	};
	
JsContainer.prototype.refresh =
	function ()
	{
		if ( this.peer == null )
		{
			throw "Peer is null.";
		}
		
		var childCount = this.children.length;
		var out = new StringWriter();
		
		for ( var i = 0; i < childCount; i++ )
		{
			var child = this.children[i];
			if ( child.invalidate )
			{
				child.invalidate();
			}
			child.renderTo( out );
		}
		
		this.peer.innerHTML = out.output;
		
		setTimeout( this.finishRefresh, REFRESH_WAIT_DELAY );
	};
	
JsContainer.prototype.add =
	function ( renderable )
	{
		var length = this.children.length;
		this.children[length] = renderable;
		
		if ( this.peer )
		{
			this.refresh();
		}
	};
	
JsContainer.prototype.remove =
	function ( renderable )
	{
		var index = this.children.indexOf( renderable );
		
		if ( index != -1 )
		{
			this.children.splice( index, 1 );
		}
		
		if ( this.peer )
		{
			this.refresh();
		}		
	};
	
JsContainer.prototype.removeAll =
	function ()
	{
		var childCount = this.children.length;
		
		for ( var i = 0; i < childCount; i++ )
		{
			var child = this.children[i];
			if ( child.invalidate )
			{
				child.invalidate();
			}
		}		
		
		this.children.length = 0;
		
		if ( this.peer )
		{
			this.refresh();	
		}			
	};
	
JsContainer.prototype.setLayout =
	function ( layout )
	{
		this.layout = layout;
		if ( this.peer )
		{
			this.refresh();
		}
	};
	
/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */
	
JsContainer.prototype.renderTo =	
	function ( out )
	{
		if ( this.layout != null )
		{
			this.layout( out, this );
		}
		else
		{
			jsDivLayout( out, this );
		}
	};

/* **********************************************************************
 * Implement ControlPeer
 * ********************************************************************** */
	
JsContainer.prototype.hookPeer =	
	function () 
	{ 
		peer = getElement(this.wid);
		if ( peer != null )
		{	
			this.peer = peer;
			this.peer.jsWidget = this;
			
			for ( var i = 0; i < JS_EVENT_KEYS.length; i++ )
			{
				var key = JS_EVENT_KEYS[i];
				this.peer[key] = function( event ) { return this.jsWidget.fireEvent( getEvent( event ) ); };
			}
			
			var childCount = this.children.length;
			
			for ( var i = 0; i < childCount; i++ )
			{
				var child = this.children[i];
				if ( child.hookPeer )
				{
					child.hookPeer();
				}
			}
					
			this.link();
		}
	};
	
JsContainer.prototype.unhookPeer =
	function ()
	{
		if ( this.peer != null )
		{
			this.unlink();
			
			var childCount = this.children.length;
			
			for ( var i = 0; i < childCount; i++ )
			{
				var child = this.children[i];
				if ( child.unhookPeer )
				{
					child.unhookPeer();
				}
			}		
			
			for ( var i = 0; i < JS_EVENT_KEYS.length; i++ )
			{
				var key = JS_EVENT_KEYS[i];
				this.peer[key] = null;
			}		
			
			this.peer.jsWidget = null;
			this.peer = null;
		}
	};		

/**
 * Constructor for JSWL text markup widgets.  The text markup widget is rendered
 * by an HTML SPAN element containing the specified markup.  The internal markup
 * parsed in any way by JSWL.  It is inserted whole using the innerHTML property
 * of the HTML SPAN element.
 * 
 * <idl>
 * interface JsText:JsContainer
 * {
 *   attribute		string	text;
 *   void					setText( in string text );
 * }
 * </idl>
 *
 * @param wid    a unique widget id.
 * @param text   the text.
 */
 
function JsMarkup( wid, content, uri )
{
	this.base = JsWidget;
	this.base(wid);
	this.content = content;
	this.uri = uri;
}

JsMarkup.prototype = new JsWidget;

JsMarkup.prototype.loadUri =
	function ( async )
	{
	    var ajaxAction;
	    var result = null;
	    
	    ajaxAction = new AjaxAction( 'JsMarkup.' + this.wid + '.action', '', this.uri, 'GET', async, this.loadCompleted, this.handleLoadError );
		if ( async )
		{
			ajaxAction.jsWidget = this;
		}
		
		ajaxAction.execute();
		
		if ( !async && ajaxAction.result )
		{
			result = ajaxAction.result;
		}
		
		return result;
	};
	
JsMarkup.prototype.loadCompleted =
	function ( req, action )
	{
		if ( req.readyState == 4 )
	    {	
	    	if ( action.jsWidget )
	    	{
	    		action.jsWidget.peer.innerHTML = req.responseText;
	    	}
	    	else
	    	{
	    		action.result = req.responseText;
	    	}
	    }
	};
	
JsMarkup.prototype.handleLoadError =
	function ( req, action )
	{
		
	};
	
JsMarkup.prototype.setContent =
	function ( content )
	{
		this.content = content;
		if ( this.peer )
		{
			this.peer.innerHTML = this.content;
		}	
	};
	
JsMarkup.prototype.setUri =
	function ( uri )
	{
		this.uri = uri;
		if ( this.peer )
		{
			this.peer.innerHTML = this.loadUri( true );
		}
	};

/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */
	
JsMarkup.prototype.renderTo =	
	function ( out )
	{
		var html;
		
		html = '<div ';
		html += 'id="' + this.wid + '" ';
		html += 'name="' + this.wid + '" ';
		
		if ( this.styleClass ) { html += 'class="' + this.styleClass + '" '; }
		html += 'style="';
		if ( !this.isVisible ) { html += 'display: none;' }
		html += '" ';
		
		html += '>';
		
		if ( this.uri )
		{
			html += this.loadUri( false );
		}
		else
		{
			out.write( this.content );
		}
		
		html += '</div>';

		out.write(html);			
	};
	
/**
 * Constructor for JSWL image widgets.  The image widget is rendered
 * by an HTML img element.
 * 
 * <idl>
 * interface JsImage:JsWidget
 * {
 * }
 * </idl>
 *
 * @param wid      a unique widget id.
 * @param uri      the image URI.
 * @param width    the image width.
 * @param height   the image height.
 */
 
function JsImage( wid, uri, width, height )
{
	this.base = JsWidget;
	this.base(wid);
	this.styleClass = 'jsImage';
	this.layoutType = INLINE_BOX_TYPE;
	this.image = ((width && height) ? new Image( width, height ) : new Image());
	this.image.sizeKnown = ( width && height );
	this.image.src = uri;
	this.image.align = 'absmiddle';
	this.image.border = 0;
}

JsImage.prototype = new JsWidget;

JsImage.prototype.setImageAlignment = 
	function ( alignment )
	{
		this.image.align = alignment;
		if ( this.peer != null )
		{
			this.peer.align = alignment;	
		}
	};
	
JsImage.prototype.setBorder = 
	function ( border )
	{
		this.image.border = border;
		if ( this.peer != null )
		{
			this.peer.border = border;	
		}
	};

JsImage.prototype.setUri =
	function ( uri )
	{
		this.image.src = uri;
		if ( this.peer != null )
		{
			this.peer.src = uri;
		}
	};

/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */

JsImage.prototype.renderTo =
	function ( out )
	{
		var html;
		
		html = '<img ';
		html += 'id="' + this.wid + '" ';
		
		if ( this.styleClass ) { html += 'class="' + this.styleClass + '" '; }
		html += 'style="';
		if ( !this.isVisible ) { html += 'display: none;' }
		html += '" ';
		
		if ( this.image.sizeKnown )	
		{	
			html += 'width="' + this.image.width + '" ';
			html += 'height="' + this.image.height + '" ';
		}
		
		if ( this.image.border ) { html += 'border="' + this.image.border + '" '; }
		if ( this.image.align ) { html += 'align="' + this.image.align + '" '; }
		if ( this.image.src ) { html += 'src="' + this.image.src + '" '; }
		
		html += ' />';
		
		out.write(html);
	};

/**
 * Constructor for JSWL widgets which correspond to HTML 
 * <CODE>LABEL</CODE> elements.
 *
 * @param wid            a unique widget id.
 * @param name           the name for the INPUT element.
 * @param initialValue   the initial value for the INPUT element.
 * @jswl.public buttonType  the type of this button widget.
 * @jswl.public styleClass  the CSS classname for the HTML element.
 * @jswl-public render      render the markup for this widget. 
 */
 
function JsLabel( wid, text, refWid )
{
	this.base = JsWidget;
	this.base( wid );	
	this.layoutType = INLINE_BOX_TYPE;
	this.text = text;
	this.refWid = refWid;
	this.styleClass = 'jsLabel';	
}

JsLabel.prototype = new JsWidget;

/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */

JsLabel.prototype.renderTo =
	function ( out )
	{
		var html;
		
		html = '<label ';
		
		if ( this.refWid ) { html += 'for="' + this.refWid + '" '; }
		
		html += 'id="' + this.wid + '" ';
		
		if ( this.styleClass ) { html += 'class="' + this.styleClass + '" '; }
		
		html += 'style="';
		if ( !this.isVisible ) { html += 'display: none;' }
		html += '" ';
				
		html += '>';
		html += this.text;
		html += '</label>';
		
		out.write(html);
	};

/**
 * Constructor for JSWL link widgets.  The link markup widget is rendered
 * by an HTML A element.
 * 
 * <idl>
 * interface JsText:JsWidget
 * {
 *   attribute		string	text;
 *   void					setText( in string text );
 * }
 * </idl>
 *
 * @param wid    a unique widget id.
 * @param text   the text.
 */
 	
function JsLink( wid, name, href, text, target )
{
	this.base = JsWidget;
	this.base(wid);
	this.name = name;
	this.href = href;
	this.text = text;
	this.target = target;
	this.styleClass = 'jsLink';
	this.actionListeners = new Array();	
	this.addEventListener( 'onclick', this );
}

JsLink.prototype = new JsWidget;

JsLink.prototype.setName = 
	function ( name )
	{
		this.name = name;
		if ( this.peer != null )
		{
			this.peer.name = name;	
		}
	};
	
JsLink.prototype.setHref = 
	function ( href )
	{
		this.href = href;
		if ( this.peer != null )
		{
			this.peer.href = href;	
		}
	};
	
JsLink.prototype.setText =
	function ( text )
	{
		this.text = text;
		if ( this.peer )
		{
			this.peer.innerHTML = this.text;
		}	
	};
		
JsLink.prototype.setTarget = 
	function ( target )
	{
		this.target = target;
		if ( this.peer )
		{
			this.peer.target = target;	
		}
	};
	
/* **********************************************************************
 * Implement EventListener
 * ********************************************************************** */
 
JsLink.prototype.handleEvent =
	function ( event, type )
	{
		var rc = DO_DEFAULT_ACTION;
		
		if ( type == 'onclick' )
		{
			rc = this.fireAction( event );
		}
		
		return rc;
	};
	
/* **********************************************************************
 * Implement Renderable
 * ********************************************************************** */

JsLink.prototype.renderTo =
	function ( out )
	{	
		var html;
		
		html = '<a ';
		
		html += 'id="' + this.wid + '" ';
		
		if ( this.styleClass ) { html += 'class="' + this.styleClass + '" '; }
		if ( this.name ) { html += 'name="' + this.name + '" '; }
		if ( this.href ) { html += 'href="' + this.href + '" '; }
		if ( this.target ) { html += 'target="' + this.target + '" '; }
		
		html += 'style="';
		if ( !this.isVisible ) { html += 'display: none;' }
		html += '" ';
		
		html += '>';
		html += this.text;
		html += '</a>';
		
		out.write(html);
	};
	
/* **********************************************************************
 * Implement ActionProducer
 * ********************************************************************** */
	
JsLink.prototype.addActionListener =
	function ( listener )
	{
		var count = this.actionListeners.length;
		this.actionListeners[count] = listener;
	};

JsLink.prototype.getActionListeners =
	function ()
	{
		return this.actionListeners;
	};
	
JsLink.prototype.removeActionListener =
	function ( listener )
	{
		var index = this.actionListeners.indexOf( listener );
		if ( index != -1 )
		{
			this.actionListeners.splice( index, 1 );
		} 
	};
	
JsLink.prototype.fireAction =	
	function ( event )
	{
		var listeners = this.getActionListeners();
		var	rc = DO_DEFAULT_ACTION;
		
		if ( listeners && listeners.length > 0 )
		{
			for ( var i = 0; i < listeners.length; i++ )
			{
				var listener = listeners[i];
				if ( listener.handleAction )
				{
					rc = listener.handleAction( event );
				}
				else if ( listener )
				{
					rc = listener( event, this );
				}
			}
		}	

		return rc;
	};
	
	
/**
 * Constructor for a JSWL action.
 */	
 
function JsAction( id, title, icon )
{
	this.id = id;
	this.title = ( title || '' ); 
	this.icon = ( icon || '' );
	this.tooltip = '';
}

JsAction.prototype.execute =
	function()
	{
	};

/* **********************************************************************
 * AJAX Support
 * ********************************************************************** */
							   
function AjaxAction( title, icon, url, method, async, successFunction, errorFunction )
{
	this.base = JsAction;
	this.base( title, icon );
	this.url = url;
	this.method = method;
	this.async = async;	
	this.successFunction = successFunction;
	this.errorFunction = errorFunction;
}

AjaxAction.prototype = new JsAction;

AjaxAction.prototype.handleAjaxResponse =
	function( req )
	{
		if ( req.readyState == 4 ) 
	    {
	        if ( req.status == 200 ) 
	        {
	        	this.successFunction( req, this );
	        }
	        else
	        {
	        	this.errorFunction( req, this );
	        }
		}
	};

AjaxAction.prototype.execute =
	function()
	{
		sendAjaxRequest( this.url, this.method, this, this.async );
	};

/**
 * The EventProxy object is used to add support for multiple event listeners
 * to any intrinsic event handler.
 */
 
function EventProxy()
{
	this.eventListeners = new Array();
}

/* **********************************************************************
 * Implement EventProducer
 * ********************************************************************** */

EventProxy.prototype.addEventListener =
	function ( listener )
	{
		var count = this.eventListeners.length;
		this.eventListeners[count] = listener;
	};
	
EventProxy.prototype.getEventListeners =
	function ()
	{
		return this.eventListeners;
	};
	
EventProxy.prototype.removeEventListener =
	function ( listener )
	{
		var index = this.eventListeners.indexOf( listener );
		if ( index != -1 )
		{
			this.eventListeners.splice( index, 1 );
		} 
	};
	
EventProxy.prototype.fireEvent =
	function ( event )
	{
		var type = getEventType( event );
		var listeners = this.getEventListeners();
		var rc = true;
			
		if ( listeners && listeners.length > 0 )
		{
			for ( var i = 0; i < listeners.length; i++ )
			{
				var listener = listeners[i];
				
				if ( listener.handleEvent )
				{
					rc = listener.handleEvent( event, type );
				}
				else if ( listener )
				{ 
					rc = listener( event, type, null );
				}
			}
		}	
		
		return rc;		
	};
	
/* ******************************************************************************************
 * Core global functions.
 * ****************************************************************************************** */
 	
function jsBindWidget( widget, id )
{
    var	element = getElement( id );
    
    if ( element )
    {
        var	sw = new StringWriter();
        widget.renderTo(sw);
        element.innerHTML = sw.output;
        widget.hookPeer();
    }
}

function jsInitWidgets( binder )
{
	jsAddEventListener( window, 'onload', function ( event ) { binder(); } );
}