From 2c1d9d453404ec2e3344ef477bf834a2a3c065af Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 6 Feb 2014 16:23:42 +0100 Subject: Implemented basic support for callback events for JUI controls; TJuiDraggable and TJuiDroppable examples --- .../Web/UI/JuiControls/TJuiControlAdapter.php | 133 ++++++++++++++-- framework/Web/UI/JuiControls/TJuiDraggable.php | 75 ++++++++- framework/Web/UI/JuiControls/TJuiDroppable.php | 167 ++++++++++++--------- framework/Web/UI/JuiControls/TJuiResizable.php | 23 ++- framework/Web/UI/JuiControls/TJuiSelectable.php | 19 ++- framework/Web/UI/JuiControls/TJuiSortable.php | 19 ++- 6 files changed, 327 insertions(+), 109 deletions(-) (limited to 'framework/Web/UI') diff --git a/framework/Web/UI/JuiControls/TJuiControlAdapter.php b/framework/Web/UI/JuiControls/TJuiControlAdapter.php index 58ca34da..a9722a74 100644 --- a/framework/Web/UI/JuiControls/TJuiControlAdapter.php +++ b/framework/Web/UI/JuiControls/TJuiControlAdapter.php @@ -15,7 +15,8 @@ Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); * TJuiControlAdapter class * * TJuiControlAdapter is the base adapter class for controls that are - * derived from a jQuery-ui widget. + * derived from a jQuery-ui widget. It exposes convenience methods to + * publish jQuery-UI javascript and css assets. * * @author Fabio Bas * @package System.Web.UI.JuiControls @@ -93,17 +94,20 @@ interface IJuiOptions { public function getOptions(); public function getValidOptions(); + public function getValidEvents(); } /** * TJuiControlOptions interface * - * TJuiControlOptions is an helper class that can collect a series of options + * TJuiControlOptions is an helper class that can collect a list of options * for a control. The control must implement {@link IJuiOptions}. - * The options are validated againg an array of valid options provided by the control. + * The options are validated againg an array of valid options provided by the control itself. * Since component properties are case insensitive, the array of valid options is used * to ensure the option name has the correct case. * The options array can then get retrieved using {@link toArray} and applied to the jQuery-ui widget. + * In addition to the options, this class will render the needed javascript to raise a callback + * for any event for which an handler is defined in the control. * * @author Fabio Bas * @package System.Web.UI.JuiControls @@ -115,7 +119,9 @@ class TJuiControlOptions * @var TMap map of javascript options. */ private $_options; - + /** + * @var TControl parent control. + */ private $_control; public function __construct($control) @@ -128,22 +134,24 @@ class TJuiControlOptions * Sets a named options with a value. Options are used to store and retrive * named values for the javascript control. * @param string option name. - * @param mixed new value. - * @param mixed default value. - * @return mixed options value. + * @param mixed option value. + * @throws THttpException */ public function __set($name,$value) { if($this->_options===null) $this->_options=array(); + + $tmpname=strtolower($name); foreach($this->_control->getValidOptions() as $option) { - if($name == strtolower($option)) + if($tmpname == $option) { $this->_options[$option] = $value; return; } } + throw new THttpException(500,'juioptions_option_invalid',$control->ID, $name); } @@ -151,23 +159,118 @@ class TJuiControlOptions * Gets an option named value. Options are used to store and retrive * named values for the base active controls. * @param string option name. - * @param mixed default value. - * @return mixed options value. + * @return mixed options value or null if not set. */ public function __get($name) { if($this->_options===null) $this->_options=array(); - return isset($this->_options[$name]) ? $this->_options[$name] : null; + + $tmpname=strtolower($name); + if(isset($this->_options[$tmpname])) + return $this->_options[$tmpname]; + + return null; } /** - * @return TMap active control options + * @return Array of active control options */ public function toArray() { - if($this->_options===null) - $this->_options=array(); - return $this->_options; + $ret= ($this->_options===null) ? $this->_options : array(); + + foreach($this->_control->getValidEvents() as $event) + if($this->_control->hasEventHandler('on'.$event)) + $ret[$event]=new TJavaScriptLiteral("function( event, ui ) { Prado.JuiCallback(".TJavascript::encode($this->_control->getUniqueID()).", ".TJavascript::encode($event).", event, ui, this); }"); + + return $ret; + } + + /** + * Raise the specific callback event handler of the target control. + * @param mixed callback parameters + */ + public function raiseCallbackEvent($param) + { + $callbackParam=$param->CallbackParameter; + if(isset($callbackParam->event)) + { + $eventName = 'On'.ucfirst($callbackParam->event); + if($this->_control->hasEventHandler($eventName)) + { + $this->_control->$eventName( new TJuiEventParameter( + $this->_control->getResponse(), + isset($callbackParam->ui) ? $callbackParam->ui : null) + ); + } + } } } + +/** + * TJuiEventParameter class + * + * TJuiEventParameter encapsulate the parameters for callback + * events of TJui* components. + * Any parameter representing a control is identified by its + * clientside ID. + * TJuiEventParameter contains a {@link getControl} helper method + * that retrieves an existing PRADO control on che current page from its + * clientside ID as returned by the callback. + * For example, if the parameter contains a "draggable" item (as returned in + * {@link TJuiDroppable}::OnDrop event), the relative PRADO control can be + * retrieved using: + * + * $draggable = $param->getControl($param->getCallbackParameter()->draggable); + * + * + * A shortcut __get() method is implemented, too: + * + * $draggable = $param->DraggableControl; + * + * + * @author Fabio Bas + * @license http://www.pradosoft.com/license + * @package System.Web.UI.JuiControls + */ +class TJuiEventParameter extends TCallbackEventParameter +{ + /** + * getControl + * + * Compatibility method to get a control from its clientside id + * @return TControl control, or null if not found + */ + public function getControl($id) + { + $control=null; + $service=prado::getApplication()->getService(); + if ($service instanceof TPageService) + { + // Find the control + // Warning, this will not work if you have a '_' in your control Id ! + $controlId=str_replace(TControl::CLIENT_ID_SEPARATOR,TControl::ID_SEPARATOR,$id); + $control=$service->getRequestedPage()->findControl($controlId); + } + return $control; + } + + /** + * Gets a control instance named after a returned control id. + * Example: if a $param->draggable control id is returned from clientside, + * calling $param->DraggableControl will return the control instance + * @return mixed control or null if not set. + */ + public function __get($name) + { + $pos=strpos($name, 'Control',1); + $name=strtolower(substr($name, 0, $pos)); + + $cp=$this->getCallbackParameter(); + if(!isset($cp->$name) || $cp->$name=='') + return null; + + return $this->getControl($cp->$name); + } +} \ No newline at end of file diff --git a/framework/Web/UI/JuiControls/TJuiDraggable.php b/framework/Web/UI/JuiControls/TJuiDraggable.php index 19d334aa..56e6c7e7 100644 --- a/framework/Web/UI/JuiControls/TJuiDraggable.php +++ b/framework/Web/UI/JuiControls/TJuiDraggable.php @@ -10,16 +10,22 @@ */ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActivePanel'); /** * TJuiDraggable class. * + * TJuiDraggable is an extension to {@link TActivePanel} based on jQuery-UI's + * {@link http://jqueryui.com/draggable/ Draggable} interaction. + * The panel can be moved using the mouse, and eventually dropped over a + * {@link TJuiDroppable}. * * * * drag me * @@ -29,8 +35,10 @@ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); * @package System.Web.UI.JuiControls * @since 3.3 */ -class TJuiDraggable extends TActivePanel implements IJuiOptions +class TJuiDraggable extends TActivePanel implements IJuiOptions, ICallbackEventHandler { + protected $_options; + /** * Creates a new callback control, sets the adapter to * TActiveControlAdapter. If you override this class, be sure to set the @@ -48,10 +56,9 @@ class TJuiDraggable extends TActivePanel implements IJuiOptions */ public function getOptions() { - static $options; - if($options===null) - $options=new TJuiControlOptions($this); - return $options; + if($this->_options===null) + $this->_options=new TJuiControlOptions($this); + return $this->_options; } /** @@ -63,13 +70,21 @@ class TJuiDraggable extends TActivePanel implements IJuiOptions return array('addClasses', 'appendTo', 'axis', 'cancel', 'connectToSortable', 'containment', 'cursor', 'cursorAt', 'delay', 'disabled', 'distance', 'grid', 'handle', 'helper', 'iframeFix', 'opacity', 'refreshPositions', 'revert', 'revertDuration', 'scope', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'snap', 'snapMode', 'snapTolerance', 'stack', 'zIndex'); } + /** + * Array containing valid javascript events + * @return array() + */ + public function getValidEvents() + { + return array('create', 'drag', 'start', 'stop'); + } + /** * @return array list of callback options. */ protected function getPostBackOptions() { - $options = $this->getOptions()->toArray(); - return $options; + return $this->getOptions()->toArray(); } /** @@ -86,4 +101,50 @@ class TJuiDraggable extends TActivePanel implements IJuiOptions $code="jQuery('#".$this->getClientId()."').draggable(".$options.");"; $cs->registerEndScript(sprintf('%08X', crc32($code)), $code); } + + /** + * Raises callback event. This method is required by the {@link ICallbackEventHandler} + * interface. + * @param TCallbackEventParameter the parameter associated with the callback event + */ + public function raiseCallbackEvent($param) + { + $this->getOptions()->raiseCallbackEvent($param); + } + + /** + * Raises the OnCreate event + * @param object $params event parameters + */ + public function onCreate ($params) + { + $this->raiseEvent('OnCreate', $this, $params); + } + + /** + * Raises the OnDrag event + * @param object $params event parameters + */ + public function onDrag ($params) + { + $this->raiseEvent('OnDrag', $this, $params); + } + + /** + * Raises the OnStart event + * @param object $params event parameters + */ + public function onStart ($params) + { + $this->raiseEvent('OnStart', $this, $params); + } + + /** + * Raises the OnStop event + * @param object $params event parameters + */ + public function onStop ($params) + { + $this->raiseEvent('OnStop', $this, $params); + } } diff --git a/framework/Web/UI/JuiControls/TJuiDroppable.php b/framework/Web/UI/JuiControls/TJuiDroppable.php index 6c07e49c..3c79f536 100644 --- a/framework/Web/UI/JuiControls/TJuiDroppable.php +++ b/framework/Web/UI/JuiControls/TJuiDroppable.php @@ -15,6 +15,13 @@ Prado::using('System.Web.UI.ActiveControls.TActivePanel'); /** * TJuiDroppable class. * + * TJuiDroppable is an extension to {@link TActivePanel} based on jQuery-UI's + * {@link http://jqueryui.com/droppable/ Droppable} interaction. + * When a {@link TJuiDraggable} is dropped over a TJuiDroppable panel, the + * {@link onDrop OnDrop} event will be triggered. An event hanler will receive + * a {@link TJuiEventParameter} object containing a reference to the dropped control + * in the DraggableControl property. + * * * * public function drop1_ondrop($sender, $param) * { - * $draggable=$param->getDroppedControl()->ID; - * $this->drop1->Controls->clear(); - * $this->drop1->Controls->add("Dropped ".$draggable." at:
Top=".$param->getOffsetTop()." Left=".$param->getOffsetLeft()); - * // it's still an active panel, after all - * $this->drop1->render($param->NewWriter); + * $draggable=$param->DraggableControl; + * $offset=$param->getCallbackParameter()->offset; + * $target=$param->getCallbackParameter()->target->offset; + * $top=$offset->top - $target->top; + * $left=$offset->left - $target->left; + * $this->label1->Text="Dropped ".$draggable->ID." at:
Top=".$top." Left=".$left; * } *
* @@ -49,6 +57,8 @@ Prado::using('System.Web.UI.ActiveControls.TActivePanel'); */ class TJuiDroppable extends TActivePanel implements IJuiOptions, ICallbackEventHandler { + protected $_options; + /** * Creates a new callback control, sets the adapter to * TActiveControlAdapter. If you override this class, be sure to set the @@ -66,10 +76,9 @@ class TJuiDroppable extends TActivePanel implements IJuiOptions, ICallbackEventH */ public function getOptions() { - static $options; - if($options===null) - $options=new TJuiControlOptions($this); - return $options; + if($this->_options===null) + $this->_options=new TJuiControlOptions($this); + return $this->_options; } /** @@ -81,104 +90,112 @@ class TJuiDroppable extends TActivePanel implements IJuiOptions, ICallbackEventH return array('addClasses', 'appendTo', 'axis', 'cancel', 'connectToSortable', 'containment', 'cursor', 'cursorAt', 'delay', 'disabled', 'distance', 'grid', 'handle', 'helper', 'iframeFix', 'opacity', 'refreshPositions', 'revert', 'revertDuration', 'scope', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'snap', 'snapMode', 'snapTolerance', 'stack', 'zIndex'); } + /** + * Array containing valid javascript events + * @return array() + */ + public function getValidEvents() + { + return array('activate', 'create', 'deactivate', 'drop', 'out', 'over'); + } + /** * @return array list of callback options. */ protected function getPostBackOptions() { - $options = $this->getOptions()->toArray(); - $options['drop'] = new TJavaScriptLiteral("function( event, ui ) { Prado.Callback(".TJavascript::encode($this->getUniqueID()).", { 'offset' : { 'left' : ui.offset.left - $(this).offset().left, 'top' : ui.offset.top - $(this).offset().top }, 'position' : ui.position, 'draggable' : ui.draggable.get(0).id }) }"); - return $options; + return $this->getOptions()->toArray(); } /** - * Raises callback event. This method is required bu {@link ICallbackEventHandler} + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + + $writer->addAttribute('id',$this->getClientID()); + $options=TJavascript::encode($this->getPostBackOptions()); + $cs=$this->getPage()->getClientScript(); + $code="jQuery('#".$this->getClientId()."').droppable(".$options.");"; + $cs->registerEndScript(sprintf('%08X', crc32($code)), $code); + } + + /** + * Raises callback event. This method is required by the {@link ICallbackEventHandler} * interface. - * It raises the {@link onDrop OnDrop} event, then, the {@link onCallback OnCallback} event - * This method is mainly used by framework and control developers. * @param TCallbackEventParameter the parameter associated with the callback event */ public function raiseCallbackEvent($param) { - $this->onDrop($param->getCallbackParameter()); - $this->onCallback($param); + $this->getOptions()->raiseCallbackEvent($param); } /** - * Raises the onDrop event. - * The drop parameters are encapsulated into a {@link TJuiDroppableEventParameter} - * - * @param object $dropControlId + * Raises the OnActivate event + * @param object $params event parameters */ - public function onDrop ($dropParams) + public function onActivate ($params) { - $this->raiseEvent('OnDrop', $this, new TJuiDroppableEventParameter ($this->getResponse(), $dropParams)); + $this->raiseEvent('OnActivate', $this, $params); + } + /** + * Raises the OnCreate event + * @param object $params event parameters + */ + public function onCreate ($params) + { + $this->raiseEvent('OnCreate', $this, $params); } /** - * 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 + * Raises the OnDeactivate event + * @param object $params event parameters */ - public function onCallback($param) + public function onDeactivate ($params) { - $this->raiseEvent('OnCallback', $this, $param); + $this->raiseEvent('OnDeactivate', $this, $params); } /** - * Ensure that the ID attribute is rendered and registers the javascript code - * for initializing the active control. + * Raises the OnDrop event + * @param object $params event parameters */ - protected function addAttributesToRender($writer) + public function onDrop ($params) { - parent::addAttributesToRender($writer); + $this->raiseEvent('OnDrop', $this, $params); + } - $writer->addAttribute('id',$this->getClientID()); - $options=TJavascript::encode($this->getPostBackOptions()); - $cs=$this->getPage()->getClientScript(); - $code="jQuery('#".$this->getClientId()."').droppable(".$options.");"; - $cs->registerEndScript(sprintf('%08X', crc32($code)), $code); + /** + * Raises the OnOut event + * @param object $params event parameters + */ + public function OnOut ($params) + { + $this->raiseEvent('OnOut', $this, $params); } -} -/** - * TJuiDroppableEventParameter class - * - * TJuiDroppableEventParameter encapsulate the parameter - * data for OnDrop event of TJuiDroppable components - * - * @author Fabio Bas - * @license http://www.pradosoft.com/license - * @package System.Web.UI.JuiControls - */ -class TJuiDroppableEventParameter extends TCallbackEventParameter -{ - public function getDragElementId() { return $this->getCallbackParameter()->draggable; } - public function getPositionTop() { return $this->getCallbackParameter()->position->top; } - public function getPositionLeft() { return $this->getCallbackParameter()->position->left; } - public function getOffsetTop() { return $this->getCallbackParameter()->offset->top; } - public function getOffsetLeft() { return $this->getCallbackParameter()->offset->left; } + /** + * Raises the OnOver event + * @param object $params event parameters + */ + public function OnOver ($params) + { + $this->raiseEvent('OnOver', $this, $params); + } /** - * GetDroppedControl - * - * Compatibility method to get the dropped control - * @return TControl dropped control, or null if not found + * 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 getDroppedControl() - { - $control=null; - $service=prado::getApplication()->getService(); - if ($service instanceof TPageService) - { - // Find the control - // Warning, this will not work if you have a '_' in your control Id ! - $dropControlId=str_replace(TControl::CLIENT_ID_SEPARATOR,TControl::ID_SEPARATOR,$this->getCallbackParameter()->draggable); - $control=$service->getRequestedPage()->findControl($dropControlId); - } - return $control; - } -} \ No newline at end of file + + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } +} diff --git a/framework/Web/UI/JuiControls/TJuiResizable.php b/framework/Web/UI/JuiControls/TJuiResizable.php index f4553aef..e5b3b531 100644 --- a/framework/Web/UI/JuiControls/TJuiResizable.php +++ b/framework/Web/UI/JuiControls/TJuiResizable.php @@ -10,10 +10,15 @@ */ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActivePanel'); /** * TJuiResizable class. * + * TJuiResizable is an extension to {@link TActivePanel} based on jQuery-UI's + * {@link http://jqueryui.com/resizable/ Resizable} interaction. + * A small handle is shown on the bottom right corner of the panel, that permits + * the panel to be resized using the mouse. * * * _options===null) + $this->_options=new TJuiControlOptions($this); + return $this->_options; } /** @@ -66,6 +72,15 @@ class TJuiResizable extends TActivePanel implements IJuiOptions return array('alsoResize', 'animate', 'animateDuration', 'animateEasing', 'aspectRatio', 'autoHide', 'cancel', 'containment', 'delay', 'disabled', 'distance', 'ghost', 'grid', 'handles', 'helper', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth'); } + /** + * Array containing valid javascript events + * @return array() + */ + public function getValidEvents() + { + return array('create', 'resize', 'start', 'stop'); + } + /** * @return array list of callback options. */ diff --git a/framework/Web/UI/JuiControls/TJuiSelectable.php b/framework/Web/UI/JuiControls/TJuiSelectable.php index e895c1f8..968f0170 100644 --- a/framework/Web/UI/JuiControls/TJuiSelectable.php +++ b/framework/Web/UI/JuiControls/TJuiSelectable.php @@ -10,6 +10,7 @@ */ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActivePanel'); /** * TJuiSelectable class. @@ -33,6 +34,8 @@ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); */ class TJuiSelectable extends TActivePanel implements IJuiOptions, ICallbackEventHandler { + protected $_options; + /** * Creates a new callback control, sets the adapter to * TActiveControlAdapter. If you override this class, be sure to set the @@ -50,10 +53,9 @@ class TJuiSelectable extends TActivePanel implements IJuiOptions, ICallbackEvent */ public function getOptions() { - static $options; - if($options===null) - $options=new TJuiControlOptions($this); - return $options; + if($this->_options===null) + $this->_options=new TJuiControlOptions($this); + return $this->_options; } /** @@ -65,6 +67,15 @@ class TJuiSelectable extends TActivePanel implements IJuiOptions, ICallbackEvent return array('appendTo', 'autoRefresh', 'cancel', 'delay', 'disabled', 'distance', 'filter', 'tolerance'); } + /** + * Array containing valid javascript events + * @return array() + */ + public function getValidEvents() + { + return array('create', 'selected', 'selecting', 'start', 'stop', 'unselected', 'unselecting'); + } + /** * @return array list of callback options. */ diff --git a/framework/Web/UI/JuiControls/TJuiSortable.php b/framework/Web/UI/JuiControls/TJuiSortable.php index 557d62ba..e54dbefb 100644 --- a/framework/Web/UI/JuiControls/TJuiSortable.php +++ b/framework/Web/UI/JuiControls/TJuiSortable.php @@ -10,6 +10,7 @@ */ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActivePanel'); /** * TJuiSortable class. @@ -30,6 +31,8 @@ Prado::using('System.Web.UI.JuiControls.TJuiControlAdapter'); */ class TJuiSortable extends TActivePanel implements IJuiOptions, ICallbackEventHandler { + protected $_options; + /** * Creates a new callback control, sets the adapter to * TActiveControlAdapter. If you override this class, be sure to set the @@ -47,10 +50,9 @@ class TJuiSortable extends TActivePanel implements IJuiOptions, ICallbackEventHa */ public function getOptions() { - static $options; - if($options===null) - $options=new TJuiControlOptions($this); - return $options; + if($this->_options===null) + $this->_options=new TJuiControlOptions($this); + return $this->_options; } /** @@ -62,6 +64,15 @@ class TJuiSortable extends TActivePanel implements IJuiOptions, ICallbackEventHa return array('appendTo', 'axis', 'cancel', 'connectWith', 'containment', 'cursor', 'cursorAt', 'delay', 'disabled', 'distance', 'dropOnEmpty', 'forceHelperSize', 'forcePlaceholderSize', 'grid', 'handle', 'helper', 'items', 'opacity', 'placeholder', 'revert', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tolerance', 'zIndex'); } + /** + * Array containing valid javascript events + * @return array() + */ + public function getValidEvents() + { + return array('activate', 'beforeStop', 'change', 'create', 'deactivate', 'out', 'over', 'receive', 'remove', 'sort', 'start', 'stop', 'update'); + } + /** * @return array list of callback options. */ -- cgit v1.2.3