diff options
54 files changed, 1514 insertions, 147 deletions
| @@ -1,4 +1,5 @@  /.project +/assets  buildscripts/jsbuilder/JSDoc  demos/quickstart/assets/274d44dc  demos/quickstart/assets/56624407 diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index 257a4914..5fa94202 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -307,6 +307,8 @@ callback_not_support_no_priority_state_update	= Callback request does not suppor  callback_invalid_callback_options		= '{1}' is not a valid TCallbackOptions control for Callback control '{0}'.  callback_invalid_clientside_options		= Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.=======  callback_not_support_no_priority_state_update	= Callback request does not support unprioritized pagestate update. +callback_invalid_handler				= Invalid callback handler, control {0} must implement ICallbackEventHandler. +callback_invalid_target					= Invalid callback target, no such control with ID {0}.  xmltransform_xslextension_required = TXmlTransform require the PHP's XSL extension  xmltransform_transformpath_invalid = TransformPath '{0}' is invalid. diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/ajax.js index 6e6d0afb..bfc080e9 100644 --- a/framework/Web/Javascripts/js/ajax.js +++ b/framework/Web/Javascripts/js/ajax.js @@ -33,20 +33,16 @@ this.transport.onreadystatechange=Prototype.emptyFunction;},getHeaderData:functi  {var json=this.header(name);return eval('('+json+')');}  catch(e)  {if(typeof(json)=="string") -{Logger.info("using json") -return Prado.CallbackRequest.decode(json);}}}});Prado.CallbackRequest=Class.create();Object.extend(Prado.CallbackRequest,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',FIELD_CALLBACK_PAGESTATE:'PRADO_PAGESTATE',FIELD_POSTBACK_TARGET:'PRADO_POSTBACK_TARGET',FIELD_POSTBACK_PARAMETER:'PRADO_POSTBACK_PARAMETER',PostDataLoaders:[],DATA_HEADER:'X-PRADO-DATA',ACTION_HEADER:'X-PRADO-ACTIONS',ERROR_HEADER:'X-PRADO-ERROR',PAGESTATE_HEADER:'X-PRADO-PAGESTATE',requestInProgress:null,dispatchActions:function(transport,actions) +return Prado.CallbackRequest.decode(json);}}});Prado.CallbackRequest=Class.create();Object.extend(Prado.CallbackRequest,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',FIELD_CALLBACK_PAGESTATE:'PRADO_PAGESTATE',FIELD_POSTBACK_TARGET:'PRADO_POSTBACK_TARGET',FIELD_POSTBACK_PARAMETER:'PRADO_POSTBACK_PARAMETER',PostDataLoaders:[],DATA_HEADER:'X-PRADO-DATA',ACTION_HEADER:'X-PRADO-ACTIONS',ERROR_HEADER:'X-PRADO-ERROR',PAGESTATE_HEADER:'X-PRADO-PAGESTATE',requestInProgress:null,addPostLoaders:function(ids) +{this.PostDataLoaders=this.PostDataLoaders.concat(ids);},dispatchActions:function(transport,actions)  {if(actions&&actions.length>0)  actions.each(this.__run.bind(this,transport));},__run:function(transport,command)  {for(var method in command) -{if(command[method][0]) -{var id=command[method][0];if($(id)||id.indexOf("[]")>-1)  {try  {method.toFunction().apply(this,command[method].concat(transport));}  catch(e)  {if(typeof(Logger)!="undefined") -Prado.CallbackRequest.Exception.onException(null,e);}} -else if(typeof(Logger)!="undefined") -{Logger.error("Error in executing callback response:","Unable to find HTML element with ID '"+id+"' before executing "+method+"().");}}}},Exception:{"on500":function(request,transport,data) +Prado.CallbackRequest.Exception.onException(null,e);}}},Exception:{"on500":function(request,transport,data)  {var e=request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER);Logger.error("Callback Server Error "+e.code,this.formatException(e));},'on200':function(request,transport,data)  {if(transport.status<500)  {var msg='HTTP '+transport.status+" with response : \n";msg+=transport.responseText+"\n";msg+="Data : \n"+inspect(data)+"\n";msg+="Actions : \n";data=request.getHeaderData(Prado.CallbackRequest.ACTION_HEADER);if(data&&data.length>0) @@ -63,19 +59,17 @@ msg+=e.version+" "+e.time+"\n";return msg;}},encode:function(data)  {if(typeof(data)=="string"&&data.trim().length>0)  return Prado.JSON.parse(data);else  return null;},dispatchPriorityRequest:function(callback) -{Logger.info("priority request "+callback.id) -this.abortRequestInProgress();callback.request=new Ajax.Request(callback.url,callback.options);callback.timeout=setTimeout(function() -{Logger.warn("priority timeout");Prado.CallbackRequest.abortRequestInProgress();},callback.options.RequestTimeOut);this.requestInProgress=callback;Logger.info("dispatched "+this.requestInProgress)},dispatchNormalRequest:function(callback) -{Logger.info("dispatching normal request");new Ajax.Request(callback.url,callback.options);},abortRequestInProgress:function() -{inProgress=Prado.CallbackRequest.requestInProgress;Logger.info("aborting ... "+inProgress);if(inProgress) -{Logger.warn("aborted "+inProgress.id) -inProgress.request.transport.abort();clearTimeout(inProgress.timeout);Prado.CallbackRequest.requestInProgress=null;return true;} +{this.abortRequestInProgress();callback.request=new Ajax.Request(callback.url,callback.options);callback.timeout=setTimeout(function() +{Prado.CallbackRequest.abortRequestInProgress();},callback.options.RequestTimeOut);this.requestInProgress=callback;},dispatchNormalRequest:function(callback) +{new Ajax.Request(callback.url,callback.options);},abortRequestInProgress:function() +{inProgress=Prado.CallbackRequest.requestInProgress;if(inProgress) +{inProgress.request.transport.abort();clearTimeout(inProgress.timeout);Prado.CallbackRequest.requestInProgress=null;return true;}  return false;},updatePageState:function(request,transport)  {pagestate=$(this.FIELD_CALLBACK_PAGESTATE);if(request.options.EnablePageStateUpdate&&request.options.HasPriority&&pagestate)  {data=request.header(this.PAGESTATE_HEADER);if(typeof(data)=="string"&&data.length>0) -{Logger.warn("updating page state");pagestate.value=data;} -else -{Logger.debug("Bad page state:"+data);}}}}) +pagestate.value=data;else +{if(typeof(Logger)!="undefined") +Logger.debug("Bad page state:"+data);}}}})  Ajax.Responders.register({onComplete:function(request)  {if(request.options.HasPriority)  Prado.CallbackRequest.abortRequestInProgress();}});Event.OnLoad(function() @@ -199,4 +193,7 @@ this.editField=this.cached_selectTag;if(this.options.loadTextURL)this.loadExtern  {this.options=options;this.baseInitialize(options.ID,options.ResultPanel,options);Object.extend(this.options,{onSuccess:this.onComplete.bind(this)});},getUpdatedChoices:function()  {Prado.Callback(this.options.EventTarget,this.getToken(),null,this.options);},onComplete:function(request,boundary)  {result=Prado.Element.extractContent(request.responseText,boundary);if(typeof(result)=="string"&&result.length>0) -this.updateChoices(result);}});
\ No newline at end of file +this.updateChoices(result);}});Prado.WebUI.TActiveTextBox=Class.extend(Prado.WebUI.TTextBox,{onInit:function(options) +{if(options['TextMode']!='MultiLine') +Event.observe(this.element,"keydown",this.handleReturnKey.bind(this));Event.observe(this.element,"change",this.doCallback.bindEvent(this,options));},doCallback:function(event,options) +{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}});
\ No newline at end of file diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index 65ffd1b8..d0744649 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -267,10 +267,15 @@ setTimeout(function(){obj.focus();},100);return false;},replace:function(element  {if(boundary)  {result=Prado.Element.extractContent(transport.responseText,boundary);if(result!=null)  content=result;} -method.toFunction().apply(this,[element,content]);},extractContent:function(text,boundary) +if(typeof(element)=="string") +{if($(element)) +method.toFunction().apply(this,[element,content]);} +else +{method.toFunction().apply(this,[content]);}},extractContent:function(text,boundary)  {f=RegExp('(<!--'+boundary+'-->)([\\s\\S\\w\\W]*)(<!--//'+boundary+'-->)',"m");result=text.match(f);if(result&&result.length>=2)  return result[2];else -return null;}} +return null;},evaluateScript:function(content) +{content.evalScripts();}}  Prado.Element.Selection={inputValue:function(el,value)  {switch(el.type.toLowerCase())  {case'checkbox':case'radio':return el.checked=value;}},selectValue:function(el,value) diff --git a/framework/Web/Javascripts/prado/activecontrols3.js b/framework/Web/Javascripts/prado/activecontrols3.js index 4be2779a..6fbde405 100644 --- a/framework/Web/Javascripts/prado/activecontrols3.js +++ b/framework/Web/Javascripts/prado/activecontrols3.js @@ -42,3 +42,19 @@ Prado.WebUI.TAutoComplete = Class.extend(Autocompleter.Base,  			this.updateChoices(result);
  	}	
  });
 +
 +Prado.WebUI.TActiveTextBox = Class.extend(Prado.WebUI.TTextBox,
 +{
 +	onInit : function(options)
 +	{
 +		if(options['TextMode'] != 'MultiLine')
 +			Event.observe(this.element, "keydown", this.handleReturnKey.bind(this));
 +		Event.observe(this.element, "change", this.doCallback.bindEvent(this,options));
 +	},
 +	
 +	doCallback : function(event, options)
 +	{
 +		new Prado.CallbackRequest(options.EventTarget, options);
 +		Event.stop(event);
 +	}
 +});
 diff --git a/framework/Web/Javascripts/prado/ajax3.js b/framework/Web/Javascripts/prado/ajax3.js index 1e832889..adf8534a 100644 --- a/framework/Web/Javascripts/prado/ajax3.js +++ b/framework/Web/Javascripts/prado/ajax3.js @@ -19,7 +19,7 @@ Object.extend(Ajax.Request.prototype,  	      	Prado.CallbackRequest.updatePageState(this,transport);
  			Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
  			Prado.CallbackRequest.dispatchActions(transport,this.getHeaderData(Prado.CallbackRequest.ACTION_HEADER));
 -	      
 +
  	        (this.options['on' + this.transport.status]
  	         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
  	         || Prototype.emptyFunction)(transport, json);
 @@ -57,10 +57,7 @@ Object.extend(Ajax.Request.prototype,  		catch (e) 
  		{
  			if(typeof(json) == "string")
 -			{
 -				Logger.info("using json")
  				return Prado.CallbackRequest.decode(json);
 -			}
  		}
  	}
  });
 @@ -118,6 +115,14 @@ Object.extend(Prado.CallbackRequest,  	requestInProgress : null,
  	/**
 +	 * Add ids of inputs element to post in the request.
 +	 */
 +	addPostLoaders : function(ids)
 +	{
 +		this.PostDataLoaders = this.PostDataLoaders.concat(ids);
 +	},
 +	
 +	/**
  	 * Dispatch callback response actions.
  	 */
  	dispatchActions : function(transport,actions)
 @@ -133,26 +138,14 @@ Object.extend(Prado.CallbackRequest,  	{
  		for(var method in command)
  		{
 -			if(command[method][0])
 +			try
  			{
 -				var id = command[method][0];
 -				if($(id) || id.indexOf("[]") > -1)
 -				{
 -					try
 -					{
 -						method.toFunction().apply(this,command[method].concat(transport));
 -					}
 -					catch(e)
 -					{	
 -						if(typeof(Logger) != "undefined")
 -							Prado.CallbackRequest.Exception.onException(null,e);
 -					}
 -				}
 -				else if(typeof(Logger) != "undefined")
 -				{
 -					Logger.error("Error in executing callback response:", 
 -					"Unable to find HTML element with ID '"+id+"' before executing "+method+"().");		
 -				}
 +				method.toFunction().apply(this,command[method].concat(transport));
 +			}
 +			catch(e)
 +			{	
 +				if(typeof(Logger) != "undefined")
 +					Prado.CallbackRequest.Exception.onException(null,e);
  			}
  		}
  	},
 @@ -251,18 +244,18 @@ Object.extend(Prado.CallbackRequest,  	 */
  	dispatchPriorityRequest : function(callback)
  	{
 -		Logger.info("priority request "+callback.id)
 +		//Logger.info("priority request "+callback.id)
  		this.abortRequestInProgress();
  		callback.request = new Ajax.Request(callback.url, callback.options);
  		callback.timeout = setTimeout(function()
  		{
 -			Logger.warn("priority timeout");
 +		//	Logger.warn("priority timeout");
  			Prado.CallbackRequest.abortRequestInProgress();
  		},callback.options.RequestTimeOut);
  		this.requestInProgress = callback;
 -		Logger.info("dispatched "+this.requestInProgress)
 +		//Logger.info("dispatched "+this.requestInProgress)
  	},
  	/**
 @@ -270,7 +263,7 @@ Object.extend(Prado.CallbackRequest,  	 */
  	dispatchNormalRequest : function(callback)
  	{
 -		Logger.info("dispatching normal request");
 +	//	Logger.info("dispatching normal request");
  		new Ajax.Request(callback.url, callback.options);
  	},
 @@ -280,10 +273,10 @@ Object.extend(Prado.CallbackRequest,  	abortRequestInProgress : function()
  	{
  		inProgress = Prado.CallbackRequest.requestInProgress;
 -		Logger.info("aborting ... "+inProgress);
 +		//Logger.info("aborting ... "+inProgress);
  		if(inProgress)
  		{
 -			Logger.warn("aborted "+inProgress.id)
 +		//	Logger.warn("aborted "+inProgress.id)
  			inProgress.request.transport.abort();
  			clearTimeout(inProgress.timeout);
  			Prado.CallbackRequest.requestInProgress = null;
 @@ -303,13 +296,11 @@ Object.extend(Prado.CallbackRequest,  		{
  			data = request.header(this.PAGESTATE_HEADER);
  			if(typeof(data) == "string" && data.length > 0)
 -			{
 -				Logger.warn("updating page state");
  				pagestate.value = data;
 -			}
  			else
  			{
 -				Logger.debug("Bad page state:"+data);
 +				if(typeof(Logger) != "undefined")
 +					Logger.debug("Bad page state:"+data);
  			}
  		}
  	}
 diff --git a/framework/Web/Javascripts/prado/element.js b/framework/Web/Javascripts/prado/element.js index 0ff0e8fa..5c76d4e6 100644 --- a/framework/Web/Javascripts/prado/element.js +++ b/framework/Web/Javascripts/prado/element.js @@ -71,7 +71,15 @@ Prado.Element =  			if(result != null)
  				content = result;
  		}
 -		method.toFunction().apply(this,[element,content]);
 +		if(typeof(element) == "string")
 +		{
 +			if($(element))
 +				method.toFunction().apply(this,[element,content]);
 +		}
 +		else
 +		{
 +			method.toFunction().apply(this,[content]);
 +		}
  	},
  	extractContent : function(text, boundary)
 @@ -82,6 +90,11 @@ Prado.Element =  			return result[2];
  		else
  			return null;
 +	},
 +	
 +	evaluateScript : function(content)
 +	{
 +		content.evalScripts();
  	}
  }
 diff --git a/framework/Web/UI/ActiveControls/TActiveButton.php b/framework/Web/UI/ActiveControls/TActiveButton.php index f4bfc678..81744057 100644 --- a/framework/Web/UI/ActiveControls/TActiveButton.php +++ b/framework/Web/UI/ActiveControls/TActiveButton.php @@ -7,9 +7,11 @@   * @copyright Copyright © 2006 PradoSoft
   * @license http://www.pradosoft.com/license/
   * @version $Revision: $  $Date: $
 - * @package System.Web.UI.WebControls
 + * @package System.Web.UI.ActiveControls
   */
 +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter');
 +
  /** 
   * TActiveButton is the active control counter part to TButton.
   * 
 @@ -17,7 +19,7 @@   * callback request is initiated. 
   * 
   * The {@link onCallback OnCallback} event is raised during a callback request 
 - * and it is raise before the {@link onClick OnClick} event.
 + * and it is raise <b>after</b> the {@link onClick OnClick} event.
   * 
   * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate}
   * property is true, changing the {@link setText Text} property during callback request
 @@ -28,7 +30,7 @@   * @package System.Web.UI.ActiveControls
   * @since 3.0
   */
 -class TActiveButton extends TButton implements ICallbackEventHandler
 +class TActiveButton extends TButton implements ICallbackEventHandler, IActiveControl
  {
  	/**
  	 * Creates a new callback control, sets the adapter to
 @@ -53,15 +55,15 @@ class TActiveButton extends TButton implements ICallbackEventHandler  	 * Raises the callback event. This method is required by {@link
  	 * ICallbackEventHandler} interface. If {@link getCausesValidation
  	 * CausesValidation} is true, it will invoke the page's {@link TPage::
 -	 * validate validate} method first. It will raise {@link onCallback
 -	 * OnCallback} event first and then the {@link onClick OnClick} event. 
 +	 * validate validate} method first. It will raise {@link onClick
 +	 * OnClick} event first and then the {@link onCallback OnCallback} event. 
  	 * This method is mainly used by framework and control developers.
  	 * @param TCallbackEventParameter the event parameter
  	 */
   	public function raiseCallbackEvent($param)
  	{
 -		$this->onCallback($param);
  		$this->raisePostBackEvent($param);
 +		$this->onCallback($param);
  	}
  	/**
 @@ -115,16 +117,6 @@ class TActiveButton extends TButton implements ICallbackEventHandler  	{
  		return 'Prado.WebUI.TActiveButton';
  	}
 -
 -	/**
 -	 * Gets the name of the javascript class responsible for performing postback for this control.
 -	 * This method overrides the parent implementation.
 -	 * @return string the javascript class name
 -	 */
 -	protected function getClientClassName()
 -	{
 -		return 'Prado.WebUI.TActiveButton';
 -	}
  }
  ?>
\ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index ab842976..de6310a6 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -1,26 +1,68 @@  <?php
 +/**
 + * TActiveControlAdapter and TCallbackPageStateTracker class file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2006 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Revision: $  : $
 + * @package System.Web.UI.ActiveControls
 + */
 +
  /*
 - * Created on 29/04/2006
 + * Load common active control options.
   */
  Prado::using('System.Web.UI.ActiveControls.TBaseActiveControl');
 +/**
 + * TActiveControlAdapter class.
 + * 
 + * Customize the parent TControl class for active control classes. 
 + * TActiveControlAdapter instantiates a common base active control class
 + * throught the {@link getBaseActiveControl BaseActiveControl} property.
 + * The type of BaseActiveControl can be provided in the second parameter in the
 + * constructor. Default is TBaseActiveControl or TBaseActiveCallbackControl if
 + * the control adapted implements ICallbackEventHandler.
 + * 
 + * TActiveControlAdapter will tracking viewstate changes to update the 
 + * corresponding client-side properties.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 20:35:34 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
  class TActiveControlAdapter extends TControlAdapter
  {
 -	private static $_renderedPosts = false;
 -	
 +	/**
 +	 * @var string base active control class name.
 +	 */
  	private $_activeControlType;
 -	
 +	/**
 +	 * @var TBaseActiveControl base active control instance.
 +	 */
  	private $_baseActiveControl;
 -	
 +	/**
 +	 * @var TCallbackPageStateTracker view state tracker.
 +	 */
  	private $_stateTracker;
 -	public function __construct($control, $baseCallbackClass=null)
 +	/**
 +	 * Constructor.
 +	 * @param IActiveControl active control to adapt.
 +	 * @param string Base active control class name.
 +	 */
 +	public function __construct(IActiveControl $control, $baseCallbackClass=null)
  	{
  		parent::__construct($control);
 -		$this->setBaseControlType($baseCallbackClass);
 +		$this->setBaseControlClass($baseCallbackClass);
  	}
 -	private function setBaseControlType($type)
 +	/**
 +	 * @param string base active control instance
 +	 */
 +	protected function setBaseControlClass($type)
  	{
  		if(is_null($type))
  		{
 @@ -30,22 +72,21 @@ class TActiveControlAdapter extends TControlAdapter  				$this->_activeControlType = 'TBaseActiveControl';
  		}
  		else
 -		{
  			$this->_activeControlType = $type;
 -		}
  	}
  	/**
 -	 * Render the callback request post data loaders once only.
 +	 * Renders the callback client scripts.
  	 */
  	public function render($writer)
  	{
  		$this->renderCallbackClientScripts();
  		parent::render($writer);
 -		if($this->getPage()->getIsCallback())
 -			$this->getPage()->getCallbackClient()->replace($this->getControl(), $writer);
  	}
 +	/**
 +	 * Register the callback clientscripts and sets the post loader IDs. 
 +	 */
  	protected function renderCallbackClientScripts()
  	{
  		$cs = $this->getPage()->getClientScript();
 @@ -54,11 +95,14 @@ class TActiveControlAdapter extends TControlAdapter  		{
  			$cs->registerPradoScript('ajax');
  			$options = TJavascript::encode($this->getPage()->getPostDataLoaders(),false);
 -			$script = "Prado.CallbackRequest.PostDataLoaders = {$options};";
 +			$script = "Prado.CallbackRequest.addPostLoaders({$options});";
  			$cs->registerEndScript($key, $script);
  		}
  	}
 +	/**
 +	 * @return TBaseActiveControl Common active control options.
 +	 */
  	public function getBaseActiveControl()
  	{
  		if(is_null($this->_baseActiveControl))
 @@ -69,6 +113,9 @@ class TActiveControlAdapter extends TControlAdapter  		return $this->_baseActiveControl;
  	}
 +	/**
 +	 * @return boolean true if the viewstate needs to be tracked.
 +	 */
  	protected function getIsTrackingPageState()
  	{
  		if($this->getPage()->getIsCallback())
 @@ -83,6 +130,10 @@ class TActiveControlAdapter extends TControlAdapter  		return false;
  	}
 +	/**
 +	 * Loads additional persistent control state. Starts viewstate tracking
 +	 * if necessary.
 +	 */
  	public function loadState()
  	{
  		if($this->getIsTrackingPageState())
 @@ -93,6 +144,10 @@ class TActiveControlAdapter extends TControlAdapter  		parent::loadState();
  	}
 +	/**
 +	 * Saves additional persistent control state. Respond to viewstate changes
 +	 * if necessary.
 +	 */
  	public function saveState()
  	{
  		if(!is_null($this->_stateTracker) 
 @@ -104,6 +159,18 @@ class TActiveControlAdapter extends TControlAdapter  	}
  } 
 +/**
 + * TCallbackPageStateTracker class.
 + * 
 + * Tracking changes to the page state during callback.
 + * 
 + * @todo Complete this class! (Wei)
 + * 
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 20:51:25 EST 2006 $
 + * @package System
 + * @since 3.0
 + */
  class TCallbackPageStateTracker
  {
  	private $_states = array('Visible', 'Enabled', 'Attributes', 'Style', 'TabIndex', 'ToolTip', 'AccessKey');
 diff --git a/framework/Web/UI/ActiveControls/TActiveLabel.php b/framework/Web/UI/ActiveControls/TActiveLabel.php index c1cb1fba..f54e0b98 100644 --- a/framework/Web/UI/ActiveControls/TActiveLabel.php +++ b/framework/Web/UI/ActiveControls/TActiveLabel.php @@ -25,7 +25,7 @@   * @package System.Web.UI.ActiveControls
   * @since 3.0
   */
 -class TActiveLabel extends TLabel
 +class TActiveLabel extends TLabel implements IActiveControl
  {
  	/**
  	 * Creates a new callback control, sets the adapter to
 diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index 77d8a7fe..90c64820 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -1,6 +1,6 @@  <?php
  /**
 - * TActivePageAdapter class file.
 + * TActivePageAdapter, TCallbackEventParameter, TCallbackErrorHandler and TInvalidCallbackException class file.
   *
   * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
   * @link http://www.pradosoft.com/
 @@ -9,11 +9,16 @@   * @version $Revision: $  $Date: $
   * @package System.Web.UI.ActiveControls
   */
 - 
 +
 +/**
 + * Load callback response adapter class.
 + */
 +Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter');
 +
  /**
   * TActivePageAdapter class.
   * 
 - * Callback request page handler.
 + * Callback request handler.
   * 
   * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
   * @version $Revision: $  $Date: $
 @@ -22,9 +27,21 @@   */
  class TActivePageAdapter extends TControlAdapter
  {	
 +	/**
 +	 * Callback response data header name.
 +	 */
  	const CALLBACK_DATA_HEADER = 'X-PRADO-DATA';
 +	/**
 +	 * Callback response client-side action header name.
 +	 */
  	const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS';
 +	/**
 +	 * Callback error header name.
 +	 */
  	const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR';
 +	/**
 +	 * Callback page state header name.
 +	 */
  	const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE';
  	/**
 @@ -57,6 +74,7 @@ class TActivePageAdapter extends TControlAdapter  	/**
  	 * Process the callback request.
 +	 * @param THtmlWriter html content writer.
  	 */
  	public function processCallbackEvent($writer)
  	{
 @@ -64,6 +82,9 @@ class TActivePageAdapter extends TControlAdapter  		$this->raiseCallbackEvent();
  	}
 +	/**
 +	 * Trap errors and exceptions to be handled by TCallbackErrorHandler.
 +	 */
  	protected function trapCallbackErrorsExceptions()
  	{
  		$this->getApplication()->setErrorHandler(new TCallbackErrorHandler);
 @@ -71,6 +92,7 @@ class TActivePageAdapter extends TControlAdapter  	/**
  	 * Render the callback response.
 +	 * @param THtmlWriter html content writer.
  	 */
  	public function renderCallbackResponse($writer)
  	{
 @@ -81,14 +103,12 @@ class TActivePageAdapter extends TControlAdapter  	/**
  	 * Renders the callback response by adding additional callback data and
  	 * javascript actions in the header and page state if required.
 +	 * @param THtmlWriter html content writer.
  	 */
  	protected function renderResponse($writer)
  	{
  		$response = $this->getResponse();
 -		$executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute()->toArray();
 -		$actions = TJavascript::jsonEncode($executeJavascript);
 -		$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions);
 -		
 +
  		//send response data in header
  		if($response->getHasAdapter())
  		{
 @@ -109,12 +129,29 @@ class TActivePageAdapter extends TControlAdapter  				$response->appendHeader(self::CALLBACK_PAGESTATE_HEADER.': '.$pagestate);
  			}
  		}
 +		
 +		//safari must receive at least 1 byte of data.
 +		$writer->write(" ");
 +
 +		//output the end javascript
 +		if($this->getPage()->getClientScript()->hasEndScripts())
 +		{
 +			$writer = $response->createHtmlWriter();
 +			$this->getPage()->getClientScript()->renderEndScripts($writer);
 +			$this->getPage()->getCallbackClient()->evaluateScript($writer);
 +		}
 +		
 +		//output the actions
 +		$executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute()->toArray();
 +		$actions = TJavascript::jsonEncode($executeJavascript);
 +		$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions);
 +
  	}
  	/**
  	 * Trys to find the callback event handler and raise its callback event.
 -	 * @throws TInvalidCallbackRequestException if call back target is not found.
 -	 * @throws TInvalidCallbackHandlerException if the requested target does not
 +	 * @throws TInvalidCallbackException if call back target is not found.
 +	 * @throws TInvalidCallbackException if the requested target does not
  	 * implement ICallbackEventHandler.
  	 */
  	private function raiseCallbackEvent()
 @@ -129,13 +166,14 @@ class TActivePageAdapter extends TControlAdapter  			}
  			else
  			{
 -				throw new TInvalidCallbackHandlerException($callbackHandler->getUniqueID());
 +				throw new TInvalidCallbackException(
 +					'callback_invalid_handler', $callbackHandler->getUniqueID());
  			}
  		 }
  		 else
  		 {
  		 	$target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
 -		 	throw new TInvalidCallbackRequestException($target);
 +		 	throw new TInvalidCallbackException('callback_invalid_target', $target);
  		 }
  	}
 @@ -164,7 +202,7 @@ class TActivePageAdapter extends TControlAdapter  	}
  	/**
 -	 * Callback parameter is decoded assuming JSON encoding. 
 +	 * Gets callback parameter. JSON encoding is assumed.
  	 * @return string postback event parameter
  	 */
  	public function getCallbackEventParameter()
 @@ -187,7 +225,7 @@ class TActivePageAdapter extends TControlAdapter  	}
  	/**
 -	 * Gets the callback client script handler that allows javascript functions
 +	 * Gets the callback client script handler. It handlers the javascript functions
  	 * to be executed during the callback response. 
  	 * @return TCallbackClientScript callback client handler.
  	 */
 @@ -267,16 +305,34 @@ class TCallbackEventParameter extends TEventParameter  	}
  }
 +/**
 + * TCallbackErrorHandler class.
 + * 
 + * Captures errors and exceptions and send them back during callback response.
 + * When the application is in debug mode, the error and exception stack trace
 + * are shown. A TJavascriptLogger must be present on the client-side to view
 + * the error stack trace.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 19:11:47 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
  class TCallbackErrorHandler extends TErrorHandler
  {
 +	/**
 +	 * Displays the exceptions to the client-side TJavascriptLogger.
 +	 * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded.
 +	 * @param Exception exception details.
 +	 */
  	protected function displayException($exception)
  	{
  		if($this->getApplication()->getMode()===TApplication::STATE_DEBUG)
  		{
  			$response = $this->getApplication()->getResponse();
 -			$data = TJavascript::jsonEncode($this->getExceptionData($exception));			
 +			$trace = TJavascript::jsonEncode($this->getExceptionStackTrace($exception));			
  			$response->appendHeader('HTTP/1.0 500 Internal Error');
 -			$response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$data);
 +			$response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace);
  		}
  		else
  		{
 @@ -286,9 +342,13 @@ class TCallbackErrorHandler extends TErrorHandler  		$this->getApplication()->getResponse()->flush();
  	}
 -	private function getExceptionData($exception)
 +	/**
 +	 * @param Exception exception details.
 +	 * @return array exception stack trace details.
 +	 */
 +	private function getExceptionStackTrace($exception)
  	{
 -		$data['code']=$exception->getCode() > 0 ? $exception->getCode() : 505;
 +		$data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500;
  		$data['file']=$exception->getFile();
  		$data['line']=$exception->getLine();
  		$data['trace']=$exception->getTrace();
 @@ -310,13 +370,16 @@ class TCallbackErrorHandler extends TErrorHandler  	}
  }
 -class TInvalidCallbackHandlerException extends TException
 +/**
 + * TInvalidCallbackException class.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 19:17:13 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TInvalidCallbackException extends TException
  {
 -	
  } 
 -class TInvalidCallbackRequestException extends TException
 -{
 -}
 -
  ?>
\ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActivePanel.php b/framework/Web/UI/ActiveControls/TActivePanel.php index db1d8a7d..787b65d3 100644 --- a/framework/Web/UI/ActiveControls/TActivePanel.php +++ b/framework/Web/UI/ActiveControls/TActivePanel.php @@ -1,9 +1,37 @@  <?php
 -/*
 - * Created on 5/05/2006
 +/**
 + * TActivePanel file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2006 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version :   : 
 + * @package System.Web.UI.ActiveControls
   */
 -class TActivePanel extends TPanel
 +/**
 + * TActivePanel is the TPanel active control counterpart.
 + * 
 + * TActivePanel allows the client-side panel contents to be updated during a 
 + * callback response using the {@link flush} method.
 + * 
 + * Example: Assume $param is an instance of TCallbackEventParameter attached to
 + * the OnCallback event a TCallback with ID "callback1", and
 + * "panel1" is the ID of a TActivePanel.
 + * <code>
 + * function callback1_requested($sender, $param)
 + * {
 + * 	   $this->panel1->flush($param->getOutput());
 + * }
 + * </code>
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 01:23:54 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TActivePanel extends TPanel implements IActiveControl
  {
  	/**
  	 * Creates a new callback control, sets the adapter to
 @@ -16,10 +44,26 @@ class TActivePanel extends TPanel  		$this->setAdapter(new TActiveControlAdapter($this));
  	}
 +	/**
 +	 * @return TBaseActiveControl standard active control options.
 +	 */
  	public function getActiveControl()
  	{
  		return $this->getAdapter()->getBaseActiveControl();
  	}
 +	
 +	/**
 +	 * Renders and replaces the panel's content on the client-side.
 +	 * @param THtmlWriter html writer
 +	 */
 +	public function flush($writer)
 +	{
 +		if($this->getActiveControl()->canUpdateClientSide())
 +		{
 +			$this->render($writer);
 +			$this->getPage()->getCallbackClient()->replaceContent($this,$writer);
 +		}
 +	}
  } 
  ?>
\ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActiveTextBox.php b/framework/Web/UI/ActiveControls/TActiveTextBox.php index ece08e11..2e207f92 100644 --- a/framework/Web/UI/ActiveControls/TActiveTextBox.php +++ b/framework/Web/UI/ActiveControls/TActiveTextBox.php @@ -1,9 +1,30 @@  <?php
 -/*
 - * Created on 6/05/2006
 +/**
 + * TActiveTextBox class file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2006 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Revision: $  : $
 + * @package System.Web.UI.ActiveControls
   */
 -class TActiveTextBox extends TTextBox
 +/**
 + * TActiveTextBox class.
 + * 
 + * TActiveTextBox allows the {@link setText Text} property of the textbox to
 + * be changed during callback. When {@link setAutoPostBack AutoPostBack} property
 + * is true, changes to the textbox contents will perform a callback request causing
 + * {@link onTextChanged OnTextChange} to be fired first followed by {@link onCallback OnCallback}
 + * event.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 20:05:16 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TActiveTextBox extends TTextBox implements ICallbackEventHandler, IActiveControl
  {
  	/**
  	 * Creates a new callback control, sets the adapter to
 @@ -15,7 +36,10 @@ class TActiveTextBox extends TTextBox  		parent::__construct();
  		$this->setAdapter(new TActiveControlAdapter($this));
  	}
 -
 +	
 +	/**
 +	 * @return TBaseActiveCallbackControl standard callback control options.
 +	 */
  	public function getActiveControl()
  	{
  		return $this->getAdapter()->getBaseActiveControl();
 @@ -31,6 +55,43 @@ class TActiveTextBox extends TTextBox  		if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData())
  			$this->getPage()->getCallbackClient()->setValue($this, $value);
  	}
 +	
 +	/**
 +	 * Raises the callback event. This method is required by {@link
 +	 * ICallbackEventHandler} interface. If {@link getCausesValidation
 +	 * CausesValidation} is true, it will invoke the page's {@link TPage::
 +	 * validate validate} method first. It will raise {@link onTextChanged
 +	 * onTextChanged} event first and then the {@link onCallback OnCallback} event. 
 +	 * This method is mainly used by framework and control developers.
 +	 * @param TCallbackEventParameter the event parameter
 +	 */
 + 	public function raiseCallbackEvent($param)
 +	{
 +		$this->raisePostDataChangedEvent($param);
 +		$this->onCallback($param);
 +	}	
 +	
 +	/**
 +	 * This method is invoked when a callback is requested. The method raises
 +	 * 'OnCallback' event to fire up the event handlers. If you override this
 +	 * method, be sure to call the parent implementation so that the event
 +	 * handler can be invoked.
 +	 * @param TCallbackEventParameter event parameter to be passed to the event handlers
 +	 */
 +	public function onCallback($param)
 +	{
 +		$this->raiseEvent('OnCallback', $this, $param);
 +	}
 +
 +	/**
 +	 * Renders the javascript for textbox.
 +	 */
 +	protected function renderClientControlScript($writer)
 +	{
 +		$writer->addAttribute('id',$this->getClientID());
 +		$this->getActiveControl()->registerCallbackClientScript(
 +			$this->getClientClassName(), $this->getPostBackOptions());
 +	}
  	/**
  	 * Gets the name of the javascript class responsible for performing postback for this control.
 diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index 63b1d089..601894ff 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -3,7 +3,7 @@   * Created on 7/05/2006
   */
 -class TAutoComplete extends TActiveTextBox implements ICallbackEventHandler, INamingContainer
 +class TAutoComplete extends TActiveTextBox implements INamingContainer
  {
  	/**
  	 * @var ITemplate template for repeater items
 diff --git a/framework/Web/UI/ActiveControls/TBaseActiveControl.php b/framework/Web/UI/ActiveControls/TBaseActiveControl.php index 13d953c8..71d50a67 100644 --- a/framework/Web/UI/ActiveControls/TBaseActiveControl.php +++ b/framework/Web/UI/ActiveControls/TBaseActiveControl.php @@ -10,6 +10,8 @@   * @package System.Web.UI.ActiveControls
   */
 +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSideOptions');
 +
  /**
   * TBaseActiveControl class provided additional basic property for every
   * active control. An instance of TBaseActiveControl or its decendent
 @@ -163,42 +165,66 @@ class TBaseActiveCallbackControl extends TBaseActiveControl  		}
  		return $client;
  	}
 +	
 +	/**
 +	 * Sets the client side options. Can only be set when client side is null.
 +	 * @param TCallbackClientSideOptions client side options.
 +	 */
 +	public function setClientSide($client)
 +	{
 +		if(is_null($this->getOption('ClientSide')))
 +			$this->setOption('ClientSide', $client);
 +		else
 +			throw new TConfigurationException(
 +				'active_controls_client_side_exists', $this->getControl()->getID());
 +	}
  	/**
  	 * @return TCallbackClientSideOptions callback client-side options.
  	 */
  	protected function createClientSideOptions()
  	{
 -		if(($id=$this->getCallbackOptionID())!=='' 
 -			&& ($control=$this->getControl()->findControl($id))!==null)
 -		{
 -			if($control instanceof TCallbackOptions)
 -				return $control->getClientSide();
 -		}
  		return new TCallbackClientSideOptions;
  	}
  	/**
 -	 * Sets the ID of a TCallbackOptions component to duplicate the client-side
 +	 * Sets default callback options. Takes the ID of a TCallbackOptions 
 +	 * component to duplicate the client-side
  	 * options for this control. The {@link getClientSide ClientSide}
  	 * subproperties has precendent over the CallbackOptions property.
  	 * @param string ID of a TCallbackOptions control from which ClientSide
  	 * options are cloned.
  	 */
 -	public function setCallbackOptionID($value)
 +	public function setCallbackOptions($value)
  	{
  		$this->setOption('CallbackOptions', $value, '');
  	}
  	/**
  	 * @return string ID of a TCallbackOptions control from which ClientSide
 -	 * options are cloned.
 +	 * options are duplicated.
  	 */
 -	public function getCallbackOptionID()
 +	public function getCallbackOptions()
  	{
  		return $this->getOption('CallbackOptions', '');
  	}
 +	/** 
 +	 * Returns an array of default callback client-side options. The default options
 +	 * are obtained from the client-side options of a TCallbackOptions control with
 +	 * ID specified by {@link setCallbackOptionsID CallbackOptionsID}. 
 +	 * @return array list of default callback client-side options. 
 +	 */
 +	protected function getDefaultClientSideOptions()
 +	{
 +		if(($id=$this->getCallbackOptions())!=='' 
 +			&& ($control=$this->getControl()->findControl($id))!==null
 +			&& $control instanceof TCallbackOptions)
 +				return $control->getClientSide()->getOptions()->toArray();
 +		else
 +			return array();
 +	}
 +
  	/**
  	 * @return boolean whether callback event trigger by this button will cause
  	 * input validation, default is true
 @@ -255,7 +281,8 @@ class TBaseActiveCallbackControl extends TBaseActiveControl  	 */
  	protected function getClientSideOptions()
  	{
 -		$options = $this->getClientSide()->getOptions()->toArray();
 +		$default = $this->getDefaultClientSideOptions();
 +		$options = array_merge($default,$this->getClientSide()->getOptions()->toArray());
  		$validate = $this->getCausesValidation();
  		$options['CausesValidation']= $validate ? '' : false;
  		$options['ValidationGroup']=$this->getValidationGroup();
 diff --git a/framework/Web/UI/ActiveControls/TCallback.php b/framework/Web/UI/ActiveControls/TCallback.php index c42b2cd7..3c7d70c5 100644 --- a/framework/Web/UI/ActiveControls/TCallback.php +++ b/framework/Web/UI/ActiveControls/TCallback.php @@ -35,7 +35,7 @@   * @package System.Web.UI.ActiveControls
   * @since 3.0
   */
 -class TCallback extends TControl implements ICallbackEventHandler
 +class TCallback extends TControl implements ICallbackEventHandler, IActiveControl
  {	
  	/**
  	 * Creates a new callback control, sets the adapter to
 diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 5d317c8a..d83bf90a 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -313,7 +313,7 @@ class TCallbackClientScript extends TApplicationComponent  	 * @see insertBefore
  	 * @see insertAfter
  	 */
 -	public function replace($element, $content, $method="Element.replace", $boundary=null)
 +	protected function replace($element, $content, $method="Element.replace", $boundary=null)
  	{
  		if($content instanceof TControl)
  		{
 @@ -325,12 +325,31 @@ class TCallbackClientScript extends TApplicationComponent  			$boundary = $this->getResponseContentBoundary($content);
  			$content = null;
  		}
 -
 +		
  		$this->callClientFunction('Prado.Element.replace', 
  					array($element, $method, $content, $boundary));		
  	}
  	/**
 +	 * Replace the content of an element with new content contained in writer.
 +	 * @param TControl|string control element or HTML element id.
 +	 * @param THtmlWriter writer for the content.
 +	 */
 +	public function replaceContent($element,$writer)
 +	{
 +		$this->replace($element, $writer);
 +	}
 +	
 +	/**
 +	 * Evaluate a block of javascript enclosed in a boundary.
 +	 * @param THtmlWriter writer for the content.
 +	 */
 +	public function evaluateScript($writer)
 +	{
 +		$this->replace(null, $writer, 'Prado.Element.evaluateScript');
 +	}
 +	
 +	/**
  	 * Renders the control and return the content boundary from
  	 * TCallbackResponseWriter. This method should only be used by framework
  	 * component developers.
 diff --git a/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php index 3f54e013..e8c2dc26 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php @@ -266,11 +266,17 @@ class TCallbackClientSideOptions extends TClientSideOptions  		return is_null($option) ? true : $option;
  	}
 +	/**
 +	 * @return string post back target ID
 +	 */
  	public function getPostBackTarget()
  	{
  		return $this->getOption('EventTarget');
  	}
 +	/**
 +	 * @param string post back target ID
 +	 */
  	public function setPostBackTarget($value)
  	{
  		if($value instanceof TControl)
 @@ -278,17 +284,21 @@ class TCallbackClientSideOptions extends TClientSideOptions  		$this->setOption('EventTarget', $value);
  	}
 +	/**
 +	 * @return string post back event parameter.
 +	 */
  	public function getPostBackParameter()
  	{
  		return $this->getOption('EventParameter');
  	}
 +	/**
 +	 * @param string post back event parameter.
 +	 */
  	public function setPostBackParameter($value)
  	{
  		$this->setOption('EventParameter', $value);
  	}
 -	
 -	
  } 
  ?>
\ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TCallbackOptions.php b/framework/Web/UI/ActiveControls/TCallbackOptions.php index fc0becb5..588cbf8a 100644 --- a/framework/Web/UI/ActiveControls/TCallbackOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackOptions.php @@ -1,10 +1,31 @@  <?php
 -/*
 - * Created on 12/05/2006
 +/**
 + * TCallbackOptions component class file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2006 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Revision: $  : $
 + * @package System.Web.UI.ActiveControls
   */
 +/**
 + * TCallbackOptions class.
 + * 
 + * TCallbackOptions allows common set of callback client-side options
 + * to be attached to other active controls.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Sun Jun 18 08:10:50 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
  class TCallbackOptions extends TControl
  { 
 +	/**
 +	 * @var TCallbackClientSideOptions client side callback options.
 +	 */
  	private $_clientSide;
  	/**
 @@ -28,7 +49,6 @@ class TCallbackOptions extends TControl  	{
  		return new TCallbackClientSideOptions;
  	}
 -	
  }
  ?>
\ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php b/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php index d1ec67ad..1f11f973 100755 --- a/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php +++ b/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php @@ -1,14 +1,48 @@  <?php +/** + * TCallbackResponseAdapter and TCallbackResponseWriter class file. + * + * @author Wei Zhuo <weizhuo[at]gamil[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version 3.0 + * @package System.Web.UI.ActiveControls + */  /** + * TCallbackResponseAdapter alters the THttpResponse's outputs.   *  + * A TCallbackResponseWriter is used instead of the TTextWrite when + * createHtmlWriter is called. Each call to createHtmlWriter will create + * a new TCallbackResponseWriter. When flushContent() is called each + * instance of TCallbackResponseWriter's content is flushed. + *  + * The callback response data can be set using the {@link setResponseData ResponseData} + * property. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Revision: $  Sun Jun 18 07:52:14 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0   */  class TCallbackResponseAdapter extends THttpResponseAdapter  { +	/** +	 * @TCallbackResponseWriter[] list of writers. +	 */  	private $_writers=array(); -	 +	/** +	 * @mixed callback response data. +	 */  	private $_data; +	/** +	 * Returns a new instance of THtmlWriter.  +	 * An instance of TCallbackResponseWriter is created to hold the content. +	 * @param string writer class name. +	 * @param THttpResponse http response handler. +	 */  	public function createNewHtmlWriter($type,$response)  	{  		$writer = new TCallbackResponseWriter(); @@ -16,6 +50,9 @@ class TCallbackResponseAdapter extends THttpResponseAdapter  		return parent::createNewHtmlWriter($type,$writer);  	} +	/** +	 * Flushes the contents in the writers. +	 */  	public function flushContent()  	{  		foreach($this->_writers as $writer) @@ -23,36 +60,75 @@ class TCallbackResponseAdapter extends THttpResponseAdapter  		parent::flushContent();  	} +	/** +	 * @param mixed callback response data. +	 */  	public function setResponseData($data)  	{  		$this->_data = $data;  	} +	/** +	 * @return mixed callback response data. +	 */  	public function getResponseData()  	{  		return $this->_data;  	}  } +/** + * TCallbackResponseWriter class. + *  + * TCallbackResponseWriter class enclosed a chunck of content within a + * html comment boundary. This allows multiple chuncks of content to return + * in the callback response and update multiple HTML elements. + * + * The {@link setBoundary Boundary} property sets boundary identifier in the + * HTML comment that forms the boundary. By default, the boundary identifier + * is generated from the object instance ID. + *  + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Revision: $  Sun Jun 18 08:02:21 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */  class TCallbackResponseWriter extends TTextWriter  { +	/** +	 * @var string boundary ID +	 */  	private $_boundary; +	/** +	 * Constructor. Generates boundary ID using object instance ID. +	 */  	public function __construct()  	{  		$this->_boundary = sprintf('%x',crc32((string)$this));  	} +	/** +	 * @return string boundary identifier. +	 */  	public function getBoundary()  	{  		return $this->_boundary;  	} +	/** +	 * @param string boundary identifier. +	 */  	public function setBoundary($value)  	{  		$this->_boundary = $value;  	} +	/** +	 * Returns the text content wrapped within a HTML comment with boundary +	 * identifier as its comment content. +	 * @return string text content chunck. +	 */  	public function flush()  	{  		$content = '<!--'.$this->getBoundary().'-->'; diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index eb5e445a..8f43fbab 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -429,6 +429,22 @@ class TClientScriptManager extends TApplicationComponent  	{  		return isset($this->_endScripts[$key]);  	} +	 +	/** +	 * @return boolean true if any end scripts are registered. +	 */ +	public function hasEndScripts() +	{ +		return count($this->_endScripts) > 0; +	} +	 +	/** +	 * @return boolean true if any begin scripts are registered. +	 */ +	public function hasBeginScripts() +	{ +		return count($this->_beginScripts) > 0; +	}  	/**  	 * @param string a unique key diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index a1d64765..838d0342 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -269,6 +269,8 @@ class TPage extends TTemplateControl  	 */
  	protected function processCallbackRequest($writer)
  	{
 +		Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter');
 +		
  		$this->setAdapter(new TActivePageAdapter($this));
  		Prado::trace("Page onPreInit()",'System.Web.UI.TPage');
 diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php index 1194089e..a0801d4c 100644 --- a/framework/Web/UI/WebControls/TBaseValidator.php +++ b/framework/Web/UI/WebControls/TBaseValidator.php @@ -124,7 +124,8 @@ abstract class TBaseValidator extends TLabel implements IValidator  	}  	/** -	 * Adds attributes to renderer. +	 * Adds attributes to renderer. Calls parent implementation and renders the  +	 * client control scripts.  	 * @param THtmlWriter the renderer  	 */  	protected function addAttributesToRender($writer) @@ -137,6 +138,7 @@ abstract class TBaseValidator extends TLabel implements IValidator  			$writer->addStyleAttribute('visibility','hidden');  		$writer->addAttribute('id',$this->getClientID());  		parent::addAttributesToRender($writer); +		$this->renderClientControlScript($writer);  	}  	/** @@ -217,11 +219,10 @@ abstract class TBaseValidator extends TLabel implements IValidator  	 * Renders the javascript code to the end script.  	 * If you override this method, be sure to call the parent implementation  	 * so that the event handlers can be invoked. -	 * @param TEventParameter event parameter to be passed to the event handlers +	 * @param THtmlWriter the renderer  	 */ -	public function onPreRender($param) +	public function renderClientControlScript($writer)  	{ -		parent::onPreRender($param);  		$scripts = $this->getPage()->getClientScript();  		$formID=$this->getPage()->getForm()->getClientID();  		$scriptKey = "TBaseValidator:$formID"; @@ -236,7 +237,7 @@ abstract class TBaseValidator extends TLabel implements IValidator  			$this->registerClientScriptValidator();  		$this->updateControlCssClass();  	} - +	  	/**  	 * Update the ControlToValidate component's css class depending  	 * if the ControlCssClass property is set, and whether this is valid. diff --git a/framework/Web/UI/WebControls/TClientScript.php b/framework/Web/UI/WebControls/TClientScript.php index 23aa1425..020b44b8 100644 --- a/framework/Web/UI/WebControls/TClientScript.php +++ b/framework/Web/UI/WebControls/TClientScript.php @@ -22,6 +22,11 @@   * <com:TClientScript UsingPradoScripts="effects, rico" />
   * </code>
   * 
 + * The {@link setPreRenderControlTypes PreRenderControlTypes} property can
 + * be used to specify that controls type/class names that should pre-render itself
 + * even though they may not be rendered on the page. This is useful to publish
 + * controls that require assets and is only visible after a callback response.
 + * 
   * @TODO May be use it to include stylesheets as well.
   *
   * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 @@ -54,6 +59,23 @@ class TClientScript extends TControl  	}
  	/**
 +	 * @param string comma delimited list of controls that wish to be prerendered 
 +	 * so as to publish its assets.
 +	 */
 +	public function setPreRenderControlTypes($value)
 +	{
 +		$this->setViewState('PreRenderControls', $value);
 +	}
 +	
 +	/**
 +	 * @return string comma delimited list of controls types that require prerendering.
 +	 */
 +	public function getPreRenderControlTypes()
 +	{
 +		return $this->getViewState('PreRenderControls', '');
 +	}
 +	
 +	/**
  	 * Calls the client script manager to add each of the requested client
  	 * script libraries.
  	 * @param mixed event parameter
 @@ -69,6 +91,23 @@ class TClientScript extends TControl  			if(strlen($script) > 0)
  				$cs->registerPradoScript($script);
  		}
 +		$this->preRenderControls($param);
 +	}
 +	
 +	/**
 +	 * PreRender other controls to allow them to publish their assets. Useful
 +	 * when callback response components that require assets to be present on the page.
 +	 * @param mixed event paramater
 +	 */
 +	protected function preRenderControls($param)
 +	{
 +		$types = preg_split('/,|\s+/', $this->getPreRenderControlTypes());
 +		foreach($types as $type)
 +		{
 +			$control = Prado::createComponent($type);
 +			$control->setPage($this->getPage());
 +			$control->onPreRender($param);
 +		}		
  	}
  }
 diff --git a/framework/interfaces.php b/framework/interfaces.php index d87cc20f..b28d3881 100644 --- a/framework/interfaces.php +++ b/framework/interfaces.php @@ -292,7 +292,10 @@ interface IBindable   */
  interface IActiveControl
  {
 -	
 +	/**
 +	 * @return TBaseActiveControl Active control properties.
 +	 */
 +	public function getActiveControl(); 	
  }
  /**
 @@ -315,11 +318,6 @@ interface ICallbackEventHandler  	 * @param TCallbackEventParameter the parameter associated with the callback event
  	 */
  	public function raiseCallbackEvent($eventArgument);
 -	
 -	/**
 -	 * @return TBaseActiveControl Active control properties.
 -	 */
 -	public function getActiveControl(); 
  }
  ?>
\ No newline at end of file diff --git a/framework/prado-cli.php b/framework/prado-cli.php new file mode 100755 index 00000000..f8ce826c --- /dev/null +++ b/framework/prado-cli.php @@ -0,0 +1,413 @@ +#!/usr/bin/php +<?php + +/** + * Prado command line developer tools.  + */ + +if(!isset($_SERVER['argv']))  +	die('Must be run from the command line'); + +//command line options. +$options['create'] = array('c:', 'create=', '<directory>', 'create a new project <directory>'); +$console = new ConsoleOptions($options, $_SERVER['argv']); + +if($console->hasOptions()) +{ +	if($dir = $console->getOption('create')) +		create_new_prado_project($dir); +} +else +{ +	$details = $console->renderOptions(); +echo <<<EOD +Usage: php prado-cli.php [-c] <directory> +Options: +$details +Example: php prado-cli.php -c ./example1 + + +EOD; +} +	 +/** + * Functions to create new prado project. + */ + +function create_new_prado_project($dir) +{ +	if(strlen(trim($dir)) == 0) +		return; +		 +	$rootPath = realpath(dirname(trim($dir))); +	 +	$basePath = $rootPath.'/'.basename($dir); +	$assetPath = $basePath.'/assets'; +	$protectedPath  = $basePath.'/protected'; +	$runtimePath = $basePath.'/protected/runtime';  +	$pagesPath = $protectedPath.'/pages'; + +	$indexFile = $basePath.'/index.php'; +	$htaccessFile = $protectedPath.'/.htaccess'; +	$defaultPageFile = $pagesPath.'/Home.page'; +	 +	create_directory($basePath, 0755); +	create_directory($assetPath,0777); +	create_directory($protectedPath,0755); +	create_directory($runtimePath,0777); +	create_directory($pagesPath,0755); +		 +	file_put_contents($indexFile, render_index_file()); +	file_put_contents($htaccessFile, render_htaccess_file()); +	file_put_contents($defaultPageFile, render_default_page()); +} + +function create_directory($dir, $mask) +{ +	if(!is_dir($dir)) +		mkdir($dir); +	if(is_dir($dir)) +		chmod($dir, $mask); +}	 + +function render_index_file() +{ +	$framework = realpath(dirname(__FILE__)).'/prado.php'; +return '<?php + +$basePath=dirname(__FILE__); +$frameworkPath="'.$framework.'"; +$assetsPath=$basePath."/assets"; +$runtimePath=$basePath."/protected/runtime"; + +if(!is_file($frameworkPath)) +	die("Unable to find prado framework path $frameworkPath."); +if(!is_writable($assetsPath)) +	die("Please make sure that the directory $assetsPath is writable by Web server process."); +if(!is_writable($runtimePath)) +	die("Please make sure that the directory $runtimePath is writable by Web server process."); + +require_once($frameworkPath); + +$application=new TApplication; +$application->run(); + +?>'; +} + +function render_htaccess_file() +{ +	return 'deny from all'; +} + + +function render_default_page() +{ +return <<<EOD +<h1>Welcome to Prado!</h1> +EOD; +} + +/* vim: set expandtab tabstop=4 shiftwidth=4: */ +// +----------------------------------------------------------------------+ +// | PHP Version 4                                                        | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group                                | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license,       | +// | that is bundled with this package in the file LICENSE, and is        | +// | available through the world-wide-web at the following url:           | +// | http://www.php.net/license/3_0.txt.                                  | +// | If you did not receive a copy of the PHP license and are unable to   | +// | obtain it through the world-wide-web, please send a note to          | +// | license@php.net so we can mail you a copy immediately.               | +// +----------------------------------------------------------------------+ +// | Author: Andrei Zmievski <andrei@php.net>                             | +// +----------------------------------------------------------------------+ +// +// $Id: Getopt.php,v 1.21.4.7 2003/12/05 21:57:01 andrei Exp $ +  +/** + * Command-line options parsing class. + * + * @author Andrei Zmievski <andrei@php.net> + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ +class ConsoleOptions  +{	 +	private $_short_options; +	private $_long_options; +	private $_options; +	private $_args; +	private $_values; +	 +	/** +	 * @param array list of options with the following format +	 * <tt>option['name'] = array('short', 'long', 'parameter', 'description')</tt> +	 * see @link setOptions} for details on 'short' and 'long' string details. +	 */ +	public function __construct($options, $args) +	{ +		$short = ''; +		$long = array(); +		foreach($options as $option) +		{ +			$short .= $option[0]; +			$long[] = $option[1]; +		} +		$this->setOptions($short,$long); +		$this->_options = $options; +		$this->_args = $args; +	} +		 +	/** +    * The second parameter is a string of allowed short options. Each of the +    * option letters can be followed by a colon ':' to specify that the option +    * requires an argument, or a double colon '::' to specify that the option +    * takes an optional argument. +    * +    * The third argument is an optional array of allowed long options. The +    * leading '--' should not be included in the option name. Options that +    * require an argument should be followed by '=', and options that take an +    * option argument should be followed by '=='. +    *  +    * @param string $short_options  specifies the list of allowed short options +    * @param array  $long_options   specifies the list of allowed long options + 	*/ +	protected function setOptions($short_options, $long_options=null) +	{ +		$this->_short_options = $short_options; +		$this->_long_options = $long_options; +	} +	 +	/** +	 * @return string list of options and its descriptions +	 */ +	public function renderOptions() +	{ +		$options = array(); +		$descriptions = array(); +		$max = 0; +		foreach($this->_options as $option) +		{ +			$short = str_replace(':','',$option[0]); +			$long = str_replace('=','',$option[1]); +			$desc = $option[2]; +			$details = " -{$short}, --{$long} {$desc}"; +			if(($len = strlen($details)) > $max) +				$max = $len; +			$options[] = $details; +			$descriptions[] = $option[3]; +		} +		 +		$content = ''; +		for($i = 0, $k = count($options); $i < $k; $i++) +			$content .= str_pad($options[$i],$max+3).$descriptions[$i]."\n"; +		return $content; +	} +	 +	/** +	 * @param string argument name +	 * @return string argument value +	 */ +	public function getOption($name) +	{ +		if(is_null($this->_values)) +			$this->_values = $this->getNamedOptions(); +		return isset($this->_values[$name]) ? $this->_values[$name] : null; +	} +	 +	/** +	 * @return array list of all options given. +	 */ +	public function getOptions() +	{ +		if(is_null($this->_values)) +			$this->_values = $this->getNamedOptions(); +		return $this->_values; +	} +	 +	/** +	 * @return boolean true if one or more options are given. +	 */ +	public function hasOptions() +	{ +		if(is_null($this->_values)) +			$this->_values = $this->getNamedOptions(); +		return count($this->_values) > 0; +	} +	 +	/** +	 * Parse the options from args into named arguements. +	 */ +	protected function getNamedOptions() +	{ +		$options = array(); +		$values = $this->parseOptions($this->_args); +		foreach($values[0] as $value) +		{ +			foreach($this->_options as $name => $option) +			{ +				if(strpos($option[0],$value[0]) !== false  +					|| strpos($option[1], $value[0]) !== false) +					$options[$name] = $value[1]; +			} +		} +		return $options; +	} +	 +    /** +     * Gets the command-line options. +     * +     * The parameter to this function should be the list of command-line +     * arguments without the leading reference to the running program. +     * +     * The return value is an array of two elements: the list of parsed +     * options and the list of non-option command-line arguments. Each entry in +     * the list of parsed options is a pair of elements - the first one +     * specifies the option, and the second one specifies the option argument, +     * if there was one. +     * +     * Long and short options can be mixed. +     * +     * @param array  $args           an array of command-line arguments +     * @return array two-element array containing the list of parsed options and +     * the non-option arguments +     */ +    public function parseOptions($args) +    { +        if (empty($args))  +            return array(array(), array()); + +		$short_options = $this->_short_options; +		$long_options = $this->_long_options; + +        $opts     = array(); +        $non_opts = array(); + +        settype($args, 'array'); + +        if ($long_options)  +            sort($long_options); + +        if (isset($args[0]{0}) && $args[0]{0} != '-')  +			array_shift($args); + +        reset($args); +         +		while (list($i, $arg) = each($args))  +		{ + +            /* The special element '--' means explicit end of +               options. Treat the rest of the arguments as non-options +               and end the loop. */ +            if ($arg == '--') { +                $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); +                break; +            } + +            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options))  +			{ +                $non_opts = array_merge($non_opts, array_slice($args, $i)); +                break; +            }  +			elseif (strlen($arg) > 1 && $arg{1} == '-')  +                $this->parseLongOption(substr($arg, 2), $long_options, $opts, $args); +			else  +                $this->parseShortOption(substr($arg, 1), $short_options, $opts, $args); +        } + +        return array($opts, $non_opts); +    } + +    private function parseShortOption($arg, $short_options, &$opts, &$args) +    { +        for ($i = 0; $i < strlen($arg); $i++)  +		{ +            $opt = $arg{$i}; +            $opt_arg = null; + +            /* Try to find the short option in the specifier string. */ +            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') +                throw new Exception("Console_Getopt: unrecognized option -- $opt"); + +            if (strlen($spec) > 1 && $spec{1} == ':')  +			{ +                if (strlen($spec) > 2 && $spec{2} == ':')  +				{ +                    if ($i + 1 < strlen($arg))  +					{ +                        /* Option takes an optional argument. Use the remainder of +                           the arg string if there is anything left. */ +                        $opts[] = array($opt, substr($arg, $i + 1)); +                        break; +                    } +                }  +				else  +				{ +                    /* Option requires an argument. Use the remainder of the arg +                       string if there is anything left. */ +                    if ($i + 1 < strlen($arg))  +					{ +                        $opts[] = array($opt,  substr($arg, $i + 1)); +                        break; +                    }  +					else if (list(, $opt_arg) = each($args)) +                        /* Else use the next argument. */; +                    else +                        throw new Exception("Console_Getopt: option requires an argument -- $opt"); +                } +            } + +            $opts[] = array($opt, $opt_arg); +        } +    } + +    private function parseLongOption($arg, $long_options, &$opts, &$args) +    { +        @list($opt, $opt_arg) = explode('=', $arg); +        $opt_len = strlen($opt); + +        for ($i = 0; $i < count($long_options); $i++)  +		{ +            $long_opt  = $long_options[$i]; +            $opt_start = substr($long_opt, 0, $opt_len); + +            /* Option doesn't match. Go on to the next one. */ +            if ($opt_start != $opt) +                continue; + +            $opt_rest  = substr($long_opt, $opt_len); + +            /* Check that the options uniquely matches one of the allowed +               options. */ +            if ($opt_rest != '' && $opt{0} != '=' && +                $i + 1 < count($long_options) && +                $opt == substr($long_options[$i+1], 0, $opt_len))  +			{ +                throw new Exception("Console_Getopt: option --$opt is ambiguous"); +            } + +            if (substr($long_opt, -1) == '=')  +			{ +                if (substr($long_opt, -2) != '==')  +				{ +                    /* Long option requires an argument. +                       Take the next argument if one wasn't specified. */; +                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args)))  +                        throw new Exception("Console_Getopt: option --$opt requires an argument"); +                } +            }  +			else if ($opt_arg)  +                throw new Exception("Console_Getopt: option --$opt doesn't allow an argument"); + +            $opts[] = array($opt, $opt_arg); +            return; +        } + +        throw new Exception("Console_Getopt: unrecognized option --$opt"); +    } +} + + +?>
\ No newline at end of file @@ -97,6 +97,7 @@ The installation is done! You will see the following subdirectories,  <h3>Special Credits To</h3>
  <ul>
 +<li>Stever for providing an Apple G3 notebook for testing safari browser issues.</li>
  <li>All PRADO users - great suggestions, feedback and support</li>
  <li>ASP.NET 2.0 for its great inspiration and reference</li>
  <li>All <a href="framework/3rdParty/readme.html">third-party work</a> used in PRADO</li>
 diff --git a/tests/FunctionalTests/active-controls/index.php b/tests/FunctionalTests/active-controls/index.php new file mode 100644 index 00000000..8fa57a6a --- /dev/null +++ b/tests/FunctionalTests/active-controls/index.php @@ -0,0 +1,20 @@ +<?php + +$basePath=dirname(__FILE__); +$frameworkPath="/Users/weizhuo/Sites/workspace/prado-trunk/framework/prado.php"; +$assetsPath=$basePath."/assets"; +$runtimePath=$basePath."/protected/runtime"; + +if(!is_file($frameworkPath)) +	die("Unable to find prado framework path $frameworkPath."); +if(!is_writable($assetsPath)) +	die("Please make sure that the directory $assetsPath is writable by Web server process."); +if(!is_writable($runtimePath)) +	die("Please make sure that the directory $runtimePath is writable by Web server process."); + +require_once($frameworkPath); + +$application=new TApplication; +$application->run(); + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/.htaccess b/tests/FunctionalTests/active-controls/protected/.htaccess new file mode 100644 index 00000000..3418e55a --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/.htaccess @@ -0,0 +1 @@ +deny from all
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/application.xml b/tests/FunctionalTests/active-controls/protected/application.xml new file mode 100755 index 00000000..03fe9bbb --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/application.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<application id="FeatureTests" Mode="Debug"> +	<paths> +		<using namespace="System.Web.UI.ActiveControls.*" /> +	</paths> +	<services> +	  <service id="page" class="TPageService" DefaultPage="FeatureList"> +	  </service> +	</services> +</application>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.page b/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.page new file mode 100644 index 00000000..5c2d1abb --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.page @@ -0,0 +1,9 @@ +<com:TForm ID="form1"> +	<h1>TActiveButton Functional Test</h1> +	<com:TActiveButton ID="button2" Text="Button 1"  +		OnClick="button2_onclick" OnCallback="button2_oncallback" /> +	 +	<com:TActiveLabel ID="label1" Text="Label 1" /> +		 +	<com:TJavascriptLogger /> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.php b/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.php new file mode 100644 index 00000000..6282b804 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveButtonTest.php @@ -0,0 +1,16 @@ +<?php + +class ActiveButtonTest extends TPage +{ +	function button2_onclick($sender, $param) +	{ +		$this->label1->Text = "Button 1 was clicked "; +	} +	 +	function button2_oncallback($sender, $param) +	{ +		$this->label1->Text .= "using callback!"; +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.page b/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.page new file mode 100644 index 00000000..468a524e --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.page @@ -0,0 +1,22 @@ +<com:TForm ID="form1"> +	<h1>Active Panel replacement tests </h1> +	<com:TCallback ID="callback1" OnCallback="callback1_requested" /> +	<com:TActivePanel ID="panel1"> +		<com:TPlaceHolder ID="content1" Visible="false"> +			Something lalala <com:TButton ID="button1" Text="Button Text"/> +		</com:TPlaceHolder> +	</com:TActivePanel> +	<div id="div1" style="border:1px solid #666; background-color: #ffe; text-align: center; padding:3em"> +		Click Me! +	</div> +	<script type="text/javascript"> +		Event.OnLoad(function() +		{ +			Event.observe($("div1"), "click", function() +			{ +				Prado.Callback("<%= $this->callback1->ClientID %>") +			}) +		}) +	</script> +	<com:TJavascriptLogger /> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.php b/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.php new file mode 100644 index 00000000..79e3d46c --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActivePanelTest.php @@ -0,0 +1,12 @@ +<?php + +class ActivePanelTest extends TPage +{ +	function callback1_requested($sender, $param) +	{ +		$this->content1->visible = true; +		$this->panel1->flush($param->output); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.page b/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.page new file mode 100644 index 00000000..d0a750ac --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.page @@ -0,0 +1,5 @@ +<com:TForm ID="form1"> +	<h1>ActiveTextBox Callback Test</h1> +	<com:TActiveTextBox	ID="textbox1" AutoPostBack="true" OnCallback="textbox1_callback" /> +	<com:TActiveLabel ID="label1" Text="Label 1" /> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.php b/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.php new file mode 100644 index 00000000..c3f729e5 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ActiveTextBoxCallback.php @@ -0,0 +1,11 @@ +<?php + +class ActiveTextBoxCallback extends TPage +{ +	function textbox1_callback($sender, $param) +	{ +		$this->label1->Text = 'Label 1: '.$sender->Text; +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.page b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.page new file mode 100644 index 00000000..93658bd7 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.page @@ -0,0 +1,48 @@ +<com:TForm ID="form1"> +	 +	<style> +	.autocomplete +	{ +	  border:1px solid #919EA9; +	  background-color:white; +	} +	.autocomplete ul, .autocomplete li +	{ +		margin: 0; +		padding: 0; +		list-style: none; +		font-size: 12px; +		font-family: Tahoma, Arial, Helvetica, sans-serif; +		color: #333; +	} + +	.autocomplete li +	{ +		padding: 4px; +		border-top: 1px solid #ccc; +	} +	.autocomplete .selected +	{ +	  background-color: #ffc; +	} +	</style> +	 +	<h1>TAutoComplete Test</h1> +	 +		<com:TAutoComplete Style="width: 20em" +			OnCallback="suggestCountries" +			Separator=", "  +			ResultPanel.CssClass="autocomplete" /> + +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<p><br /></p> +	<com:TJavascriptLogger /> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php new file mode 100644 index 00000000..938b8640 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php @@ -0,0 +1,34 @@ +<?php +/* + * Created on 7/05/2006 + */ + +class AutoCompleteTest extends TPage +{ +	public function suggestCountries($sender, $param) +	{ +		$sender->setDataSource($this->matchCountries($param->getParameter())); +		$sender->dataBind(); +		$sender->render($param->getOutput()); +	} +	 +	protected function matchCountries($token) +	{ +		$info = Prado::createComponent('System.I18N.core.CultureInfo', 'en'); +		$list = array(); +		$count = 0; +		$token = strtolower($token); +		foreach($info->getCountries() as $country) +		{ +			if(strpos(strtolower($country), $token) === 0) +			{ +				$list[] = $country; +				$count++; +				if($count > 10) break; +			}	 +		} +		return $list;				  +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/Calculator.page b/tests/FunctionalTests/active-controls/protected/pages/Calculator.page new file mode 100644 index 00000000..7a565658 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/Calculator.page @@ -0,0 +1,31 @@ +<com:TForm ID="form1"> +	<h1>Callback Enabled Calculator</h1> +	 +	<com:TActiveTextBox id="a" /> +	+ +	<com:TActiveTextBox id="b" /> +	= +	<com:TActiveTextBox id="c" /> +	 +	<com:TActiveButton id="sum"  +		onclick="do_sum"  +		text="Calculate!" /> +	 +	<com:TRequiredFieldValidator  +			ControlToValidate="a" +			ErrorMessage="left summand is required." +			ControlCssClass="required" +			Display="None" /> +	<com:TRequiredFieldValidator +			ControlToValidate="b" +			ErrorMessage="right summand is requied." +			ControlCssClass="required" +			Display="None" /> + +	<div class="summarybox"> +		<com:TValidationSummary +			ID="summary" +			HeaderText="Unable to calculate because" /> +	</div> +	 +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/Calculator.php b/tests/FunctionalTests/active-controls/protected/pages/Calculator.php new file mode 100644 index 00000000..098a8460 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/Calculator.php @@ -0,0 +1,12 @@ +<?php + +class Calculator extends TPage +{ +	public function do_sum($sender, $param) +	{ +		$this->c->Text = floatval($this->a->Text) + floatval($this->b->Text);	 +	} +} + + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/CallbackOptionsTest.page b/tests/FunctionalTests/active-controls/protected/pages/CallbackOptionsTest.page new file mode 100644 index 00000000..6e8b8a1e --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/CallbackOptionsTest.page @@ -0,0 +1,34 @@ +<com:TForm ID="form1"> +	<h1>TCallbackOptions Test</h1> +	 +	<com:TCallbackOptions ID="options1"> +		<prop:ClientSide.OnLoading> +			$("status").show(); +		</prop:ClientSide.OnLoading> +		<prop:ClientSide.OnComplete> +			$("status").hide(); +		</prop:ClientSide.OnComplete> +		<prop:ClientSide.OnSuccess> +			Element.update("label1", "Button 1 has returned"); +		</prop:ClientSide.OnSuccess> +	</com:TCallbackOptions> +	 +	<com:TActiveButton id="button1" Text="Button 1" ActiveControl.CallbackOptions="options1" /> +	<com:TActiveButton id="button2" Text="Button 2" ActiveControl.CallbackOptions="options1"> +		<prop:ActiveControl.ClientSide.OnSuccess> +			Element.update("label2", "Button 2 has returned"); +		</prop:ActiveControl.ClientSide.OnSuccess> +	</com:TActiveButton> +	<com:TActiveButton id="button3" Text="Button 3"> +		<prop:ActiveControl.ClientSide.OnSuccess> +			Element.update("label3", "Button 3 has returned"); +		</prop:ActiveControl.ClientSide.OnSuccess> +	</com:TActiveButton> +	 +	<div id="label1">Label 1</div> +	<div id="label2">Label 2</div> +	<div id="label3">Label 3</div> +	<div id="status" style="display:none; background-color: #c00; color:white; text-align:center; padding: 1em" > +		Loading...  +	</div> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/FeatureList.page b/tests/FunctionalTests/active-controls/protected/pages/FeatureList.page new file mode 100755 index 00000000..eab14a87 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/FeatureList.page @@ -0,0 +1,3 @@ +<com:TForm ID="form1"> +	<com:TBulletedList ID="List" DisplayMode="HyperLink"/>	 +</com:TForm> diff --git a/tests/FunctionalTests/active-controls/protected/pages/FeatureList.php b/tests/FunctionalTests/active-controls/protected/pages/FeatureList.php new file mode 100755 index 00000000..8f28f6de --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/FeatureList.php @@ -0,0 +1,34 @@ +<?php + +class FeatureList extends TPage +{ +	public function onLoad($param) +	{ +		parent::onLoad($param); +		$list=$this->getPageList(dirname(__FILE__),''); +		$this->List->DataSource=$list; +		$this->List->dataBind(); +	} + +	protected function getPageList($directory,$basePath) +	{ +		$list=array(); +		$folder=@opendir($directory); +		while($entry=@readdir($folder)) +		{ +			if($entry[0]==='.') +				continue; +			else if(is_file($directory.'/'.$entry)) +			{ +				if(($page=basename($entry,'.page'))!==$entry && strpos($page,'.')===false) +					$list['?page='.$basePath.$page]=$basePath.$page; +			} +			else +				$list=array_merge($list,$this->getPageList($directory.'/'.$entry,$basePath.$entry.'.')); +		} +		closedir($folder); +		return $list; +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/Home.page b/tests/FunctionalTests/active-controls/protected/pages/Home.page new file mode 100644 index 00000000..ff226d4e --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/Home.page @@ -0,0 +1 @@ +<h1>Welcome to Prado!</h1>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.page b/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.page new file mode 100644 index 00000000..d8eacf15 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.page @@ -0,0 +1,28 @@ +<com:TForm ID="form1"> +	<h1>Nested Active Controls Test</h1> +	<com:TCallback ID="callback1" OnCallback="callback1_requested" /> +	<com:TActivePanel ID="panel1"> +		<com:TPlaceHolder ID="content1" Visible="false"> +			Something lalala  +			<com:TActiveButton ID="button1" Text="Button 1" OnClick="button1_clicked" /> +			<com:TActiveLabel ID="label3" Text="Label 3" /> +			 +		</com:TPlaceHolder> +		<com:TActiveLabel ID="label1" Text="Label 1" /> +	</com:TActivePanel> +	<div id="div1" style="border:1px solid #666; background-color: #ffe; text-align: center; padding:1em; margin: 2em"> +		Click Me! +	</div> +	<com:TActiveLabel ID="label2" Text="Label 2" /> +	<script type="text/javascript"> +		Event.OnLoad(function() +		{ +			Event.observe($("div1"), "click", function() +			{ +				Prado.Callback("<%= $this->callback1->ClientID %>") +			}) +		}) +	</script> +	<com:TJavascriptLogger /> +	 +</com:TForm> diff --git a/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.php b/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.php new file mode 100644 index 00000000..a4a22cbf --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/NestedActiveControls.php @@ -0,0 +1,19 @@ +<?php + +class NestedActiveControls extends TPage +{ +	function callback1_requested($sender, $param) +	{ +		$this->content1->visible = true; +		$this->panel1->flush($param->output); +	} +	 +	function button1_clicked($sender, $param) +	{ +		$this->label1->Text = "Label 1: Button 1 Clicked"; +		$this->label2->Text = "Label 2: Button 1 Clicked"; +		$this->label3->Text = "Label 3: Button 1 Clicked"; +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/ActiveButtonTestCase.php b/tests/FunctionalTests/active-controls/tests/ActiveButtonTestCase.php new file mode 100644 index 00000000..0a294906 --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/ActiveButtonTestCase.php @@ -0,0 +1,16 @@ +<?php + +class ActiveButtonTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=ActiveButtonTest"); +		$this->verifyTextPresent("TActiveButton Functional Test"); +		$this->assertText("label1", "Label 1"); +		$this->click("button2"); +		$this->pause(500); +		$this->assertText("label1", "Button 1 was clicked using callback!"); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/ActivePanelTestCase.php b/tests/FunctionalTests/active-controls/tests/ActivePanelTestCase.php new file mode 100644 index 00000000..a9fb6c4e --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/ActivePanelTestCase.php @@ -0,0 +1,16 @@ +<?php + +class ActivePanelTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=ActivePanelTest"); +		$this->verifyTextPresent("Active Panel replacement tests"); +		$this->assertTextNotPresent('Something lalala'); +		$this->click("div1"); +		$this->pause(500); +		$this->assertTextPresent("Something lalala"); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/CalculatorTestCase.php b/tests/FunctionalTests/active-controls/tests/CalculatorTestCase.php new file mode 100644 index 00000000..2fb28224 --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/CalculatorTestCase.php @@ -0,0 +1,24 @@ +<?php + +class CalculatorTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=Calculator"); +		$this->assertTextPresent("Callback Enabled Calculator"); +		$this->assertNotVisible("summary"); +		 +		$this->click("sum"); +		$this->assertVisible("summary"); +		 +		$this->type("a", "2"); +		$this->type("b", "5"); +		 +		$this->click("sum"); +		$this->assertNotVisible("summary"); +		 +		$this->assertValue("c", "7"); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/CallbackOptionsTestCase.php b/tests/FunctionalTests/active-controls/tests/CallbackOptionsTestCase.php new file mode 100644 index 00000000..d72499cf --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/CallbackOptionsTestCase.php @@ -0,0 +1,34 @@ +<?php + +class CallbackOptionsTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=CallbackOptionsTest"); +		$this->verifyTextPresent("TCallbackOptions Test"); +		 +		$this->assertText("label1", "Label 1"); +		$this->assertText("label2", "Label 2"); +		$this->assertText("label3", "Label 3"); +		 +		$this->click("button1"); +		$this->pause(500); +		$this->assertText("label1", "Button 1 has returned"); +		$this->assertText("label2", "Label 2"); +		$this->assertText("label3", "Label 3"); + +		$this->click("button2"); +		$this->pause(500); +		$this->assertText("label1", "Button 1 has returned"); +		$this->assertText("label2", "Button 2 has returned"); +		$this->assertText("label3", "Label 3"); + +		$this->click("button3"); +		$this->pause(500); +		$this->assertText("label1", "Button 1 has returned"); +		$this->assertText("label2", "Button 2 has returned"); +		$this->assertText("label3", "Button 3 has returned"); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/NestedActiveControlsTestCase.php b/tests/FunctionalTests/active-controls/tests/NestedActiveControlsTestCase.php new file mode 100644 index 00000000..8d0cfb87 --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/NestedActiveControlsTestCase.php @@ -0,0 +1,26 @@ +<?php + +class NestedActiveControlsTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=NestedActiveControls"); +		$this->verifyTextPresent("Nested Active Controls Test"); +		$this->assertText("label1", "Label 1"); +		$this->assertText("label2", "Label 2"); +		$this->assertTextNotPresent("Label 3"); +		 +		$this->click("div1"); +		$this->pause(500); +		$this->assertTextPresent("Something lalala"); +		$this->assertText("label3", "Label 3"); +		 +		$this->click("button1"); +		$this->pause(500); +		$this->assertText("label1", "Label 1: Button 1 Clicked"); +		$this->assertText("label2", "Label 2: Button 1 Clicked"); +		$this->assertText("label3", "Label 3: Button 1 Clicked"); +	}	 +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/TextBoxCallbackTestCase.php b/tests/FunctionalTests/active-controls/tests/TextBoxCallbackTestCase.php new file mode 100644 index 00000000..24cba49b --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/TextBoxCallbackTestCase.php @@ -0,0 +1,17 @@ +<?php + +class TextBoxCallbackTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=ActiveTextBoxCallback"); +		$this->verifyTextPresent("ActiveTextBox Callback Test"); +		$this->assertText("label1", "Label 1"); +		 +		$this->type("textbox1", "hello!"); +		$this->pause(500); +		$this->assertText("label1", "Label 1: hello!"); +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active.php b/tests/FunctionalTests/active.php new file mode 100755 index 00000000..ca6ab635 --- /dev/null +++ b/tests/FunctionalTests/active.php @@ -0,0 +1,8 @@ +<?php + +require(dirname(__FILE__).'/PradoTester.php'); + +$tester=new PradoTester(dirname(__FILE__).'/active-controls/tests'); +$tester->run(new SimpleReporter()); + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/index.php b/tests/FunctionalTests/index.php index c22543c0..ceba599a 100644 --- a/tests/FunctionalTests/index.php +++ b/tests/FunctionalTests/index.php @@ -9,6 +9,7 @@ Prado Functional Test Suites  <ul>    <li><a href="quickstart.php">Tests of QuickStart Tutorial Demo</a></li>    <li><a href="validators.php">Tests of Validators</a></li> +  <li><a href="active.php">Tests of Active Controls</a> (<a href="active-controls/index.php">list of test samples</a>) </li>    <li><a href="tickets.php">Tests of Trac Tickets</a></li>    <li><a href="features.php">Tests of New Features</a> (<a href="features/index.php">list of new features</a>)</li>  </ul> | 
