diff options
7 files changed, 478 insertions, 30 deletions
| diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index f77fe0f2..c2f2fdfd 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -257,7 +257,8 @@ selection[method](isList?element:el,value);},click:function(element)  {var el=$(element);if(el)  Event.fireEvent(el,'click');},setAttribute:function(element,attribute,value)  {var el=$(element);if(attribute=="disabled"&&value==false) -el.removeAttribute(attribute);else +el.removeAttribute(attribute);else if(attribute.match(/^on/i)) +el[attribute]=eval("(function(event){"+value+"})");else  el.setAttribute(attribute,value);},setOptions:function(element,options)  {var el=$(element);if(el&&el.tagName.toLowerCase()=="select")  {while(el.length>0) diff --git a/framework/Web/Javascripts/prado/element.js b/framework/Web/Javascripts/prado/element.js index 59a8ea1e..9d0179dc 100644 --- a/framework/Web/Javascripts/prado/element.js +++ b/framework/Web/Javascripts/prado/element.js @@ -35,6 +35,8 @@ Prado.Element =  		var el = $(element);
  		if(attribute == "disabled" && value==false)
  			el.removeAttribute(attribute);
 +		else if(attribute.match(/^on/i)) //event methods
 +			el[attribute] = eval("(function(event){"+value+"})");
  		else
  			el.setAttribute(attribute, value);
  	},
 diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index de6310a6..e6e8585f 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -157,6 +157,14 @@ class TActiveControlAdapter extends TControlAdapter  		}
  		parent::saveState();
  	}
 +	
 +	/**
 +	 * @return TCallbackPageStateTracker state tracker.
 +	 */
 +	public function getStateTracker()
 +	{
 +		return $this->_stateTracker;
 +	}
  } 
  /**
 @@ -168,87 +176,378 @@ class TActiveControlAdapter extends TControlAdapter   * 
   * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
   * @version $Revision: $  Sun Jun 18 20:51:25 EST 2006 $
 - * @package System
 + * @package System.Web.UI.ActiveControls
   * @since 3.0
   */
  class TCallbackPageStateTracker
  {
 -	private $_states = array('Visible', 'Enabled', 'Attributes', 'Style', 'TabIndex', 'ToolTip', 'AccessKey');
 +	/**
 +	 * @var TMap new view state data
 +	 */
 +	private $_states;
 +	/**
 +	 * @var TMap old view state data
 +	 */
  	private $_existingState;
 +	/**
 +	 * @var TControl the control tracked
 +	 */
  	private $_control;
 +	/**
 +	 * @var object null object.
 +	 */
  	private $_nullObject;
 -	
 +		
 +	/**
 +	 * Constructor. Add a set of default states to track.
 +	 * @param TControl control to track.
 +	 */
  	public function __construct($control)
  	{
  		$this->_control = $control;
  		$this->_existingState = new TMap;
  		$this->_nullObject = new stdClass;
 +		$this->_states = new TMap;
 +		$this->addStatesToTrack();
  	}
 -	public function trackChanges()
 +	/**
 +	 * Add a list of view states to track. Each state is added
 +	 * to the StatesToTrack property with the view state name as key.
 +	 * The value should be an array with two enteries. The first entery
 +	 * is the name of the class that will calculate the state differences.
 +	 * The second entry is a php function/method callback that handles
 +	 * the changes in the viewstate.
 +	 */
 +	protected function addStatesToTrack()
  	{
 -		foreach($this->_states as $name)
 -			$this->_existingState[$name] = $this->_control->getViewState($name);
 +		$states = $this->getStatesToTrack();
 +		$states['Visible'] = array('TScalarDiff', array($this, 'updateVisible'));
 +		$states['Enabled'] = array('TScalarDiff', array($this, 'updateEnabled'));
 +		$states['Attributes'] = array('TMapCollectionDiff', array($this, 'updateAttributes'));
 +		$states['Style'] = array('TStyleDiff', array($this, 'updateStyle'));
 +		$states['TabIndex'] = array('TScalarDiff', array($this, 'updateTabIndex'));
 +		$states['ToolTip'] = array('TScalarDiff', array($this, 'updateToolTip'));
 +		$states['AccessKey'] = array('TScalarDiff', array($this, 'updateAccessKey'));	
  	}
 -	protected function getChanges()
 +	/**
 +	 * @return TMap list of viewstates to track.
 +	 */
 +	protected function getStatesToTrack()
  	{
 -		$diff = array();
 -		foreach($this->_states as $name)
 -		{
 -			$state = $this->_control->getViewState($name);
 -		//	echo " $name ";
 -			$changes = $this->difference($state, $this->_existingState[$name]);
 -		//	echo " \n ";
 -			if($changes !== $this->_nullObject)
 -				$diff[$name] = $changes;		
 -		}
 -		return $diff;
 +		return $this->_states;
  	}
 -
 -	protected function difference($value1, $value2)
 +	
 +	/**
 +	 * Start tracking view state changes. The clone function on objects are called
 +	 * for those viewstate having an object as value.
 +	 */
 +	public function trackChanges()
  	{
 -//		var_dump($value1, $value2);
 -		if(gettype($value1) === gettype($value2) 
 -				&& $value1 === $value2) return $this->_nullObject;
 -		return $value1;
 +		foreach($this->_states as $name => $value)
 +		{
 +			$obj = $this->_control->getViewState($name);
 +			$this->_existingState[$name] = is_object($obj) ? clone($obj) : $obj;
 +		}
  	}
 -	public function respondToChanges()
 +	/**
 +	 * @array list of viewstate and the changed data.
 +	 */
 +	protected function getChanges()
  	{
 -		foreach($this->getChanges() as $property => $value)
 +		$changes = array();
 +		foreach($this->_states as $name => $details)
  		{
 -			$this->{'update'.$property}($value);
 +			$new = $this->_control->getViewState($name);
 +			$old = $this->_existingState[$name];
 +			if($new !== $old)
 +			{
 +				$diff = new $details[0]($new, $old, $this->_nullObject);
 +				if(($change = $diff->getDifference()) !== $this->_nullObject)
 +					$changes[] = array($details[1],array($change));
 +			}
  		}
 +		return $changes;
 +	}
 +		
 +	/**
 +	 * For each of the changes call the corresponding change handlers.
 +	 */
 +	public function respondToChanges()
 +	{
 +		foreach($this->getChanges() as $change)
 +			call_user_func_array($change[0], $change[1]);
  	}
 +	/**
 +	 * @return TCallbackClientScript callback client scripting
 +	 */
  	protected function client()
  	{
  		return $this->_control->getPage()->getCallbackClient();
  	}
 +	/**
 +	 * Updates the tooltip.
 +	 * @param string new tooltip
 +	 */
  	protected function updateToolTip($value)
  	{
  		$this->client()->setAttribute($this->_control, 'title', $value); 
  	}
 +	/**
 +	 * Updates the tab index.
 +	 * @param integer tab index
 +	 */
 +	protected function updateTabIndex($value)
 +	{
 +		$this->client()->setAttribute($this->_control, 'tabindex', $value); 
 +	}
 +	
 +	/**
 +	 * Updates the modifier access key
 +	 * @param string access key
 +	 */
 +	protected function updateAccessKey($value)
 +	{
 +		$this->client()->setAttribute($this->_control, 'accesskey', $value); 
 +	}
 +	
 +	/**
 +	 * Hides or shows the control on the client-side. The control must be 
 +	 * already rendered on the client-side.
 +	 * @param boolean true to show the control, false to hide.
 +	 */
  	protected function updateVisible($visible)
  	{
 -		var_dump($visible);
  		if($visible === false)
  			$this->client()->hide($this->_control);
  		else
  			$this->client()->show($this->_control);
  	}
 +	/**
 +	 * Enables or Disables the control on the client-side.
 +	 * @param boolean true to enable the control, false to disable.
 +	 */
  	protected function updateEnabled($enable)
  	{
  		$this->client()->setAttribute($this->_control, 'disabled', $enable===false);
  	}
 +	/**
 +	 * Updates the CSS style on the control on the client-side.
 +	 * @param array list of new CSS style declarations.
 +	 */
  	protected function updateStyle($style)
  	{
 -		var_dump($style);	
 +		if(!is_null($style['CssClass']))
 +			$this->client()->setAttribute($this->_control, 'class', $style['CssClass']);
 +		if(count($style['Style']) > 0)
 +			$this->client()->setStyle($this->_control, $style['Style']);
 +	}
 +	
 +	/**
 +	 * Updates/adds a list of attributes on the control.
 +	 * @param array list of attribute name-value pairs.
 +	 */
 +	protected function updateAttributes($attributes)
 +	{
 +		foreach($attributes as $name => $value)
 +			$this->client()->setAttribute($this->_control, $name, $value);
 +	}
 +}
 +
 +/**
 + * Calculates the viewstate changes during the request.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Thu Jun 22 02:50:45 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +abstract class TViewStateDiff
 +{
 +	/**
 +	 * @var mixed updated viewstate 
 +	 */
 +	protected $_new;
 +	/**
 +	 * @var mixed viewstate value at the begining of the request.
 +	 */
 +	protected $_old;
 +	/**
 +	 * @var object null value.
 +	 */
 +	protected $_null;
 +	
 +	/**
 +	 * Constructor. 
 +	 * @param mixed updated viewstate value.
 +	 * @param mixed viewstate value at the begining of the request.
 +	 * @param object representing the null value.
 +	 */
 +	public function __construct($new, $old, $null)
 +	{
 +		$this->_new = $new;
 +		$this->_old = $old;
 +		$this->_null = $null;
 +	}
 +	
 +	/**
 +	 * @return mixed view state changes, nullObject if no difference.
 +	 */
 +	public abstract function getDifference();
 +}
 +
 +/**
 + * TScalarDiff class.
 + * 
 + * Calculate the changes to a scalar value.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Thu Jun 22 02:54:42 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TScalarDiff extends TViewStateDiff
 +{
 +	/**
 +	 * @return mixed update viewstate value.
 +	 */
 +	public function getDifference()
 +	{
 +		if(gettype($this->_new) === gettype($this->_old) 
 +			&& $this->_new === $this->_old) 
 +			return $this->_null;
 +		else
 +			return $this->_new;
 +	}
 +}
 +
 +/**
 + * TStyleDiff class.
 + * 
 + * Calculates the changes to the Style properties.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Thu Jun 22 02:55:47 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TStyleDiff extends TViewStateDiff
 +{
 +	/** 
 +	 * @param TStyle control style
 +	 * @return array all the style properties combined.
 +	 */
 +	protected function getCombinedStyle($obj)
 +	{
 +		if(!($obj instanceof TStyle)) 
 +			return array();
 +		$style = $obj->getStyleFields();
 +		$style = array_merge($style,$this->getStyleFromString($obj->getCustomStyle()));
 +		if($obj->hasFont())
 +			$style = array_merge($style, $this->getStyleFromString($obj->getFont()->toString()));
 +		return $style;
 +	}
 +	
 +	/**
 +	 * @param string CSS custom style string.
 +	 * @param array CSS style as name-value array.
 +	 */
 +	protected function getStyleFromString($string)
 +	{
 +		$style = array();
 +		if(!is_string($string)) return $style;
 +
 +		foreach(explode(';',$string) as $sub)
 +		{
 +			$arr=explode(':',$sub);
 +			if(isset($arr[1]) && trim($arr[0])!=='')
 +				$style[trim($arr[0])] = trim($arr[1]);
 +		}
 +		return $style;
 +	}
 +	
 +	/**
 +	 * @return string changes to the CSS class name.
 +	 */
 +	protected function getCssClassDiff()
 +	{
 +		if(is_null($this->_old))
 +		{
 +			return !is_null($this->_new) && $this->_new->hasCssClass() 
 +						? $this->_new->getCssClass() : null;		
 +		}
 +		else
 +		{
 +			return $this->_old->getCssClass() !== $this->_new->getCssClass() ? 
 +				$this->_new->getCssClass() : null;
 +		}
 +	}
 +	
 +	/**
 +	 * @return array list of changes to the control style.
 +	 */
 +	protected function getStyleDiff()
 +	{
 +		$diff = array_diff_assoc(
 +					$this->getCombinedStyle($this->_new),
 +					$this->getCombinedStyle($this->_old));
 +		return count($diff) > 0 ? $diff : null;
 +	}
 +
 +	/**
 +	 * @return array list of changes to the control style and CSS class name.
 +	 */
 +	public function getDifference()
 +	{
 +		if(is_null($this->_new))
 +			return $this->_null;
 +		else
 +		{
 +			$css = $this->getCssClassDiff();
 +			$style = $this->getStyleDiff();
 +			if(!is_null($css) || !is_null($style))
 +				return array('CssClass' => $css, 'Style' => $style);
 +			else
 +				$this->_null;
 +		}
 +	}
 +}
 +
 +/**
 + * TAttributesDiff class.
 + * 
 + * Calculate the changes to attributes collection.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @version $Revision: $  Thu Jun 22 02:54:42 EST 2006 $
 + * @package System.Web.UI.ActiveControls
 + * @since 3.0
 + */
 +class TMapCollectionDiff extends TViewStateDiff
 +{
 +	/**
 +	 * @return array updates to the attributes collection.
 +	 */
 +	public function getDifference()
 +	{
 +		if(is_null($this->_old))
 +		{
 +			return !is_null($this->_new) ? $this->_new->toArray() : $this->_null;			
 +		}
 +		else
 +		{
 +			$new = $this->_new->toArray();
 +			$old = $this->_old->toArray();
 +			$diff = array_diff_assoc($new, $old);
 +			return count($diff) > 0 ? $diff : $this->_null;
 +		}
  	}
  }
 diff --git a/framework/Web/UI/WebControls/TStyle.php b/framework/Web/UI/WebControls/TStyle.php index 6bc413e4..597229ea 100644 --- a/framework/Web/UI/WebControls/TStyle.php +++ b/framework/Web/UI/WebControls/TStyle.php @@ -55,6 +55,15 @@ class TStyle extends TComponent  	}
  	/**
 +	 * Need to clone the font object.
 +	 */
 +	public function __clone()
 +	{
 +		if(!is_null($this->_font))
 +			$this->_font = clone($this->_font);
 +	}
 +
 +	/**
  	 * @return string the background color of the control
  	 */
  	public function getBackColor()
 @@ -140,6 +149,14 @@ class TStyle extends TComponent  	}
  	/**
 +	 * @return boolean true if CSS is set or empty.
 +	 */
 +	public function hasCssClass()
 +	{
 +		return !is_null($this->_class);
 +	}
 +
 +	/**
  	 * @param string the name of the CSS class of the control
  	 */
  	public function setCssClass($value)
 @@ -156,6 +173,14 @@ class TStyle extends TComponent  			$this->_font=new TFont;
  		return $this->_font;
  	}
 +	
 +	/**
 +	 * @return boolean true if font is set.
 +	 */
 +	public function hasFont()
 +	{
 +		return $this->_font !== null;
 +	}
  	/**
  	 * @return string the foreground color of the control
 @@ -337,6 +362,14 @@ class TStyle extends TComponent  		if($this->_class!==null)
  			$writer->addAttribute('class',$this->_class);
  	}
 +	
 +	/**
 +	 * @return array list of style fields.
 +	 */
 +	public function getStyleFields()
 +	{
 +		return $this->_fields;
 +	}
  }
  /**
 diff --git a/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.page b/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.page new file mode 100644 index 00000000..f0f3044d --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.page @@ -0,0 +1,23 @@ +<com:TForm ID="form1"> +	<h1>Control Adapter - State Tracking Tests</h1> +	<com:TActiveButton ID="button1" Text="Test Button 1" /> +	<com:TActiveButton ID="button2" Text="Test Button 2"  +		Attributes.onclick="alert('ok')" +		Style.Font.Size="12" Style.BackColor="blue" Style.Height="2em" /> +	 +	<div style="margin:1em; padding: 0.5em; border:1px solid #ccc; text-align:center"> +		<com:TActiveButton ID="test1" Text="Enable/Disable Button 1" OnClick="change_enabled"/> +		<com:TActiveButton ID="test2" Text="Hide/Show Button 1" OnClick="change_visible"/> +		<br /> +		<com:TActiveButton ID="test3" Text="Change Button 1 Tooltip" OnClick="change_tooltip"/> +		<com:TActiveButton ID="test4" Text="Change Button 1 TabIndex" OnClick="change_tabindex"/> +		<com:TActiveButton ID="test5" Text="Change Button 1 AccessKey" OnClick="change_accesskey"/> +		<br /> +		<com:TActiveButton ID="test6" Text="Change Button 1 BGColor" OnClick="change_bgcolor1"/> +		<com:TActiveButton ID="test7" Text="Change Button 2 BGColor" OnClick="change_bgcolor2"/> +		<br /> +		<com:TActiveButton ID="test8" Text="Change Button 1 Attributes" OnClick="change_attributes1"/> +		<com:TActiveButton ID="test9" Text="Change Button 2 Attributes" OnClick="change_attributes2"/> +	</div> +	<com:TJavascriptLogger /> +</com:TForm>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.php b/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.php new file mode 100644 index 00000000..edaf0720 --- /dev/null +++ b/tests/FunctionalTests/active-controls/protected/pages/ControlAdapterTest.php @@ -0,0 +1,58 @@ +<?php + +class ControlAdapterTest extends TPage +{ +	function change_enabled() +	{ +		$this->button1->Enabled = !$this->button1->Enabled; +	} +	 +	function change_visible() +	{ +		$this->button1->Visible = !$this->button1->Visible; +	} +	 +	function change_tooltip() +	{ +		$this->button1->ToolTip = "hello world"; +	} +	 +	function change_tabindex() +	{ +		$this->button1->tabIndex = 10; +	} +	 +	function change_accesskey() +	{ +		$this->button1->accessKey = "F"; +	} + +	function change_bgcolor1() +	{ +		$this->button1->BackColor = "orange"; +		$this->button1->ForeColor="white"; +		$this->button1->Font->Bold = true; +		$this->button1->Font->Size = "2em"; +	} + +	 +	function change_bgcolor2() +	{ +		$this->button2->BackColor = "red"; +		$this->button2->ForeColor="white"; +		$this->button2->Font->Bold = true; +		$this->button2->Font->Size = 14; +	} +	 +	function change_attributes1() +	{ +		$this->button1->Attributes['onclick'] = "alert('haha!')"; +	} + +	function change_attributes2() +	{ +		$this->button2->Attributes['onclick'] = "alert('baz!')"; +	} +} + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/active-controls/tests/CallbackAdapterTestCase.php b/tests/FunctionalTests/active-controls/tests/CallbackAdapterTestCase.php new file mode 100644 index 00000000..3a3b292c --- /dev/null +++ b/tests/FunctionalTests/active-controls/tests/CallbackAdapterTestCase.php @@ -0,0 +1,32 @@ +<?php + +class CallbackAdapterTestCase extends SeleniumTestCase +{ +	function test() +	{ +		$this->open("active-controls/index.php?page=ControlAdapterTest"); +		$this->assertTextPresent('Control Adapter - State Tracking Tests'); +		 +		$this->click('button2'); +		$this->assertAlert('ok'); +		 +		$this->click('test6'); +		$this->pause(500); +		$this->click('test7'); +		$this->pause(500);		 +		$this->click('test8'); +		$this->pause(500); +		$this->click('test9'); +		$this->pause(500); +		 +		$this->click('button1'); +		$this->assertAlert('haha!'); +		 +		$this->click('button2'); +		$this->assertAlert('ok'); +		$this->assertAlert('baz!'); +		 +	} +} + +?>
\ No newline at end of file | 
