From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Thu, 12 Jul 2012 11:21:01 +0000 Subject: standardize the use of unix eol; use svn properties to enforce native eol --- framework/Web/UI/ActiveControls/TActiveButton.php | 264 +- .../Web/UI/ActiveControls/TActiveClientScript.php | 164 +- .../UI/ActiveControls/TActiveControlAdapter.php | 1150 ++--- .../UI/ActiveControls/TActiveCustomValidator.php | 530 +-- framework/Web/UI/ActiveControls/TActiveLabel.php | 2 +- .../Web/UI/ActiveControls/TActivePageAdapter.php | 802 ++-- framework/Web/UI/ActiveControls/TActivePanel.php | 200 +- .../Web/UI/ActiveControls/TActiveRatingList.php | 266 +- framework/Web/UI/ActiveControls/TActiveTextBox.php | 250 +- framework/Web/UI/ActiveControls/TAutoComplete.php | 880 ++-- .../Web/UI/ActiveControls/TBaseActiveControl.php | 784 ++-- framework/Web/UI/ActiveControls/TCallback.php | 202 +- .../UI/ActiveControls/TCallbackClientScript.php | 1410 +++--- .../Web/UI/ActiveControls/TCallbackClientSide.php | 644 +-- .../UI/ActiveControls/TCallbackEventParameter.php | 174 +- .../Web/UI/ActiveControls/TCallbackOptions.php | 106 +- .../UI/ActiveControls/TEventTriggeredCallback.php | 190 +- .../Web/UI/ActiveControls/TInPlaceTextBox.php | 580 +-- .../Web/UI/ActiveControls/TTriggeredCallback.php | 142 +- .../UI/ActiveControls/TValueTriggeredCallback.php | 240 +- framework/Web/UI/TCachePageStatePersister.php | 400 +- framework/Web/UI/TCompositeControl.php | 74 +- framework/Web/UI/TControl.php | 4790 ++++++++++---------- framework/Web/UI/TControlAdapter.php | 286 +- framework/Web/UI/TForm.php | 342 +- framework/Web/UI/THtmlWriter.php | 458 +- framework/Web/UI/TPage.php | 2682 +++++------ framework/Web/UI/TPageStatePersister.php | 140 +- framework/Web/UI/TSessionPageStatePersister.php | 260 +- framework/Web/UI/TTemplateControl.php | 484 +- framework/Web/UI/TTemplateManager.php | 2150 ++++----- framework/Web/UI/TThemeManager.php | 1034 ++--- framework/Web/UI/WebControls/TAccordion.php | 1488 +++--- framework/Web/UI/WebControls/TBaseDataList.php | 378 +- framework/Web/UI/WebControls/TBoundColumn.php | 498 +- framework/Web/UI/WebControls/TBulletedList.php | 982 ++-- framework/Web/UI/WebControls/TButton.php | 734 +-- framework/Web/UI/WebControls/TButtonColumn.php | 554 +-- framework/Web/UI/WebControls/TCaptcha.php | 990 ++-- framework/Web/UI/WebControls/TCaptchaValidator.php | 254 +- framework/Web/UI/WebControls/TCheckBox.php | 1026 ++--- framework/Web/UI/WebControls/TCheckBoxColumn.php | 244 +- framework/Web/UI/WebControls/TCheckBoxList.php | 998 ++-- framework/Web/UI/WebControls/TColorPicker.php | 580 +-- framework/Web/UI/WebControls/TCompareValidator.php | 528 +-- framework/Web/UI/WebControls/TConditional.php | 284 +- framework/Web/UI/WebControls/TContent.php | 92 +- .../Web/UI/WebControls/TContentPlaceHolder.php | 94 +- framework/Web/UI/WebControls/TCustomValidator.php | 414 +- framework/Web/UI/WebControls/TDataBoundControl.php | 1174 ++--- framework/Web/UI/WebControls/TDataGrid.php | 4516 +++++++++--------- framework/Web/UI/WebControls/TDataGridColumn.php | 1134 ++--- .../Web/UI/WebControls/TDataGridItemRenderer.php | 58 +- .../Web/UI/WebControls/TDataGridPagerStyle.php | 510 +-- framework/Web/UI/WebControls/TDataList.php | 3530 +++++++-------- .../Web/UI/WebControls/TDataListItemRenderer.php | 342 +- framework/Web/UI/WebControls/TDataRenderer.php | 102 +- .../Web/UI/WebControls/TDataSourceControl.php | 234 +- framework/Web/UI/WebControls/TDataSourceView.php | 410 +- .../Web/UI/WebControls/TDataTypeValidator.php | 280 +- framework/Web/UI/WebControls/TDatePicker.php | 1986 ++++---- framework/Web/UI/WebControls/TDropDownList.php | 308 +- .../Web/UI/WebControls/TDropDownListColumn.php | 640 +-- .../Web/UI/WebControls/TEditCommandColumn.php | 528 +-- .../Web/UI/WebControls/TEmailAddressValidator.php | 192 +- framework/Web/UI/WebControls/TExpression.php | 122 +- framework/Web/UI/WebControls/TFileUpload.php | 562 +-- framework/Web/UI/WebControls/TFlushOutput.php | 170 +- framework/Web/UI/WebControls/TFont.php | 634 +-- framework/Web/UI/WebControls/THead.php | 754 +-- framework/Web/UI/WebControls/THiddenField.php | 410 +- framework/Web/UI/WebControls/THtmlElement.php | 136 +- framework/Web/UI/WebControls/THyperLink.php | 454 +- framework/Web/UI/WebControls/THyperLinkColumn.php | 546 +-- framework/Web/UI/WebControls/TImage.php | 312 +- framework/Web/UI/WebControls/TImageButton.php | 882 ++-- framework/Web/UI/WebControls/TImageMap.php | 1672 +++---- framework/Web/UI/WebControls/TItemDataRenderer.php | 164 +- framework/Web/UI/WebControls/TJavascriptLogger.php | 186 +- framework/Web/UI/WebControls/TKeyboard.php | 378 +- framework/Web/UI/WebControls/TLabel.php | 306 +- framework/Web/UI/WebControls/TLinkButton.php | 666 +-- framework/Web/UI/WebControls/TListBox.php | 486 +- framework/Web/UI/WebControls/TListControl.php | 1846 ++++---- .../Web/UI/WebControls/TListControlValidator.php | 448 +- framework/Web/UI/WebControls/TListItem.php | 366 +- framework/Web/UI/WebControls/TLiteral.php | 222 +- framework/Web/UI/WebControls/TLiteralColumn.php | 306 +- framework/Web/UI/WebControls/TMarkdown.php | 148 +- framework/Web/UI/WebControls/TMultiView.php | 756 +-- framework/Web/UI/WebControls/TOutputCache.php | 1240 ++--- framework/Web/UI/WebControls/TPager.php | 1582 +++---- framework/Web/UI/WebControls/TPanel.php | 492 +- framework/Web/UI/WebControls/TPanelStyle.php | 554 +-- framework/Web/UI/WebControls/TPlaceHolder.php | 54 +- framework/Web/UI/WebControls/TRadioButton.php | 638 +-- framework/Web/UI/WebControls/TRangeValidator.php | 716 +-- framework/Web/UI/WebControls/TRatingList.php | 718 +-- framework/Web/UI/WebControls/TReCaptcha.php | 464 +- .../Web/UI/WebControls/TReCaptchaValidator.php | 244 +- .../UI/WebControls/TRegularExpressionValidator.php | 288 +- framework/Web/UI/WebControls/TRepeatInfo.php | 1118 ++--- framework/Web/UI/WebControls/TRepeater.php | 2048 ++++----- .../Web/UI/WebControls/TRepeaterItemRenderer.php | 98 +- .../Web/UI/WebControls/TRequiredFieldValidator.php | 274 +- framework/Web/UI/WebControls/TSafeHtml.php | 170 +- framework/Web/UI/WebControls/TSlider.php | 1148 ++--- framework/Web/UI/WebControls/TStatements.php | 124 +- framework/Web/UI/WebControls/TStyle.php | 1786 ++++---- framework/Web/UI/WebControls/TTable.php | 818 ++-- framework/Web/UI/WebControls/TTableCell.php | 442 +- framework/Web/UI/WebControls/TTableFooterRow.php | 92 +- framework/Web/UI/WebControls/TTableHeaderCell.php | 246 +- framework/Web/UI/WebControls/TTableHeaderRow.php | 92 +- framework/Web/UI/WebControls/TTableRow.php | 414 +- framework/Web/UI/WebControls/TTemplateColumn.php | 510 +-- framework/Web/UI/WebControls/TTextBox.php | 1304 +++--- framework/Web/UI/WebControls/TTextProcessor.php | 170 +- .../Web/UI/WebControls/TValidationSummary.php | 1072 ++--- .../Web/UI/WebControls/TWebControlAdapter.php | 140 +- framework/Web/UI/WebControls/TWizard.php | 4290 +++++++++--------- .../WebControls/TWizardNavigationButtonStyle.php | 308 +- framework/Web/UI/WebControls/assets/captcha.php | 448 +- 123 files changed, 42598 insertions(+), 42598 deletions(-) (limited to 'framework/Web/UI') diff --git a/framework/Web/UI/ActiveControls/TActiveButton.php b/framework/Web/UI/ActiveControls/TActiveButton.php index aaa10168..15b33692 100644 --- a/framework/Web/UI/ActiveControls/TActiveButton.php +++ b/framework/Web/UI/ActiveControls/TActiveButton.php @@ -1,132 +1,132 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load active control adapter. - */ -Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); - -/** - * TActiveButton is the active control counter part to TButton. - * - * When a TActiveButton is clicked, rather than a normal post back request a - * callback request is initiated. - * - * The {@link onCallback OnCallback} event is raised during a callback request - * and it is raise after the {@link onClick OnClick} event. - * - * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate} - * property is true, changing the {@link setText Text} property during callback request - * will update the button's caption upon callback response completion. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveButton extends TButton implements ICallbackEventHandler, IActiveControl -{ - /** - * Creates a new callback control, sets the adapter to - * TActiveControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, by calling this constructor. - */ - public function __construct() - { - parent::__construct(); - $this->setAdapter(new TActiveControlAdapter($this)); - } - - /** - * @return TBaseActiveCallbackControl standard callback control options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * @return TCallbackClientSide client side request options. - */ - public function getClientSide() - { - return $this->getAdapter()->getBaseActiveControl()->getClientSide(); - } - - /** - * 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 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->raisePostBackEvent($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); - } - - /** - * Updates the button text on the client-side if the - * {@link setEnableUpdate EnableUpdate} property is set to true. - * @param string caption of the button - */ - public function setText($value) - { - parent::setText($value); - if($this->getActiveControl()->canUpdateClientSide()) - $this->getPage()->getCallbackClient()->setAttribute($this, 'value', $value); - } - - /** - * Override parent implementation, no javascript is rendered here instead - * the javascript required for active control is registered in {@link addAttributesToRender}. - */ - protected function renderClientControlScript($writer) - { - } - - /** - * 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()); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); - } - - /** - * @return string corresponding javascript class name for this TActiveButton. - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TActiveButton'; - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveButton is the active control counter part to TButton. + * + * When a TActiveButton is clicked, rather than a normal post back request a + * callback request is initiated. + * + * The {@link onCallback OnCallback} event is raised during a callback request + * and it is raise after the {@link onClick OnClick} event. + * + * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate} + * property is true, changing the {@link setText Text} property during callback request + * will update the button's caption upon callback response completion. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveButton extends TButton implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * 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 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->raisePostBackEvent($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); + } + + /** + * Updates the button text on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param string caption of the button + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'value', $value); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * 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()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * @return string corresponding javascript class name for this TActiveButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveButton'; + } +} + diff --git a/framework/Web/UI/ActiveControls/TActiveClientScript.php b/framework/Web/UI/ActiveControls/TActiveClientScript.php index 423918d9..2e58b6b0 100755 --- a/framework/Web/UI/ActiveControls/TActiveClientScript.php +++ b/framework/Web/UI/ActiveControls/TActiveClientScript.php @@ -1,82 +1,82 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ - * @package System.Web.UI.ActiveControls - */ - -/** - * TActiveClientScript class - * - * This is the active counterpart of the {@link TClientScript} class. - * - * TActiveClientScript has the ability to render itself on ajax - * callbacks. This means that every variable or function declared in javascript - * code will be available to the page. - * - * Beware that when rendered on normal (postback) or ajax callbacks, some - * javascript code won't behave in the same way. - * When rendered as part of a normal/postback response, scripts will execute instantly - * where they are in the page and in a synchronous fashion. - * Instead, when they are rendered as part of a callback response, - * they will be executed when all DOM modifications are complete and any dynamic - * script file includes are loaded, out-of-band and practically all blocks at once, - * regardless of where they actually occour in the original template/markup code. - * This can potentially hurt compatibility and graceful fallback. - * - * @author Wei Zhuo - * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ - * @package System.Web.UI.ActiveControls - * @since 3.2 - */ - -class TActiveClientScript extends TClientScript -{ - /** - * Renders the custom script file. - * @param THtmLWriter the renderer - */ - protected function renderCustomScriptFile($writer) - { - if(($scriptUrl = $this->getScriptUrl())!=='') - { - if($this->getPage()->getIsCallback()) - { - $cs = $this->getPage()->getClientScript(); - $uniqueid=$this->ClientID.'_custom'; - if(!$cs->isScriptFileRegistered($uniqueid)) - $cs->registerScriptFile($uniqueid, $scriptUrl); - } else { - $writer->write("\n"); - } - } - } - - /** - * Registers the body content as javascript. - * @param THtmlWriter the renderer - */ - protected function renderCustomScript($writer) - { - if($this->getHasControls()) - { - if($this->getPage()->getIsCallback()) - { - $extWriter= $this->getPage()->getResponse()->createHtmlWriter(); - $extWriter->write("/*renderChildren($extWriter); - $extWriter->write("\n/*]]>*/"); - $this->getPage()->getCallbackClient()->appendScriptBlock($extWriter); - } else { - $writer->write("\n"); - } - } - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveClientScript class + * + * This is the active counterpart of the {@link TClientScript} class. + * + * TActiveClientScript has the ability to render itself on ajax + * callbacks. This means that every variable or function declared in javascript + * code will be available to the page. + * + * Beware that when rendered on normal (postback) or ajax callbacks, some + * javascript code won't behave in the same way. + * When rendered as part of a normal/postback response, scripts will execute instantly + * where they are in the page and in a synchronous fashion. + * Instead, when they are rendered as part of a callback response, + * they will be executed when all DOM modifications are complete and any dynamic + * script file includes are loaded, out-of-band and practically all blocks at once, + * regardless of where they actually occour in the original template/markup code. + * This can potentially hurt compatibility and graceful fallback. + * + * @author Wei Zhuo + * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.2 + */ + +class TActiveClientScript extends TClientScript +{ + /** + * Renders the custom script file. + * @param THtmLWriter the renderer + */ + protected function renderCustomScriptFile($writer) + { + if(($scriptUrl = $this->getScriptUrl())!=='') + { + if($this->getPage()->getIsCallback()) + { + $cs = $this->getPage()->getClientScript(); + $uniqueid=$this->ClientID.'_custom'; + if(!$cs->isScriptFileRegistered($uniqueid)) + $cs->registerScriptFile($uniqueid, $scriptUrl); + } else { + $writer->write("\n"); + } + } + } + + /** + * Registers the body content as javascript. + * @param THtmlWriter the renderer + */ + protected function renderCustomScript($writer) + { + if($this->getHasControls()) + { + if($this->getPage()->getIsCallback()) + { + $extWriter= $this->getPage()->getResponse()->createHtmlWriter(); + $extWriter->write("/*renderChildren($extWriter); + $extWriter->write("\n/*]]>*/"); + $this->getPage()->getCallbackClient()->appendScriptBlock($extWriter); + } else { + $writer->write("\n"); + } + } + } +} diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index 8a0c8aec..7e7a82fe 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -1,575 +1,575 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/* - * 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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveControlAdapter extends TControlAdapter -{ - /** - * @var string base active control class name. - */ - private $_activeControlType; - /** - * @var TBaseActiveControl base active control instance. - */ - private $_baseActiveControl; - /** - * @var TCallbackPageStateTracker view state tracker. - */ - private $_stateTracker; - - /** - * 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->setBaseControlClass($baseCallbackClass); - } - - /** - * @param string base active control instance - */ - protected function setBaseControlClass($type) - { - if($type===null) - { - if($this->getControl() instanceof ICallbackEventHandler) - $this->_activeControlType = 'TBaseActiveCallbackControl'; - else - $this->_activeControlType = 'TBaseActiveControl'; - } - else - $this->_activeControlType = $type; - } - - /** - * Publish the ajax script - */ - public function onPreRender($param) - { - parent::onPreRender($param); - } - - /** - * Renders the callback client scripts. - */ - public function render($writer) - { - $this->getPage()->getClientScript()->registerPradoScript('ajax'); - $this->renderCallbackClientScripts(); - if($this->_control->getVisible(false)) - { - parent::render($writer); - } else { - $writer->write("_control->getClientID()."\" style=\"display:none\">"); - } - } - - /** - * Register the callback clientscripts and sets the post loader IDs. - */ - protected function renderCallbackClientScripts() - { - $cs = $this->getPage()->getClientScript(); - $key = 'Prado.CallbackRequest.addPostLoaders'; - if(!$cs->isEndScriptRegistered($key)) - { - $data = $this->getPage()->getPostDataLoaders(); - if(count($data) > 0) - { - $options = TJavaScript::encode($data,false); - $script = "Prado.CallbackRequest.addPostLoaders({$options});"; - $cs->registerEndScript($key, $script); - } - } - } - - /** - * @param TBaseActiveControl change base active control - */ - public function setBaseActiveControl($control) - { - $this->_baseActiveControl=$control; - } - - /** - * @return TBaseActiveControl Common active control options. - */ - public function getBaseActiveControl() - { - if($this->_baseActiveControl===null) - { - $type = $this->_activeControlType; - $this->_baseActiveControl = new $type($this->getControl()); - } - return $this->_baseActiveControl; - } - - /** - * @return boolean true if the viewstate needs to be tracked. - */ - protected function getIsTrackingPageState() - { - if($this->getPage()->getIsCallback()) - { - $target = $this->getPage()->getCallbackEventTarget(); - if($target instanceof ICallbackEventHandler) - { - $client = $target->getActiveControl()->getClientSide(); - return $client->getEnablePageStateUpdate(); - } - } - return false; - } - - /** - * Starts viewstate tracking if necessary after when controls has been loaded - */ - public function onLoad($param) - { - if($this->getIsTrackingPageState()) - { - $this->_stateTracker = new TCallbackPageStateTracker($this->getControl()); - $this->_stateTracker->trackChanges(); - } - parent::onLoad($param); - } - - /** - * Saves additional persistent control state. Respond to viewstate changes - * if necessary. - */ - public function saveState() - { - if(($this->_stateTracker!==null) - && $this->getControl()->getActiveControl()->canUpdateClientSide(true)) - { - $this->_stateTracker->respondToChanges(); - } - parent::saveState(); - } - - /** - * @return TCallbackPageStateTracker state tracker. - */ - public function getStateTracker() - { - return $this->_stateTracker; - } -} - -/** - * TCallbackPageStateTracker class. - * - * Tracking changes to the page state during callback. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackPageStateTracker -{ - /** - * @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(); - } - - /** - * 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() - { - $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')); - } - - /** - * @return TMap list of viewstates to track. - */ - protected function getStatesToTrack() - { - return $this->_states; - } - - /** - * Start tracking view state changes. The clone function on objects are called - * for those viewstate having an object as value. - */ - public function trackChanges() - { - foreach($this->_states as $name => $value) - { - $obj = $this->_control->getViewState($name); - $this->_existingState[$name] = is_object($obj) ? clone($obj) : $obj; - } - } - - /** - * @return array list of viewstate and the changed data. - */ - protected function getChanges() - { - $changes = array(); - foreach($this->_states as $name => $details) - { - $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) - { - if($visible === false) - $this->client()->replaceContent($this->_control,"_control->getClientID()."\" style=\"display:none\" >"); - else - $this->client()->replaceContent($this->_control,$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) - { - if($style['CssClass']!==null) - $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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -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($this->_old===null) - { - return ($this->_new!==null) && $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($this->_new===null) - return $this->_null; - else - { - $css = $this->getCssClassDiff(); - $style = $this->getStyleDiff(); - if(($css!==null) || ($style!==null)) - return array('CssClass' => $css, 'Style' => $style); - else - $this->_null; - } - } -} - -/** - * TAttributesDiff class. - * - * Calculate the changes to attributes collection. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TMapCollectionDiff extends TViewStateDiff -{ - /** - * @return array updates to the attributes collection. - */ - public function getDifference() - { - if($this->_old===null) - { - return ($this->_new!==null) ? $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; - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/* + * 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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveControlAdapter extends TControlAdapter +{ + /** + * @var string base active control class name. + */ + private $_activeControlType; + /** + * @var TBaseActiveControl base active control instance. + */ + private $_baseActiveControl; + /** + * @var TCallbackPageStateTracker view state tracker. + */ + private $_stateTracker; + + /** + * 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->setBaseControlClass($baseCallbackClass); + } + + /** + * @param string base active control instance + */ + protected function setBaseControlClass($type) + { + if($type===null) + { + if($this->getControl() instanceof ICallbackEventHandler) + $this->_activeControlType = 'TBaseActiveCallbackControl'; + else + $this->_activeControlType = 'TBaseActiveControl'; + } + else + $this->_activeControlType = $type; + } + + /** + * Publish the ajax script + */ + public function onPreRender($param) + { + parent::onPreRender($param); + } + + /** + * Renders the callback client scripts. + */ + public function render($writer) + { + $this->getPage()->getClientScript()->registerPradoScript('ajax'); + $this->renderCallbackClientScripts(); + if($this->_control->getVisible(false)) + { + parent::render($writer); + } else { + $writer->write("_control->getClientID()."\" style=\"display:none\">"); + } + } + + /** + * Register the callback clientscripts and sets the post loader IDs. + */ + protected function renderCallbackClientScripts() + { + $cs = $this->getPage()->getClientScript(); + $key = 'Prado.CallbackRequest.addPostLoaders'; + if(!$cs->isEndScriptRegistered($key)) + { + $data = $this->getPage()->getPostDataLoaders(); + if(count($data) > 0) + { + $options = TJavaScript::encode($data,false); + $script = "Prado.CallbackRequest.addPostLoaders({$options});"; + $cs->registerEndScript($key, $script); + } + } + } + + /** + * @param TBaseActiveControl change base active control + */ + public function setBaseActiveControl($control) + { + $this->_baseActiveControl=$control; + } + + /** + * @return TBaseActiveControl Common active control options. + */ + public function getBaseActiveControl() + { + if($this->_baseActiveControl===null) + { + $type = $this->_activeControlType; + $this->_baseActiveControl = new $type($this->getControl()); + } + return $this->_baseActiveControl; + } + + /** + * @return boolean true if the viewstate needs to be tracked. + */ + protected function getIsTrackingPageState() + { + if($this->getPage()->getIsCallback()) + { + $target = $this->getPage()->getCallbackEventTarget(); + if($target instanceof ICallbackEventHandler) + { + $client = $target->getActiveControl()->getClientSide(); + return $client->getEnablePageStateUpdate(); + } + } + return false; + } + + /** + * Starts viewstate tracking if necessary after when controls has been loaded + */ + public function onLoad($param) + { + if($this->getIsTrackingPageState()) + { + $this->_stateTracker = new TCallbackPageStateTracker($this->getControl()); + $this->_stateTracker->trackChanges(); + } + parent::onLoad($param); + } + + /** + * Saves additional persistent control state. Respond to viewstate changes + * if necessary. + */ + public function saveState() + { + if(($this->_stateTracker!==null) + && $this->getControl()->getActiveControl()->canUpdateClientSide(true)) + { + $this->_stateTracker->respondToChanges(); + } + parent::saveState(); + } + + /** + * @return TCallbackPageStateTracker state tracker. + */ + public function getStateTracker() + { + return $this->_stateTracker; + } +} + +/** + * TCallbackPageStateTracker class. + * + * Tracking changes to the page state during callback. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackPageStateTracker +{ + /** + * @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(); + } + + /** + * 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() + { + $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')); + } + + /** + * @return TMap list of viewstates to track. + */ + protected function getStatesToTrack() + { + return $this->_states; + } + + /** + * Start tracking view state changes. The clone function on objects are called + * for those viewstate having an object as value. + */ + public function trackChanges() + { + foreach($this->_states as $name => $value) + { + $obj = $this->_control->getViewState($name); + $this->_existingState[$name] = is_object($obj) ? clone($obj) : $obj; + } + } + + /** + * @return array list of viewstate and the changed data. + */ + protected function getChanges() + { + $changes = array(); + foreach($this->_states as $name => $details) + { + $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) + { + if($visible === false) + $this->client()->replaceContent($this->_control,"_control->getClientID()."\" style=\"display:none\" >"); + else + $this->client()->replaceContent($this->_control,$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) + { + if($style['CssClass']!==null) + $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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +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($this->_old===null) + { + return ($this->_new!==null) && $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($this->_new===null) + return $this->_null; + else + { + $css = $this->getCssClassDiff(); + $style = $this->getStyleDiff(); + if(($css!==null) || ($style!==null)) + return array('CssClass' => $css, 'Style' => $style); + else + $this->_null; + } + } +} + +/** + * TAttributesDiff class. + * + * Calculate the changes to attributes collection. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TMapCollectionDiff extends TViewStateDiff +{ + /** + * @return array updates to the attributes collection. + */ + public function getDifference() + { + if($this->_old===null) + { + return ($this->_new!==null) ? $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/ActiveControls/TActiveCustomValidator.php b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php index 9e09eb88..df8f45d9 100644 --- a/framework/Web/UI/ActiveControls/TActiveCustomValidator.php +++ b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php @@ -1,265 +1,265 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); - -/** - * TActiveCustomValidator Class - * - * Performs custom validation using only server-side {@link onServerValidate onServerValidate} - * validation event. The client-side uses callbacks to raise - * the {@link onServerValidate onServerValidate} event. - * - * Beware that the {@link onServerValidate onServerValidate} may be - * raised when the control to validate on the client side - * changes value, that is, the server validation may be called many times. - * - * After the callback or postback, the {@link onServerValidate onServerValidate} - * is raised once more. The {@link getIsCallback IsCallback} property - * will be true when validation is made during a callback request. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveCustomValidator extends TCustomValidator - implements ICallbackEventHandler, IActiveControl -{ - /** - * @var boolean true if validation is made during a callback request. - */ - private $_isCallback = false; - - /** - * @return boolean true if validation is made during a callback request. - */ - public function getIsCallback() - { - return $this->_isCallback; - } - - /** - * Creates a new callback control, sets the adapter to - * TActiveControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, by calling this constructor. - */ - public function __construct() - { - parent::__construct(); - $this->setAdapter(new TActiveControlAdapter($this)); - $this->getActiveControl()->setClientSide(new TActiveCustomValidatorClientSide); - } - - /** - * @return TBaseActiveCallbackControl standard callback control options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * @return TCallbackClientSide client side request options. - */ - public function getClientSide() - { - return $this->getAdapter()->getBaseActiveControl()->getClientSide(); - } - - /** - * Client validation function is NOT supported. - */ - public function setClientValidationFunction($value) - { - throw new TNotSupportedException('tactivecustomvalidator_clientfunction_unsupported', - get_class($this)); - } - - /** - * Raises the callback event. This method is required by {@link - * ICallbackEventHandler} interface. The {@link onServerValidate - * OnServerValidate} event is raised 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->_isCallback = true; - $result = $this->onServerValidate($param->getCallbackParameter()); - $param->setResponseData($result); - $this->onCallback($param); - } - - /** - * @param boolean whether the value is valid; this method will trigger a clientside update if needed - */ - public function setIsValid($value) - { - parent::setIsValid($value); - if($this->getActiveControl()->canUpdateClientSide()) - { - $client = $this->getPage()->getCallbackClient(); - $func = 'Prado.Validation.updateActiveCustomValidator'; - $client->callClientFunction($func, array($this, $value)); - } - } - - /** - * 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); - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options=TBaseValidator::getClientScriptOptions(); - $options['EventTarget'] = $this->getUniqueID(); - return $options; - } - - /** - * Sets the text for the error message. Updates client-side erorr message. - * @param string the error message - */ - public function setErrorMessage($value) - { - parent::setErrorMessage($value); - if($this->getActiveControl()->canUpdateClientSide()) - { - $client = $this->getPage()->getCallbackClient(); - $func = 'Prado.Validation.setErrorMessage'; - $client->callClientFunction($func, array($this, $value)); - } - } - - - /** - * It's mandatory for the EnableClientScript to be activated or the TActiveCustomValidator won't work. - * @return boolean whether client-side validation is enabled. - */ - public function getEnableClientScript() - { - return true; - } - - /** - * Ensure that the ID attribute is rendered and registers the javascript code - * for initializing the active control. - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - TBaseValidator::registerClientScriptValidator(); - } - - /** - * @return string corresponding javascript class name for this this. - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TActiveCustomValidator'; - } -} - -/** - * Custom Validator callback client side options class. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveCustomValidatorClientSide extends TCallbackClientSide -{ - /** - * @return string javascript code for client-side OnValidate event. - */ - public function getOnValidate() - { - return $this->getOption('OnValidate'); - } - - /** - * Client-side OnValidate validator event is raise before the validators - * validation functions are called. - * @param string javascript code for client-side OnValidate event. - */ - public function setOnValidate($javascript) - { - $this->setFunction('OnValidate', $javascript); - } - - /** - * Client-side OnSuccess event is raise after validation is successfull. - * This will override the default client-side validator behaviour. - * @param string javascript code for client-side OnSuccess event. - */ - public function setOnValidationSuccess($javascript) - { - $this->setFunction('OnValidationSuccess', $javascript); - } - - /** - * @return string javascript code for client-side OnSuccess event. - */ - public function getOnValidationSuccess() - { - return $this->getOption('OnValidationSuccess'); - } - - /** - * Client-side OnError event is raised after validation failure. - * This will override the default client-side validator behaviour. - * @param string javascript code for client-side OnError event. - */ - public function setOnValidationError($javascript) - { - $this->setFunction('OnValidationError', $javascript); - } - - /** - * @return string javascript code for client-side OnError event. - */ - public function getOnValidationError() - { - return $this->getOption('OnValidationError'); - } - - /** - * @param boolean true to revalidate when the control to validate changes value. - */ - public function setObserveChanges($value) - { - $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value)); - } - - /** - * @return boolean true to observe changes. - */ - public function getObserveChanges() - { - $changes = $this->getOption('ObserveChanges'); - return ($changes===null) ? true : $changes; - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); + +/** + * TActiveCustomValidator Class + * + * Performs custom validation using only server-side {@link onServerValidate onServerValidate} + * validation event. The client-side uses callbacks to raise + * the {@link onServerValidate onServerValidate} event. + * + * Beware that the {@link onServerValidate onServerValidate} may be + * raised when the control to validate on the client side + * changes value, that is, the server validation may be called many times. + * + * After the callback or postback, the {@link onServerValidate onServerValidate} + * is raised once more. The {@link getIsCallback IsCallback} property + * will be true when validation is made during a callback request. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCustomValidator extends TCustomValidator + implements ICallbackEventHandler, IActiveControl +{ + /** + * @var boolean true if validation is made during a callback request. + */ + private $_isCallback = false; + + /** + * @return boolean true if validation is made during a callback request. + */ + public function getIsCallback() + { + return $this->_isCallback; + } + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + $this->getActiveControl()->setClientSide(new TActiveCustomValidatorClientSide); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Client validation function is NOT supported. + */ + public function setClientValidationFunction($value) + { + throw new TNotSupportedException('tactivecustomvalidator_clientfunction_unsupported', + get_class($this)); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. The {@link onServerValidate + * OnServerValidate} event is raised 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->_isCallback = true; + $result = $this->onServerValidate($param->getCallbackParameter()); + $param->setResponseData($result); + $this->onCallback($param); + } + + /** + * @param boolean whether the value is valid; this method will trigger a clientside update if needed + */ + public function setIsValid($value) + { + parent::setIsValid($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $func = 'Prado.Validation.updateActiveCustomValidator'; + $client->callClientFunction($func, array($this, $value)); + } + } + + /** + * 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); + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options=TBaseValidator::getClientScriptOptions(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Sets the text for the error message. Updates client-side erorr message. + * @param string the error message + */ + public function setErrorMessage($value) + { + parent::setErrorMessage($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $func = 'Prado.Validation.setErrorMessage'; + $client->callClientFunction($func, array($this, $value)); + } + } + + + /** + * It's mandatory for the EnableClientScript to be activated or the TActiveCustomValidator won't work. + * @return boolean whether client-side validation is enabled. + */ + public function getEnableClientScript() + { + return true; + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + TBaseValidator::registerClientScriptValidator(); + } + + /** + * @return string corresponding javascript class name for this this. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveCustomValidator'; + } +} + +/** + * Custom Validator callback client side options class. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCustomValidatorClientSide extends TCallbackClientSide +{ + /** + * @return string javascript code for client-side OnValidate event. + */ + public function getOnValidate() + { + return $this->getOption('OnValidate'); + } + + /** + * Client-side OnValidate validator event is raise before the validators + * validation functions are called. + * @param string javascript code for client-side OnValidate event. + */ + public function setOnValidate($javascript) + { + $this->setFunction('OnValidate', $javascript); + } + + /** + * Client-side OnSuccess event is raise after validation is successfull. + * This will override the default client-side validator behaviour. + * @param string javascript code for client-side OnSuccess event. + */ + public function setOnValidationSuccess($javascript) + { + $this->setFunction('OnValidationSuccess', $javascript); + } + + /** + * @return string javascript code for client-side OnSuccess event. + */ + public function getOnValidationSuccess() + { + return $this->getOption('OnValidationSuccess'); + } + + /** + * Client-side OnError event is raised after validation failure. + * This will override the default client-side validator behaviour. + * @param string javascript code for client-side OnError event. + */ + public function setOnValidationError($javascript) + { + $this->setFunction('OnValidationError', $javascript); + } + + /** + * @return string javascript code for client-side OnError event. + */ + public function getOnValidationError() + { + return $this->getOption('OnValidationError'); + } + + /** + * @param boolean true to revalidate when the control to validate changes value. + */ + public function setObserveChanges($value) + { + $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value)); + } + + /** + * @return boolean true to observe changes. + */ + public function getObserveChanges() + { + $changes = $this->getOption('ObserveChanges'); + return ($changes===null) ? true : $changes; + } +} diff --git a/framework/Web/UI/ActiveControls/TActiveLabel.php b/framework/Web/UI/ActiveControls/TActiveLabel.php index 32d91cd2..c05b1744 100644 --- a/framework/Web/UI/ActiveControls/TActiveLabel.php +++ b/framework/Web/UI/ActiveControls/TActiveLabel.php @@ -80,7 +80,7 @@ class TActiveLabel extends TLabel implements IActiveControl /** * Adds attribute id to the renderer. - * @param THtmlWriter the writer used for the rendering purpose + * @param THtmlWriter the writer used for the rendering purpose */ protected function addAttributesToRender($writer) { $writer->addAttribute('id',$this->getClientID()); diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index 726dd8ba..144f621e 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -1,401 +1,401 @@ - - * @author Gabor Berczi (lazyload additions & progressive rendering) - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load callback response adapter class. - */ -Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter'); -Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript'); -Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); - -/** - * TActivePageAdapter class. - * - * Callback request handler. - * - * @author Wei Zhuo - * @author Gabor Berczi (lazyload additions & progressive rendering) - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -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'; - /** - * Script list header name. - */ - const CALLBACK_SCRIPTLIST_HEADER = 'X-PRADO-SCRIPTLIST'; - /** - * Stylesheet list header name. - */ - const CALLBACK_STYLESHEETLIST_HEADER = 'X-PRADO-STYLESHEETLIST'; - /** - * Hidden field list header name. - */ - const CALLBACK_HIDDENFIELDLIST_HEADER = 'X-PRADO-HIDDENFIELDLIST'; - - /** - * Callback redirect url header name. - */ - const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT'; - - /** - * @var ICallbackEventHandler callback event handler. - */ - private $_callbackEventTarget; - /** - * @var mixed callback event parameter. - */ - private $_callbackEventParameter; - /** - * @var TCallbackClientScript callback client script handler - */ - private $_callbackClient; - - private $_controlsToRender=array(); - - /** - * Constructor, trap errors and exception to let the callback response - * handle them. - */ - public function __construct(TPage $control) - { - parent::__construct($control); - - //TODO: can this be done later? - $response = $this->getApplication()->getResponse(); - $response->setAdapter(new TCallbackResponseAdapter($response)); - - $this->trapCallbackErrorsExceptions(); - } - - /** - * Process the callback request. - * @param THtmlWriter html content writer. - */ - public function processCallbackEvent($writer) - { - Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - $this->raiseCallbackEvent(); - } - - /** - * Register a control for defered render() call. - * @param TControl control for defered rendering - * @param THtmlWriter the renderer - */ - public function registerControlToRender($control,$writer) - { - $id = $control->getUniqueID(); - if(!isset($this->_controlsToRender[$id])) - $this->_controlsToRender[$id] = array($control,$writer); - } - - /** - * Trap errors and exceptions to be handled by TCallbackErrorHandler. - */ - protected function trapCallbackErrorsExceptions() - { - $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); - } - - /** - * Render the callback response. - * @param THtmlWriter html content writer. - */ - public function renderCallbackResponse($writer) - { - Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null) - $this->renderResponse($writer); - else - $this->redirect($url); - } - - /** - * Redirect url on the client-side using javascript. - * @param string new url to load. - */ - protected function redirect($url) - { - Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url); - //$this->getResponse()->appendHeader(self::CALLBACK_REDIRECT.': '.$url); - } - - /** - * 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) - { - Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - //renders all the defered render() calls. - foreach($this->_controlsToRender as $rid => $forRender) - $forRender[0]->render($forRender[1]); - - $response = $this->getResponse(); - - //send response data in header - if($response->getHasAdapter()) - { - $responseData = $response->getAdapter()->getResponseData(); - if($responseData!==null) - { - $data = TJavaScript::jsonEncode($responseData); - - $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data); - //$response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data); - } - } - - //sends page state in header - if(($handler = $this->getCallbackEventTarget()) !== null) - { - if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate()) - { - $pagestate = $this->getPage()->getClientState(); - $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate); - //$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(); - $actions = TJavaScript::jsonEncode($executeJavascript); - $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions); - //$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); - - - $cs = $this->Page->getClientScript(); - - // collect all stylesheet file references - $stylesheets = $cs->getStyleSheetUrls(); - if (count($stylesheets)>0) - $this->appendContentPart($response, self::CALLBACK_STYLESHEETLIST_HEADER, TJavaScript::jsonEncode($stylesheets)); - - // collect all script file references - $scripts = $cs->getScriptUrls(); - if (count($scripts)>0) - $this->appendContentPart($response, self::CALLBACK_SCRIPTLIST_HEADER, TJavaScript::jsonEncode($scripts)); - - // collect all hidden field references - $fields = $cs->getHiddenFields(); - if (count($fields)>0) - $this->appendContentPart($response, self::CALLBACK_HIDDENFIELDLIST_HEADER, TJavaScript::jsonEncode($fields)); - } - - /** - * Appends data or javascript code to the body content surrounded with delimiters - */ - private function appendContentPart($response, $delimiter, $data) - { - $content = $response->createHtmlWriter(); - $content->getWriter()->setBoundary($delimiter); - $content->write($data); - } - - /** - * Trys to find the callback event handler and raise its callback event. - * @throws TInvalidCallbackException if call back target is not found. - * @throws TInvalidCallbackException if the requested target does not - * implement ICallbackEventHandler. - */ - private function raiseCallbackEvent() - { - if(($callbackHandler=$this->getCallbackEventTarget())!==null) - { - if($callbackHandler instanceof ICallbackEventHandler) - { - $param = $this->getCallbackEventParameter(); - $result = new TCallbackEventParameter($this->getResponse(), $param); - $callbackHandler->raiseCallbackEvent($result); - } - else - { - throw new TInvalidCallbackException( - 'callback_invalid_handler', $callbackHandler->getUniqueID()); - } - } - else - { - $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); - throw new TInvalidCallbackException('callback_invalid_target', $target); - } - } - - /** - * @return TControl the control responsible for the current callback event, - * null if nonexistent - */ - public function getCallbackEventTarget() - { - if($this->_callbackEventTarget===null) - { - $eventTarget=$this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); - if(!empty($eventTarget)) - $this->_callbackEventTarget=$this->getPage()->findControl($eventTarget); - } - return $this->_callbackEventTarget; - } - - /** - * Registers a control to raise callback event in the current request. - * @param TControl control registered to raise callback event. - */ - public function setCallbackEventTarget(TControl $control) - { - $this->_callbackEventTarget=$control; - } - - /** - * Gets callback parameter. JSON encoding is assumed. - * @return string postback event parameter - */ - public function getCallbackEventParameter() - { - if($this->_callbackEventParameter===null) - { - $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); - if(strlen($param) > 0) - $this->_callbackEventParameter=TJavaScript::jsonDecode((string)$param); - } - return $this->_callbackEventParameter; - } - - /** - * @param mixed postback event parameter - */ - public function setCallbackEventParameter($value) - { - $this->_callbackEventParameter=$value; - } - - /** - * Gets the callback client script handler. It handlers the javascript functions - * to be executed during the callback response. - * @return TCallbackClientScript callback client handler. - */ - public function getCallbackClientHandler() - { - if($this->_callbackClient===null) - $this->_callbackClient = new TCallbackClientScript; - return $this->_callbackClient; - } -} - -/** - * 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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -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(); - $trace = TJavaScript::jsonEncode($this->getExceptionStackTrace($exception)); - $response->setStatusCode(500, 'Internal Server Error'); - $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace); - } - else - { - error_log("Error happened while processing an existing error:\n".$exception->__toString()); - header('HTTP/1.0 500 Internal Server Error', true, 500); - } - $this->getApplication()->getResponse()->flush(); - } - - /** - * @param Exception exception details. - * @return array exception stack trace details. - */ - private function getExceptionStackTrace($exception) - { - $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500; - $data['file']=$exception->getFile(); - $data['line']=$exception->getLine(); - $data['trace']=$exception->getTrace(); - if($exception instanceof TPhpErrorException) - { - // if PHP exception, we want to show the 2nd stack level context - // because the 1st stack level is of little use (it's in error handler) - if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line'])) - { - $data['file']=$trace[0]['file']; - $data['line']=$trace[0]['line']; - } - } - $data['type']=get_class($exception); - $data['message']=$exception->getMessage(); - $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion(); - $data['time']=@strftime('%Y-%m-%d %H:%M',time()); - return $data; - } -} - -/** - * TInvalidCallbackException class. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TInvalidCallbackException extends TException -{ -} - + + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load callback response adapter class. + */ +Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter'); +Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TActivePageAdapter class. + * + * Callback request handler. + * + * @author Wei Zhuo + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +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'; + /** + * Script list header name. + */ + const CALLBACK_SCRIPTLIST_HEADER = 'X-PRADO-SCRIPTLIST'; + /** + * Stylesheet list header name. + */ + const CALLBACK_STYLESHEETLIST_HEADER = 'X-PRADO-STYLESHEETLIST'; + /** + * Hidden field list header name. + */ + const CALLBACK_HIDDENFIELDLIST_HEADER = 'X-PRADO-HIDDENFIELDLIST'; + + /** + * Callback redirect url header name. + */ + const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT'; + + /** + * @var ICallbackEventHandler callback event handler. + */ + private $_callbackEventTarget; + /** + * @var mixed callback event parameter. + */ + private $_callbackEventParameter; + /** + * @var TCallbackClientScript callback client script handler + */ + private $_callbackClient; + + private $_controlsToRender=array(); + + /** + * Constructor, trap errors and exception to let the callback response + * handle them. + */ + public function __construct(TPage $control) + { + parent::__construct($control); + + //TODO: can this be done later? + $response = $this->getApplication()->getResponse(); + $response->setAdapter(new TCallbackResponseAdapter($response)); + + $this->trapCallbackErrorsExceptions(); + } + + /** + * Process the callback request. + * @param THtmlWriter html content writer. + */ + public function processCallbackEvent($writer) + { + Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->raiseCallbackEvent(); + } + + /** + * Register a control for defered render() call. + * @param TControl control for defered rendering + * @param THtmlWriter the renderer + */ + public function registerControlToRender($control,$writer) + { + $id = $control->getUniqueID(); + if(!isset($this->_controlsToRender[$id])) + $this->_controlsToRender[$id] = array($control,$writer); + } + + /** + * Trap errors and exceptions to be handled by TCallbackErrorHandler. + */ + protected function trapCallbackErrorsExceptions() + { + $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); + } + + /** + * Render the callback response. + * @param THtmlWriter html content writer. + */ + public function renderCallbackResponse($writer) + { + Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null) + $this->renderResponse($writer); + else + $this->redirect($url); + } + + /** + * Redirect url on the client-side using javascript. + * @param string new url to load. + */ + protected function redirect($url) + { + Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url); + //$this->getResponse()->appendHeader(self::CALLBACK_REDIRECT.': '.$url); + } + + /** + * 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) + { + Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + //renders all the defered render() calls. + foreach($this->_controlsToRender as $rid => $forRender) + $forRender[0]->render($forRender[1]); + + $response = $this->getResponse(); + + //send response data in header + if($response->getHasAdapter()) + { + $responseData = $response->getAdapter()->getResponseData(); + if($responseData!==null) + { + $data = TJavaScript::jsonEncode($responseData); + + $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data); + //$response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data); + } + } + + //sends page state in header + if(($handler = $this->getCallbackEventTarget()) !== null) + { + if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate()) + { + $pagestate = $this->getPage()->getClientState(); + $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate); + //$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(); + $actions = TJavaScript::jsonEncode($executeJavascript); + $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions); + //$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); + + + $cs = $this->Page->getClientScript(); + + // collect all stylesheet file references + $stylesheets = $cs->getStyleSheetUrls(); + if (count($stylesheets)>0) + $this->appendContentPart($response, self::CALLBACK_STYLESHEETLIST_HEADER, TJavaScript::jsonEncode($stylesheets)); + + // collect all script file references + $scripts = $cs->getScriptUrls(); + if (count($scripts)>0) + $this->appendContentPart($response, self::CALLBACK_SCRIPTLIST_HEADER, TJavaScript::jsonEncode($scripts)); + + // collect all hidden field references + $fields = $cs->getHiddenFields(); + if (count($fields)>0) + $this->appendContentPart($response, self::CALLBACK_HIDDENFIELDLIST_HEADER, TJavaScript::jsonEncode($fields)); + } + + /** + * Appends data or javascript code to the body content surrounded with delimiters + */ + private function appendContentPart($response, $delimiter, $data) + { + $content = $response->createHtmlWriter(); + $content->getWriter()->setBoundary($delimiter); + $content->write($data); + } + + /** + * Trys to find the callback event handler and raise its callback event. + * @throws TInvalidCallbackException if call back target is not found. + * @throws TInvalidCallbackException if the requested target does not + * implement ICallbackEventHandler. + */ + private function raiseCallbackEvent() + { + if(($callbackHandler=$this->getCallbackEventTarget())!==null) + { + if($callbackHandler instanceof ICallbackEventHandler) + { + $param = $this->getCallbackEventParameter(); + $result = new TCallbackEventParameter($this->getResponse(), $param); + $callbackHandler->raiseCallbackEvent($result); + } + else + { + throw new TInvalidCallbackException( + 'callback_invalid_handler', $callbackHandler->getUniqueID()); + } + } + else + { + $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); + throw new TInvalidCallbackException('callback_invalid_target', $target); + } + } + + /** + * @return TControl the control responsible for the current callback event, + * null if nonexistent + */ + public function getCallbackEventTarget() + { + if($this->_callbackEventTarget===null) + { + $eventTarget=$this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); + if(!empty($eventTarget)) + $this->_callbackEventTarget=$this->getPage()->findControl($eventTarget); + } + return $this->_callbackEventTarget; + } + + /** + * Registers a control to raise callback event in the current request. + * @param TControl control registered to raise callback event. + */ + public function setCallbackEventTarget(TControl $control) + { + $this->_callbackEventTarget=$control; + } + + /** + * Gets callback parameter. JSON encoding is assumed. + * @return string postback event parameter + */ + public function getCallbackEventParameter() + { + if($this->_callbackEventParameter===null) + { + $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); + if(strlen($param) > 0) + $this->_callbackEventParameter=TJavaScript::jsonDecode((string)$param); + } + return $this->_callbackEventParameter; + } + + /** + * @param mixed postback event parameter + */ + public function setCallbackEventParameter($value) + { + $this->_callbackEventParameter=$value; + } + + /** + * Gets the callback client script handler. It handlers the javascript functions + * to be executed during the callback response. + * @return TCallbackClientScript callback client handler. + */ + public function getCallbackClientHandler() + { + if($this->_callbackClient===null) + $this->_callbackClient = new TCallbackClientScript; + return $this->_callbackClient; + } +} + +/** + * 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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +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(); + $trace = TJavaScript::jsonEncode($this->getExceptionStackTrace($exception)); + $response->setStatusCode(500, 'Internal Server Error'); + $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace); + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Server Error', true, 500); + } + $this->getApplication()->getResponse()->flush(); + } + + /** + * @param Exception exception details. + * @return array exception stack trace details. + */ + private function getExceptionStackTrace($exception) + { + $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500; + $data['file']=$exception->getFile(); + $data['line']=$exception->getLine(); + $data['trace']=$exception->getTrace(); + if($exception instanceof TPhpErrorException) + { + // if PHP exception, we want to show the 2nd stack level context + // because the 1st stack level is of little use (it's in error handler) + if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line'])) + { + $data['file']=$trace[0]['file']; + $data['line']=$trace[0]['line']; + } + } + $data['type']=get_class($exception); + $data['message']=$exception->getMessage(); + $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion(); + $data['time']=@strftime('%Y-%m-%d %H:%M',time()); + return $data; + } +} + +/** + * TInvalidCallbackException class. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TInvalidCallbackException extends TException +{ +} + diff --git a/framework/Web/UI/ActiveControls/TActivePanel.php b/framework/Web/UI/ActiveControls/TActivePanel.php index ae1dd09f..1edfa57c 100644 --- a/framework/Web/UI/ActiveControls/TActivePanel.php +++ b/framework/Web/UI/ActiveControls/TActivePanel.php @@ -1,100 +1,100 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load active control adapter. - */ -Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); - -/** - * TActivePanel is the TPanel active control counterpart. - * - * TActivePanel allows the client-side panel contents to be updated during a - * callback response using the {@link render} method. - * - * Example: Assume $param is an instance of TCallbackEventParameter attached to - * the OnCallback event of a TCallback with ID "callback1", and - * "panel1" is the ID of a TActivePanel. - * - * function callback1_requested($sender, $param) - * { - * $this->panel1->render($param->getNewWriter()); - * } - * - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActivePanel extends TPanel implements IActiveControl -{ - /** - * Creates a new callback control, sets the adapter to - * TActiveControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, by calling this constructor. - */ - public function __construct() - { - parent::__construct(); - $this->setAdapter(new TActiveControlAdapter($this)); - } - - /** - * @return TBaseActiveControl standard active control options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * Adds attribute id to the renderer. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) { - $writer->addAttribute('id',$this->getClientID()); - parent::addAttributesToRender($writer); - } - - /** - * Renders and replaces the panel's content on the client-side. - * When render() is called before the OnPreRender event, such as when render() - * is called during a callback event handler, the rendering - * is defered until OnPreRender event is raised. - * @param THtmlWriter html writer - */ - public function render($writer) - { - if($this->getHasPreRendered()) - { - parent::render($writer); - if($this->getActiveControl()->canUpdateClientSide()) - $this->getPage()->getCallbackClient()->replaceContent($this,$writer); - } - else - { - $this->getPage()->getAdapter()->registerControlToRender($this,$writer); - if ($this->getHasControls()) - { - // If we update a TActivePanel on callback, - // We shouldn't update all childs, because the whole content will be replaced by - // the parent - foreach ($this->findControlsByType('IActiveControl', false) as $control) - { - $control->getActiveControl()->setEnableUpdate(false); - } - } - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActivePanel is the TPanel active control counterpart. + * + * TActivePanel allows the client-side panel contents to be updated during a + * callback response using the {@link render} method. + * + * Example: Assume $param is an instance of TCallbackEventParameter attached to + * the OnCallback event of a TCallback with ID "callback1", and + * "panel1" is the ID of a TActivePanel. + * + * function callback1_requested($sender, $param) + * { + * $this->panel1->render($param->getNewWriter()); + * } + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActivePanel extends TPanel implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Adds attribute id to the renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) { + $writer->addAttribute('id',$this->getClientID()); + parent::addAttributesToRender($writer); + } + + /** + * Renders and replaces the panel's content on the client-side. + * When render() is called before the OnPreRender event, such as when render() + * is called during a callback event handler, the rendering + * is defered until OnPreRender event is raised. + * @param THtmlWriter html writer + */ + public function render($writer) + { + if($this->getHasPreRendered()) + { + parent::render($writer); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this,$writer); + } + else + { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + if ($this->getHasControls()) + { + // If we update a TActivePanel on callback, + // We shouldn't update all childs, because the whole content will be replaced by + // the parent + foreach ($this->findControlsByType('IActiveControl', false) as $control) + { + $control->getActiveControl()->setEnableUpdate(false); + } + } + } + } +} + diff --git a/framework/Web/UI/ActiveControls/TActiveRatingList.php b/framework/Web/UI/ActiveControls/TActiveRatingList.php index 7c878dd8..81f50dd1 100644 --- a/framework/Web/UI/ActiveControls/TActiveRatingList.php +++ b/framework/Web/UI/ActiveControls/TActiveRatingList.php @@ -1,133 +1,133 @@ - - * @author Bradley Booms - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * TActiveRatingList Class - * - * Displays clickable images that represent a TRadioButtonList - * - * @author Wei Zhuo - * @author Bradley Booms - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveRatingList extends TRatingList implements IActiveControl, ICallbackEventHandler -{ - /** - * Creates a new callback control, sets the adapter to - * TActiveListControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, by calling this constructor. - */ - public function __construct() - { - $this->setAdapter(new TActiveListControlAdapter($this)); - $this->setAutoPostBack(true); - parent::__construct(); - } - - /** - * @return TBaseActiveCallbackControl standard callback control options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * @return TCallbackClientSide client side request options. - */ - public function getClientSide() - { - return $this->getAdapter()->getBaseActiveControl()->getClientSide(); - } - - /** - * Raises the callback event. This method is required by {@link - * ICallbackEventHandler} interface. - * This method is mainly used by framework and control developers. - * @param TCallbackEventParameter the event parameter - */ - public function raiseCallbackEvent($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); - } - - /** - * @param boolean whether the items in the column can be edited - */ - public function setReadOnly($value) - { - parent::setReadOnly($value); - $value = $this->getReadOnly(); - $this->callClientFunction('setReadOnly',$value); - } - - /** - * @param float rating value, also sets the selected Index - */ - public function setRating($value) - { - parent::setRating($value); - $value = $this->getRating(); - $this->callClientFunction('setRating',$value); - } - - /** - * Calls the client-side static method for this control class. - * @param string static method name - * @param mixed method parmaeter - */ - protected function callClientFunction($func,$value) - { - if($this->getActiveControl()->canUpdateClientSide()) - { - $client = $this->getPage()->getCallbackClient(); - $code = parent::getClientClassName().'.'.$func; - $client->callClientFunction($code,array($this,$value)); - } - } - - /** - * @param string caption text - */ - public function setCaption($value) - { - parent::setCaption($value); - // if it's an active control, this should not be needed. - $this->callClientFunction('setCaption',$value); - } - - /** - * 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.TActiveRatingList'; - } -} - + + * @author Bradley Booms + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveRatingList Class + * + * Displays clickable images that represent a TRadioButtonList + * + * @author Wei Zhuo + * @author Bradley Booms + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveRatingList extends TRatingList implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + parent::__construct(); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($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); + } + + /** + * @param boolean whether the items in the column can be edited + */ + public function setReadOnly($value) + { + parent::setReadOnly($value); + $value = $this->getReadOnly(); + $this->callClientFunction('setReadOnly',$value); + } + + /** + * @param float rating value, also sets the selected Index + */ + public function setRating($value) + { + parent::setRating($value); + $value = $this->getRating(); + $this->callClientFunction('setRating',$value); + } + + /** + * Calls the client-side static method for this control class. + * @param string static method name + * @param mixed method parmaeter + */ + protected function callClientFunction($func,$value) + { + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $code = parent::getClientClassName().'.'.$func; + $client->callClientFunction($code,array($this,$value)); + } + } + + /** + * @param string caption text + */ + public function setCaption($value) + { + parent::setCaption($value); + // if it's an active control, this should not be needed. + $this->callClientFunction('setCaption',$value); + } + + /** + * 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.TActiveRatingList'; + } +} + diff --git a/framework/Web/UI/ActiveControls/TActiveTextBox.php b/framework/Web/UI/ActiveControls/TActiveTextBox.php index 1e2fc46f..562f59cd 100644 --- a/framework/Web/UI/ActiveControls/TActiveTextBox.php +++ b/framework/Web/UI/ActiveControls/TActiveTextBox.php @@ -1,125 +1,125 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load active control adapter. - */ -Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); - -/** - * 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 OnTextChanged} to be fired first followed by {@link onCallback OnCallback} - * event. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActiveTextBox extends TTextBox implements ICallbackEventHandler, IActiveControl -{ - /** - * Creates a new callback control, sets the adapter to - * TActiveControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, by calling this constructor. - */ - public function __construct() - { - parent::__construct(); - $this->setAdapter(new TActiveControlAdapter($this)); - } - - /** - * @return TBaseActiveCallbackControl standard callback control options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * @return TCallbackClientSide client side request options. - */ - public function getClientSide() - { - return $this->getAdapter()->getBaseActiveControl()->getClientSide(); - } - - /** - * Client-side Text property can only be updated after the OnLoad stage. - * @param string text content for the textbox - */ - public function setText($value) - { - parent::setText($value); - if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()) - $this->getPage()->getCallbackClient()->setValue($this, $value); - } - - /** - * Raises the callback event. This method is required by {@link - * ICallbackEventHandler} interface. - * This method is mainly used by framework and control developers. - * @param TCallbackEventParameter the event parameter - */ - public function raiseCallbackEvent($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); - } - - /** - * 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.TActiveTextBox'; - } - - /** - * Override parent implementation, no javascript is rendered here instead - * the javascript required for active control is registered in {@link addAttributesToRender}. - */ - protected function renderClientControlScript($writer) - { - } - - /** - * 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()); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * 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 OnTextChanged} to be fired first followed by {@link onCallback OnCallback} + * event. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveTextBox extends TTextBox implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Client-side Text property can only be updated after the OnLoad stage. + * @param string text content for the textbox + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()) + $this->getPage()->getCallbackClient()->setValue($this, $value); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($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); + } + + /** + * 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.TActiveTextBox'; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * 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()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } +} + diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index 413d5c21..ce648b02 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -1,440 +1,440 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load active text box. - */ -Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); -Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); - -/** - * TAutoComplete class. - * - * TAutoComplete is a textbox that provides a list of suggestion on - * the current partial word typed in the textbox. The suggestions are - * requested using callbacks, and raises the {@link onSuggestion OnSuggestion} - * event. The events of the TActiveText (from which TAutoComplete is extended from) - * and {@link onSuggestion OnSuggestion} are mutually exculsive. That is, - * if {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} - * events are raise, then {@link onSuggestion OnSuggestion} will not be raise, and - * vice versa. - * - * The list of suggestions should be set in the {@link onSuggestion OnSuggestion} - * event handler. The partial word to match the suggestion is in the - * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} - * property. The datasource of the TAutoComplete must be set using {@link setDataSource} - * method. This sets the datasource for the suggestions repeater, available through - * the {@link getSuggestions Suggestions} property. Header, footer templates and - * other properties of the repeater can be access via the {@link getSuggestions Suggestions} - * property and its sub-properties. - * - * The {@link setTextCssClass TextCssClass} property if set is used to find - * the element within the Suggestions.ItemTemplate and Suggestions.AlternatingItemTemplate - * that contains the actual text for the suggestion selected. That is, - * only text inside elements with CSS class name equal to {@link setTextCssClass TextCssClass} - * will be used as suggestions. - * - * To return the list of suggestions back to the browser, supply a non-empty data source - * and call databind. For example, - * - * function autocomplete_suggestion($sender, $param) - * { - * $token = $param->getToken(); //the partial word to match - * $sender->setDataSource($this->getSuggestionsFor($token)); //set suggestions - * $sender->dataBind(); - * } - * - * - * The suggestion will be rendered when the {@link dataBind()} method is called - * during a callback request. - * - * When an suggestion is selected, that is, when the use has clicked, pressed - * the "Enter" key, or pressed the "Tab" key, the {@link onSuggestionSelected OnSuggestionSelected} - * event is raised. The - * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} - * property contains the index of the selected suggestion. - * - * TAutoComplete allows multiple suggestions within one textbox with each - * word or phrase separated by any characters specified in the - * {@link setSeparator Separator} property. The {@link setFrequency Frequency} - * and {@link setMinChars MinChars} properties sets the delay and minimum number - * of characters typed, respectively, before requesting for sugggestions. - * - * Use {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} events - * to handle post backs due to {@link setAutoPostBack AutoPostBack}. - * - * In the {@link getSuggestions Suggestions} TRepater item template, all HTML text elements - * are considered as text for the suggestion. Text within HTML elements with CSS class name - * "informal" are ignored as text for suggestions. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TAutoComplete extends TActiveTextBox implements INamingContainer -{ - /** - * @var ITemplate template for repeater items - */ - private $_repeater=null; - /** - * @var TPanel result panel holding the suggestion items. - */ - private $_resultPanel=null; - - /** - * @return string word or token separators (delimiters). - */ - public function getSeparator() - { - return $this->getViewState('tokens', ''); - } - - /** - * @return string word or token separators (delimiters). - */ - public function setSeparator($value) - { - $this->setViewState('tokens', TPropertyValue::ensureString($value), ''); - } - - /** - * @return float maximum delay (in seconds) before requesting a suggestion. - */ - public function getFrequency() - { - return $this->getViewState('frequency', ''); - } - - /** - * @param float maximum delay (in seconds) before requesting a suggestion. - * Default is 0.4. - */ - public function setFrequency($value) - { - $this->setViewState('frequency', TPropertyValue::ensureFloat($value),''); - } - - /** - * @return integer minimum number of characters before requesting a suggestion. - */ - public function getMinChars() - { - return $this->getViewState('minChars',''); - } - - /** - * @param integer minimum number of characters before requesting a suggestion. - */ - public function setMinChars($value) - { - $this->setViewState('minChars', TPropertyValue::ensureInteger($value), ''); - } - - /** - * @param string Css class name of the element to use for suggestion. - */ - public function setTextCssClass($value) - { - $this->setViewState('TextCssClass', $value); - } - - /** - * @return string Css class name of the element to use for suggestion. - */ - public function getTextCssClass() - { - return $this->getViewState('TextCssClass'); - } - - /** - * Raises the callback event. This method is overrides the parent implementation. - * If {@link setAutoPostBack AutoPostBack} is enabled it will raise - * {@link onTextChanged OnTextChanged} event event and then the - * {@link onCallback OnCallback} event. The {@link onSuggest OnSuggest} event is - * raise if the request is to find sugggestions, the {@link onTextChanged OnTextChanged} - * and {@link onCallback OnCallback} events are NOT raised. - * This method is mainly used by framework and control developers. - * @param TCallbackEventParameter the event parameter - */ - public function raiseCallbackEvent($param) - { - $token = $param->getCallbackParameter(); - if(is_array($token) && count($token) == 2) - { - if($token[1] === '__TAutoComplete_onSuggest__') - { - $parameter = new TAutoCompleteEventParameter($this->getResponse(), $token[0]); - $this->onSuggest($parameter); - } - else if($token[1] === '__TAutoComplete_onSuggestionSelected__') - { - $parameter = new TAutoCompleteEventParameter($this->getResponse(), null, $token[0]); - $this->onSuggestionSelected($parameter); - } - } - else if($this->getAutoPostBack()) - parent::raiseCallbackEvent($param); - } - - /** - * This method is invoked when an autocomplete suggestion is requested. - * The method raises 'OnSuggest' event. 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 onSuggest($param) - { - $this->raiseEvent('OnSuggest', $this, $param); - } - - /** - * This method is invoked when an autocomplete suggestion is selected. - * The method raises 'OnSuggestionSelected' event. 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 onSuggestionSelected($param) - { - $this->raiseEvent('OnSuggestionSelected', $this, $param); - } - - /** - * @param array data source for suggestions. - */ - public function setDataSource($data) - { - $this->getSuggestions()->setDataSource($data); - } - - /** - * Overrides parent implementation. Callback {@link renderSuggestions()} when - * page's IsCallback property is true. - */ - public function dataBind() - { - parent::dataBind(); - if($this->getPage()->getIsCallback()) - $this->renderSuggestions($this->getResponse()->createHtmlWriter()); - } - - /** - * @return TPanel suggestion results panel. - */ - public function getResultPanel() - { - if($this->_resultPanel===null) - $this->_resultPanel = $this->createResultPanel(); - return $this->_resultPanel; - } - - /** - * @return TPanel new instance of result panel. Default uses TPanel. - */ - protected function createResultPanel() - { - $panel = Prado::createComponent('System.Web.UI.WebControls.TPanel'); - $this->getControls()->add($panel); - $panel->setID('result'); - return $panel; - } - - /** - * @return TRepeater suggestion list repeater - */ - public function getSuggestions() - { - if($this->_repeater===null) - $this->_repeater = $this->createRepeater(); - return $this->_repeater; - } - - /** - * @return TRepeater new instance of TRepater to render the list of suggestions. - */ - protected function createRepeater() - { - $repeater = Prado::createComponent('System.Web.UI.WebControls.TRepeater'); - $repeater->setHeaderTemplate(new TAutoCompleteTemplate('
    ')); - $repeater->setFooterTemplate(new TAutoCompleteTemplate('
')); - $repeater->setItemTemplate(new TTemplate('
  • <%# $this->DataItem %>
  • ',null)); - $this->getControls()->add($repeater); - return $repeater; - } - - /** - * Renders the end tag and registers javascript effects library. - */ - public function renderEndTag($writer) - { - $this->getPage()->getClientScript()->registerPradoScript('effects'); - parent::renderEndTag($writer); - $this->renderResultPanel($writer); - } - - /** - * Renders the result panel. - * @param THtmlWriter the renderer. - */ - protected function renderResultPanel($writer) - { - $this->getResultPanel()->render($writer); - } - - /** - * Renders the suggestions during a callback respones. - * @param THtmlWriter the renderer. - */ - public function renderCallback($writer) - { - $this->renderSuggestions($writer); - } - - /** - * Renders the suggestions repeater. - * @param THtmlWriter the renderer. - */ - public function renderSuggestions($writer) - { - if($this->getActiveControl()->canUpdateClientSide()) - { - $this->getSuggestions()->render($writer); - $boundary = $writer->getWriter()->getBoundary(); - $this->getResponse()->getAdapter()->setResponseData($boundary); - } - } - - /** - * @return array list of callback options. - */ - protected function getPostBackOptions() - { - //disallow page state update ? - //$this->getActiveControl()->getClientSide()->setEnablePageStateUpdate(false); - $options = array(); - if(strlen($string = $this->getSeparator())) - { - $string = strtr($string,array('\t'=>"\t",'\n'=>"\n",'\r'=>"\r")); - $token = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY); - $options['tokens'] = $token; - } - if($this->getAutoPostBack()) - { - $options = array_merge($options,parent::getPostBackOptions()); - $options['AutoPostBack'] = true; - } - if(strlen($select = $this->getTextCssClass())) - $options['select'] = $select; - $options['ResultPanel'] = $this->getResultPanel()->getClientID(); - $options['ID'] = $this->getClientID(); - $options['EventTarget'] = $this->getUniqueID(); - if(($minchars=$this->getMinChars())!=='') - $options['minChars'] = $minchars; - if(($frequency=$this->getFrequency())!=='') - $options['frequency'] = $frequency; - $options['CausesValidation'] = $this->getCausesValidation(); - $options['ValidationGroup'] = $this->getValidationGroup(); - return $options; - } - - /** - * Override parent implementation, no javascript is rendered here instead - * the javascript required for active control is registered in {@link addAttributesToRender}. - */ - protected function renderClientControlScript($writer) - { - } - - /** - * @return string corresponding javascript class name for this TActiveButton. - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TAutoComplete'; - } -} - -/** - * TAutCompleteEventParameter contains the {@link getToken Token} requested by - * the user for a partial match of the suggestions. - * - * The {@link getSelectedIndex SelectedIndex} is a zero-based index of the - * suggestion selected by the user, -1 if not suggestion is selected. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TAutoCompleteEventParameter extends TCallbackEventParameter -{ - private $_selectedIndex=-1; - - /** - * Creates a new TCallbackEventParameter. - */ - public function __construct($response, $parameter, $index=-1) - { - parent::__construct($response, $parameter); - $this->_selectedIndex=$index; - } - - /** - * @return int selected suggestion zero-based index, -1 if not selected. - */ - public function getSelectedIndex() - { - return $this->_selectedIndex; - } - - /** - * @return string token for matching a list of suggestions. - */ - public function getToken() - { - return $this->getCallbackParameter(); - } -} - -/** - * TAutoCompleteTemplate class. - * - * TAutoCompleteTemplate is the default template for TAutoCompleteTemplate - * item template. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TAutoCompleteTemplate extends TComponent implements ITemplate -{ - private $_template; - - public function __construct($template) - { - $this->_template = $template; - } - /** - * Instantiates the template. - * It creates a {@link TDataList} control. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $parent->getControls()->add($this->_template); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active text box. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TAutoComplete class. + * + * TAutoComplete is a textbox that provides a list of suggestion on + * the current partial word typed in the textbox. The suggestions are + * requested using callbacks, and raises the {@link onSuggestion OnSuggestion} + * event. The events of the TActiveText (from which TAutoComplete is extended from) + * and {@link onSuggestion OnSuggestion} are mutually exculsive. That is, + * if {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} + * events are raise, then {@link onSuggestion OnSuggestion} will not be raise, and + * vice versa. + * + * The list of suggestions should be set in the {@link onSuggestion OnSuggestion} + * event handler. The partial word to match the suggestion is in the + * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} + * property. The datasource of the TAutoComplete must be set using {@link setDataSource} + * method. This sets the datasource for the suggestions repeater, available through + * the {@link getSuggestions Suggestions} property. Header, footer templates and + * other properties of the repeater can be access via the {@link getSuggestions Suggestions} + * property and its sub-properties. + * + * The {@link setTextCssClass TextCssClass} property if set is used to find + * the element within the Suggestions.ItemTemplate and Suggestions.AlternatingItemTemplate + * that contains the actual text for the suggestion selected. That is, + * only text inside elements with CSS class name equal to {@link setTextCssClass TextCssClass} + * will be used as suggestions. + * + * To return the list of suggestions back to the browser, supply a non-empty data source + * and call databind. For example, + * + * function autocomplete_suggestion($sender, $param) + * { + * $token = $param->getToken(); //the partial word to match + * $sender->setDataSource($this->getSuggestionsFor($token)); //set suggestions + * $sender->dataBind(); + * } + * + * + * The suggestion will be rendered when the {@link dataBind()} method is called + * during a callback request. + * + * When an suggestion is selected, that is, when the use has clicked, pressed + * the "Enter" key, or pressed the "Tab" key, the {@link onSuggestionSelected OnSuggestionSelected} + * event is raised. The + * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} + * property contains the index of the selected suggestion. + * + * TAutoComplete allows multiple suggestions within one textbox with each + * word or phrase separated by any characters specified in the + * {@link setSeparator Separator} property. The {@link setFrequency Frequency} + * and {@link setMinChars MinChars} properties sets the delay and minimum number + * of characters typed, respectively, before requesting for sugggestions. + * + * Use {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} events + * to handle post backs due to {@link setAutoPostBack AutoPostBack}. + * + * In the {@link getSuggestions Suggestions} TRepater item template, all HTML text elements + * are considered as text for the suggestion. Text within HTML elements with CSS class name + * "informal" are ignored as text for suggestions. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoComplete extends TActiveTextBox implements INamingContainer +{ + /** + * @var ITemplate template for repeater items + */ + private $_repeater=null; + /** + * @var TPanel result panel holding the suggestion items. + */ + private $_resultPanel=null; + + /** + * @return string word or token separators (delimiters). + */ + public function getSeparator() + { + return $this->getViewState('tokens', ''); + } + + /** + * @return string word or token separators (delimiters). + */ + public function setSeparator($value) + { + $this->setViewState('tokens', TPropertyValue::ensureString($value), ''); + } + + /** + * @return float maximum delay (in seconds) before requesting a suggestion. + */ + public function getFrequency() + { + return $this->getViewState('frequency', ''); + } + + /** + * @param float maximum delay (in seconds) before requesting a suggestion. + * Default is 0.4. + */ + public function setFrequency($value) + { + $this->setViewState('frequency', TPropertyValue::ensureFloat($value),''); + } + + /** + * @return integer minimum number of characters before requesting a suggestion. + */ + public function getMinChars() + { + return $this->getViewState('minChars',''); + } + + /** + * @param integer minimum number of characters before requesting a suggestion. + */ + public function setMinChars($value) + { + $this->setViewState('minChars', TPropertyValue::ensureInteger($value), ''); + } + + /** + * @param string Css class name of the element to use for suggestion. + */ + public function setTextCssClass($value) + { + $this->setViewState('TextCssClass', $value); + } + + /** + * @return string Css class name of the element to use for suggestion. + */ + public function getTextCssClass() + { + return $this->getViewState('TextCssClass'); + } + + /** + * Raises the callback event. This method is overrides the parent implementation. + * If {@link setAutoPostBack AutoPostBack} is enabled it will raise + * {@link onTextChanged OnTextChanged} event event and then the + * {@link onCallback OnCallback} event. The {@link onSuggest OnSuggest} event is + * raise if the request is to find sugggestions, the {@link onTextChanged OnTextChanged} + * and {@link onCallback OnCallback} events are NOT raised. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $token = $param->getCallbackParameter(); + if(is_array($token) && count($token) == 2) + { + if($token[1] === '__TAutoComplete_onSuggest__') + { + $parameter = new TAutoCompleteEventParameter($this->getResponse(), $token[0]); + $this->onSuggest($parameter); + } + else if($token[1] === '__TAutoComplete_onSuggestionSelected__') + { + $parameter = new TAutoCompleteEventParameter($this->getResponse(), null, $token[0]); + $this->onSuggestionSelected($parameter); + } + } + else if($this->getAutoPostBack()) + parent::raiseCallbackEvent($param); + } + + /** + * This method is invoked when an autocomplete suggestion is requested. + * The method raises 'OnSuggest' event. 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 onSuggest($param) + { + $this->raiseEvent('OnSuggest', $this, $param); + } + + /** + * This method is invoked when an autocomplete suggestion is selected. + * The method raises 'OnSuggestionSelected' event. 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 onSuggestionSelected($param) + { + $this->raiseEvent('OnSuggestionSelected', $this, $param); + } + + /** + * @param array data source for suggestions. + */ + public function setDataSource($data) + { + $this->getSuggestions()->setDataSource($data); + } + + /** + * Overrides parent implementation. Callback {@link renderSuggestions()} when + * page's IsCallback property is true. + */ + public function dataBind() + { + parent::dataBind(); + if($this->getPage()->getIsCallback()) + $this->renderSuggestions($this->getResponse()->createHtmlWriter()); + } + + /** + * @return TPanel suggestion results panel. + */ + public function getResultPanel() + { + if($this->_resultPanel===null) + $this->_resultPanel = $this->createResultPanel(); + return $this->_resultPanel; + } + + /** + * @return TPanel new instance of result panel. Default uses TPanel. + */ + protected function createResultPanel() + { + $panel = Prado::createComponent('System.Web.UI.WebControls.TPanel'); + $this->getControls()->add($panel); + $panel->setID('result'); + return $panel; + } + + /** + * @return TRepeater suggestion list repeater + */ + public function getSuggestions() + { + if($this->_repeater===null) + $this->_repeater = $this->createRepeater(); + return $this->_repeater; + } + + /** + * @return TRepeater new instance of TRepater to render the list of suggestions. + */ + protected function createRepeater() + { + $repeater = Prado::createComponent('System.Web.UI.WebControls.TRepeater'); + $repeater->setHeaderTemplate(new TAutoCompleteTemplate('
      ')); + $repeater->setFooterTemplate(new TAutoCompleteTemplate('
    ')); + $repeater->setItemTemplate(new TTemplate('
  • <%# $this->DataItem %>
  • ',null)); + $this->getControls()->add($repeater); + return $repeater; + } + + /** + * Renders the end tag and registers javascript effects library. + */ + public function renderEndTag($writer) + { + $this->getPage()->getClientScript()->registerPradoScript('effects'); + parent::renderEndTag($writer); + $this->renderResultPanel($writer); + } + + /** + * Renders the result panel. + * @param THtmlWriter the renderer. + */ + protected function renderResultPanel($writer) + { + $this->getResultPanel()->render($writer); + } + + /** + * Renders the suggestions during a callback respones. + * @param THtmlWriter the renderer. + */ + public function renderCallback($writer) + { + $this->renderSuggestions($writer); + } + + /** + * Renders the suggestions repeater. + * @param THtmlWriter the renderer. + */ + public function renderSuggestions($writer) + { + if($this->getActiveControl()->canUpdateClientSide()) + { + $this->getSuggestions()->render($writer); + $boundary = $writer->getWriter()->getBoundary(); + $this->getResponse()->getAdapter()->setResponseData($boundary); + } + } + + /** + * @return array list of callback options. + */ + protected function getPostBackOptions() + { + //disallow page state update ? + //$this->getActiveControl()->getClientSide()->setEnablePageStateUpdate(false); + $options = array(); + if(strlen($string = $this->getSeparator())) + { + $string = strtr($string,array('\t'=>"\t",'\n'=>"\n",'\r'=>"\r")); + $token = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY); + $options['tokens'] = $token; + } + if($this->getAutoPostBack()) + { + $options = array_merge($options,parent::getPostBackOptions()); + $options['AutoPostBack'] = true; + } + if(strlen($select = $this->getTextCssClass())) + $options['select'] = $select; + $options['ResultPanel'] = $this->getResultPanel()->getClientID(); + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + if(($minchars=$this->getMinChars())!=='') + $options['minChars'] = $minchars; + if(($frequency=$this->getFrequency())!=='') + $options['frequency'] = $frequency; + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + return $options; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * @return string corresponding javascript class name for this TActiveButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TAutoComplete'; + } +} + +/** + * TAutCompleteEventParameter contains the {@link getToken Token} requested by + * the user for a partial match of the suggestions. + * + * The {@link getSelectedIndex SelectedIndex} is a zero-based index of the + * suggestion selected by the user, -1 if not suggestion is selected. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoCompleteEventParameter extends TCallbackEventParameter +{ + private $_selectedIndex=-1; + + /** + * Creates a new TCallbackEventParameter. + */ + public function __construct($response, $parameter, $index=-1) + { + parent::__construct($response, $parameter); + $this->_selectedIndex=$index; + } + + /** + * @return int selected suggestion zero-based index, -1 if not selected. + */ + public function getSelectedIndex() + { + return $this->_selectedIndex; + } + + /** + * @return string token for matching a list of suggestions. + */ + public function getToken() + { + return $this->getCallbackParameter(); + } +} + +/** + * TAutoCompleteTemplate class. + * + * TAutoCompleteTemplate is the default template for TAutoCompleteTemplate + * item template. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoCompleteTemplate extends TComponent implements ITemplate +{ + private $_template; + + public function __construct($template) + { + $this->_template = $template; + } + /** + * Instantiates the template. + * It creates a {@link TDataList} control. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $parent->getControls()->add($this->_template); + } +} + diff --git a/framework/Web/UI/ActiveControls/TBaseActiveControl.php b/framework/Web/UI/ActiveControls/TBaseActiveControl.php index d3391b71..c412a2e2 100644 --- a/framework/Web/UI/ActiveControls/TBaseActiveControl.php +++ b/framework/Web/UI/ActiveControls/TBaseActiveControl.php @@ -1,392 +1,392 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); - -/** - * TBaseActiveControl class provided additional basic property for every - * active control. An instance of TBaseActiveControl or its decendent - * TBaseActiveCallbackControl is created by {@link TActiveControlAdapter::getBaseActiveControl()} - * method. - * - * The {@link setEnableUpdate EnableUpdate} property determines wether the active - * control is allowed to update the contents of the client-side when the callback - * response returns. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TBaseActiveControl extends TComponent -{ - /** - * @var TMap map of active control options. - */ - private $_options; - /** - * @var TControl attached control. - */ - private $_control; - - /** - * Constructor. Attach a base active control to an active control instance. - * @param TControl active control - */ - public function __construct($control) - { - $this->_control = $control; - $this->_options = new TMap; - } - - /** - * Sets a named options with a value. Options are used to store and retrive - * named values for the base active controls. - * @param string option name. - * @param mixed new value. - * @param mixed default value. - * @return mixed options value. - */ - protected function setOption($name,$value,$default=null) - { - $value = ($value===null) ? $default : $value; - if($value!==null) - $this->_options->add($name,$value); - } - - /** - * 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. - */ - protected function getOption($name,$default=null) - { - $item = $this->_options->itemAt($name); - return ($item===null) ? $default : $item; - } - - /** - * @return TMap active control options - */ - protected function getOptions() - { - return $this->_options; - } - - /** - * @return TPage the page containing the attached control. - */ - protected function getPage() - { - return $this->_control->getPage(); - } - - /** - * @return TControl the attached control. - */ - protected function getControl() - { - return $this->_control; - } - - /** - * @param boolean true to allow fine grain callback updates. - */ - public function setEnableUpdate($value) - { - $this->setOption('EnableUpdate', TPropertyValue::ensureBoolean($value), true); - } - - /** - * @return boolean true to allow fine grain callback updates. - */ - public function getEnableUpdate() - { - return $this->getOption('EnableUpdate', true); - } - - /** - * Returns true if callback response is allowed to update the browser contents. - * Is is true if the control is initilized, and is a callback request and - * the {@link setEnableUpdate EnableUpdate} property is true and - * the page is not loading post data. - * @return boolean true if the callback response is allowed update - * client-side contents. - */ - public function canUpdateClientSide($bDontRequireVisibility=false) - { - return $this->getControl()->getHasChildInitialized() - && $this->getPage()->getIsLoadingPostData() == false - && $this->getPage()->getIsCallback() - && $this->getEnableUpdate() - && ($bDontRequireVisibility || $this->getControl()->getVisible()); - } -} - -/** - * TBaseActiveCallbackControl is a common set of options and functionality for - * active controls that can perform callback requests. - * - * The properties of TBaseActiveCallbackControl can be accessed and changed from - * each individual active controls' {@link getActiveControl ActiveControl} - * property. - * - * The following example sets the validation group property of a TCallback component. - * - * - * - * - * Additional client-side options and events can be set using the - * {@link getClientSide ClientSide} property. The following example shows - * an alert box when a TCallback component response returns successfully. - * - * - * - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TBaseActiveCallbackControl extends TBaseActiveControl -{ - /** - * Callback client-side options can be set by setting the properties of - * the ClientSide property. E.g. - * See {@link TCallbackClientSide} for details on the properties of ClientSide. - * @return TCallbackClientSide client-side callback options. - */ - public function getClientSide() - { - if(($client = $this->getOption('ClientSide'))===null) - { - $client = $this->createClientSide(); - $this->setOption('ClientSide', $client); - } - return $client; - } - - /** - * Sets the client side options. Can only be set when client side is null. - * @param TCallbackClientSide client side options. - */ - public function setClientSide($client) - { - if( $this->getOption('ClientSide')===null) - $this->setOption('ClientSide', $client); - else - throw new TConfigurationException( - 'active_controls_client_side_exists', $this->getControl()->getID()); - } - - /** - * @return TCallbackClientSide callback client-side options. - */ - protected function createClientSide() - { - return new TCallbackClientSide; - } - - /** - * 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 takes precedence over the CallbackOptions property. - * @param string ID of a TCallbackOptions control from which ClientSide - * options are cloned. - */ - public function setCallbackOptions($value) - { - $this->setOption('CallbackOptions', $value, ''); - } - - /** - * @return string ID of a TCallbackOptions control from which ClientSide - * options are duplicated. - */ - 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 setCallbackOptions CallbackOptions}. - * @return array list of default callback client-side options. - */ - protected function getDefaultClientSideOptions() - { - if(($id=$this->getCallbackOptions())!=='') - { - if(($pos=strrpos($id,'.'))!==false) - { - $control=$this->getControl()->getSubProperty(substr($id,0,$pos)); - $newid=substr($id,$pos+1); - if ($control!==null) - $control=$control->$newid; - } - else - { - $control=$this->getControl()->findControl($id); - } - - if($control instanceof TCallbackOptions) - return $control->getClientSide()->getOptions()->toArray(); - else - throw new TConfigurationException('callback_invalid_callback_options', $this->getControl()->getID(), $id); - } - - return array(); - } - - /** - * @return boolean whether callback event trigger by this button will cause - * input validation, default is true - */ - public function getCausesValidation() - { - return $this->getOption('CausesValidation',true); - } - - /** - * @param boolean whether callback event trigger by this button will cause - * input validation - */ - public function setCausesValidation($value) - { - $this->setOption('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the group of validators which the button causes validation - * upon callback - */ - public function getValidationGroup() - { - return $this->getOption('ValidationGroup',''); - } - - /** - * @param string the group of validators which the button causes validation - * upon callback - */ - public function setValidationGroup($value) - { - $this->setOption('ValidationGroup',$value,''); - } - - /** - * @return boolean whether to perform validation if the callback is - * requested. - */ - public function canCauseValidation() - { - if($this->getCausesValidation()) - { - $group=$this->getValidationGroup(); - return $this->getPage()->getValidators($group)->getCount()>0; - } - else - return false; - } - - /** - * @param mixed callback parameter value. - */ - public function setCallbackParameter($value) - { - $this->setOption('CallbackParameter', $value, ''); - } - - /** - * @return mixed callback parameter value. - */ - public function getCallbackParameter() - { - return $this->getOption('CallbackParameter', ''); - } - - - /** - * @return array list of callback javascript options. - */ - protected function getClientSideOptions() - { - $default = $this->getDefaultClientSideOptions(); - $options = array_merge($default,$this->getClientSide()->getOptions()->toArray()); - $validate = $this->getCausesValidation(); - $options['CausesValidation']= $validate ? '' : false; - $options['ValidationGroup']=$this->getValidationGroup(); - $options['CallbackParameter'] = $this->getCallbackParameter(); - return $options; - } - - /** - * Registers the callback control javascript code. Client-side options are - * merged and passed to the javascript code. This method should be called by - * Active component developers wanting to register the javascript to initialize - * the active component with additional options offered by the - * {@link getClientSide ClientSide} property. - * @param string client side javascript class name. - * @param array additional callback options. - */ - public function registerCallbackClientScript($class,$options=null) - { - $cs = $this->getPage()->getClientScript(); - if(is_array($options)) - $options = array_merge($this->getClientSideOptions(),$options); - else - $options = $this->getClientSideOptions(); - - //remove true as default to save bytes - if($options['CausesValidation']===true) - $options['CausesValidation']=''; - $cs->registerCallbackControl($class, $options); - } - - /** - * Returns the javascript callback request instance. To invoke a callback - * request for this control call the dispatch() method on the - * request instance. Example code in javascript - * - * var request = <%= $this->mycallback->ActiveControl->Javascript %>; - * request.setParameter('hello'); - * request.dispatch(); //make the callback request. - * - * - * Alternatively, - * - * //dispatches immediately - * Prado.Callback("<%= $this->mycallback->UniqueID %>", - * $this->mycallback->ActiveControl->JsCallbackOptions); - * - * @return string javascript client-side callback request object (javascript - * code) - */ - public function getJavascript() - { - $client = $this->getPage()->getClientScript(); - return $client->getCallbackReference($this->getControl(),$this->getClientSideOptions()); - } - - /** - * @param string callback requestion options as javascript code. - */ - public function getJsCallbackOptions() - { - return TJavaScript::encode($this->getClientSideOptions()); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); + +/** + * TBaseActiveControl class provided additional basic property for every + * active control. An instance of TBaseActiveControl or its decendent + * TBaseActiveCallbackControl is created by {@link TActiveControlAdapter::getBaseActiveControl()} + * method. + * + * The {@link setEnableUpdate EnableUpdate} property determines wether the active + * control is allowed to update the contents of the client-side when the callback + * response returns. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TBaseActiveControl extends TComponent +{ + /** + * @var TMap map of active control options. + */ + private $_options; + /** + * @var TControl attached control. + */ + private $_control; + + /** + * Constructor. Attach a base active control to an active control instance. + * @param TControl active control + */ + public function __construct($control) + { + $this->_control = $control; + $this->_options = new TMap; + } + + /** + * Sets a named options with a value. Options are used to store and retrive + * named values for the base active controls. + * @param string option name. + * @param mixed new value. + * @param mixed default value. + * @return mixed options value. + */ + protected function setOption($name,$value,$default=null) + { + $value = ($value===null) ? $default : $value; + if($value!==null) + $this->_options->add($name,$value); + } + + /** + * 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. + */ + protected function getOption($name,$default=null) + { + $item = $this->_options->itemAt($name); + return ($item===null) ? $default : $item; + } + + /** + * @return TMap active control options + */ + protected function getOptions() + { + return $this->_options; + } + + /** + * @return TPage the page containing the attached control. + */ + protected function getPage() + { + return $this->_control->getPage(); + } + + /** + * @return TControl the attached control. + */ + protected function getControl() + { + return $this->_control; + } + + /** + * @param boolean true to allow fine grain callback updates. + */ + public function setEnableUpdate($value) + { + $this->setOption('EnableUpdate', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return boolean true to allow fine grain callback updates. + */ + public function getEnableUpdate() + { + return $this->getOption('EnableUpdate', true); + } + + /** + * Returns true if callback response is allowed to update the browser contents. + * Is is true if the control is initilized, and is a callback request and + * the {@link setEnableUpdate EnableUpdate} property is true and + * the page is not loading post data. + * @return boolean true if the callback response is allowed update + * client-side contents. + */ + public function canUpdateClientSide($bDontRequireVisibility=false) + { + return $this->getControl()->getHasChildInitialized() + && $this->getPage()->getIsLoadingPostData() == false + && $this->getPage()->getIsCallback() + && $this->getEnableUpdate() + && ($bDontRequireVisibility || $this->getControl()->getVisible()); + } +} + +/** + * TBaseActiveCallbackControl is a common set of options and functionality for + * active controls that can perform callback requests. + * + * The properties of TBaseActiveCallbackControl can be accessed and changed from + * each individual active controls' {@link getActiveControl ActiveControl} + * property. + * + * The following example sets the validation group property of a TCallback component. + * + * + * + * + * Additional client-side options and events can be set using the + * {@link getClientSide ClientSide} property. The following example shows + * an alert box when a TCallback component response returns successfully. + * + * + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TBaseActiveCallbackControl extends TBaseActiveControl +{ + /** + * Callback client-side options can be set by setting the properties of + * the ClientSide property. E.g. + * See {@link TCallbackClientSide} for details on the properties of ClientSide. + * @return TCallbackClientSide client-side callback options. + */ + public function getClientSide() + { + if(($client = $this->getOption('ClientSide'))===null) + { + $client = $this->createClientSide(); + $this->setOption('ClientSide', $client); + } + return $client; + } + + /** + * Sets the client side options. Can only be set when client side is null. + * @param TCallbackClientSide client side options. + */ + public function setClientSide($client) + { + if( $this->getOption('ClientSide')===null) + $this->setOption('ClientSide', $client); + else + throw new TConfigurationException( + 'active_controls_client_side_exists', $this->getControl()->getID()); + } + + /** + * @return TCallbackClientSide callback client-side options. + */ + protected function createClientSide() + { + return new TCallbackClientSide; + } + + /** + * 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 takes precedence over the CallbackOptions property. + * @param string ID of a TCallbackOptions control from which ClientSide + * options are cloned. + */ + public function setCallbackOptions($value) + { + $this->setOption('CallbackOptions', $value, ''); + } + + /** + * @return string ID of a TCallbackOptions control from which ClientSide + * options are duplicated. + */ + 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 setCallbackOptions CallbackOptions}. + * @return array list of default callback client-side options. + */ + protected function getDefaultClientSideOptions() + { + if(($id=$this->getCallbackOptions())!=='') + { + if(($pos=strrpos($id,'.'))!==false) + { + $control=$this->getControl()->getSubProperty(substr($id,0,$pos)); + $newid=substr($id,$pos+1); + if ($control!==null) + $control=$control->$newid; + } + else + { + $control=$this->getControl()->findControl($id); + } + + if($control instanceof TCallbackOptions) + return $control->getClientSide()->getOptions()->toArray(); + else + throw new TConfigurationException('callback_invalid_callback_options', $this->getControl()->getID(), $id); + } + + return array(); + } + + /** + * @return boolean whether callback event trigger by this button will cause + * input validation, default is true + */ + public function getCausesValidation() + { + return $this->getOption('CausesValidation',true); + } + + /** + * @param boolean whether callback event trigger by this button will cause + * input validation + */ + public function setCausesValidation($value) + { + $this->setOption('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the button causes validation + * upon callback + */ + public function getValidationGroup() + { + return $this->getOption('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation + * upon callback + */ + public function setValidationGroup($value) + { + $this->setOption('ValidationGroup',$value,''); + } + + /** + * @return boolean whether to perform validation if the callback is + * requested. + */ + public function canCauseValidation() + { + if($this->getCausesValidation()) + { + $group=$this->getValidationGroup(); + return $this->getPage()->getValidators($group)->getCount()>0; + } + else + return false; + } + + /** + * @param mixed callback parameter value. + */ + public function setCallbackParameter($value) + { + $this->setOption('CallbackParameter', $value, ''); + } + + /** + * @return mixed callback parameter value. + */ + public function getCallbackParameter() + { + return $this->getOption('CallbackParameter', ''); + } + + + /** + * @return array list of callback javascript options. + */ + protected function getClientSideOptions() + { + $default = $this->getDefaultClientSideOptions(); + $options = array_merge($default,$this->getClientSide()->getOptions()->toArray()); + $validate = $this->getCausesValidation(); + $options['CausesValidation']= $validate ? '' : false; + $options['ValidationGroup']=$this->getValidationGroup(); + $options['CallbackParameter'] = $this->getCallbackParameter(); + return $options; + } + + /** + * Registers the callback control javascript code. Client-side options are + * merged and passed to the javascript code. This method should be called by + * Active component developers wanting to register the javascript to initialize + * the active component with additional options offered by the + * {@link getClientSide ClientSide} property. + * @param string client side javascript class name. + * @param array additional callback options. + */ + public function registerCallbackClientScript($class,$options=null) + { + $cs = $this->getPage()->getClientScript(); + if(is_array($options)) + $options = array_merge($this->getClientSideOptions(),$options); + else + $options = $this->getClientSideOptions(); + + //remove true as default to save bytes + if($options['CausesValidation']===true) + $options['CausesValidation']=''; + $cs->registerCallbackControl($class, $options); + } + + /** + * Returns the javascript callback request instance. To invoke a callback + * request for this control call the dispatch() method on the + * request instance. Example code in javascript + * + * var request = <%= $this->mycallback->ActiveControl->Javascript %>; + * request.setParameter('hello'); + * request.dispatch(); //make the callback request. + * + * + * Alternatively, + * + * //dispatches immediately + * Prado.Callback("<%= $this->mycallback->UniqueID %>", + * $this->mycallback->ActiveControl->JsCallbackOptions); + * + * @return string javascript client-side callback request object (javascript + * code) + */ + public function getJavascript() + { + $client = $this->getPage()->getClientScript(); + return $client->getCallbackReference($this->getControl(),$this->getClientSideOptions()); + } + + /** + * @param string callback requestion options as javascript code. + */ + public function getJsCallbackOptions() + { + return TJavaScript::encode($this->getClientSideOptions()); + } +} + diff --git a/framework/Web/UI/ActiveControls/TCallback.php b/framework/Web/UI/ActiveControls/TCallback.php index 7a1f54b7..662bc03f 100644 --- a/framework/Web/UI/ActiveControls/TCallback.php +++ b/framework/Web/UI/ActiveControls/TCallback.php @@ -1,101 +1,101 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load active control adapter. - */ -Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); - -/** - * TCallback component class. - * - * The TCallback provides a basic callback handler that can be invoked from the - * client side by running the javascript code obtained from the - * {@link TBaseActiveCallbackControl::getJavascript ActiveControl.Javascript} property. - * The event {@link onCallback OnCallback} is raised when a callback is requested made. - * - * Example usage: - * - * - * - *
    Click Me!
    - *
    - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallback extends TControl implements ICallbackEventHandler, IActiveControl -{ - /** - * Creates a new callback control, sets the adapter to - * TActiveControlAdapter. If you override this class, be sure to set the - * adapter appropriately by, for example, call this constructor. - */ - public function __construct() - { - parent::__construct(); - $this->setAdapter(new TActiveControlAdapter($this)); - } - - /** - * @return TBaseActiveCallbackControl standard callback options. - */ - public function getActiveControl() - { - return $this->getAdapter()->getBaseActiveControl(); - } - - /** - * @return TCallbackClientSide client side request options. - */ - public function getClientSide() - { - return $this->getAdapter()->getBaseActiveControl()->getClientSide(); - } - - /** - * Raises the callback event. This method is required by - * {@link ICallbackEventHandler ICallbackEventHandler} interface. If - * {@link getCausesValidation ActiveControl.CausesValidation} is true, - * it will invoke the page's {@link TPage::validate validate} method first. - * It will raise {@link onCallback OnCallback} event. This method is mainly - * used by framework and control developers. - * @param TCallbackEventParameter the event parameter - */ - public function raiseCallbackEvent($param) - { - if($this->getActiveControl()->canCauseValidation()) - $this->getPage()->validate($this->getActiveControl()->getValidationGroup()); - $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); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TCallback component class. + * + * The TCallback provides a basic callback handler that can be invoked from the + * client side by running the javascript code obtained from the + * {@link TBaseActiveCallbackControl::getJavascript ActiveControl.Javascript} property. + * The event {@link onCallback OnCallback} is raised when a callback is requested made. + * + * Example usage: + * + * + * + *
    Click Me!
    + *
    + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallback extends TControl implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, call this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by + * {@link ICallbackEventHandler ICallbackEventHandler} interface. If + * {@link getCausesValidation ActiveControl.CausesValidation} is true, + * it will invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onCallback OnCallback} event. This method is mainly + * used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + if($this->getActiveControl()->canCauseValidation()) + $this->getPage()->validate($this->getActiveControl()->getValidationGroup()); + $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); + } +} + diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 6dcbe3d0..291f22f2 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -1,705 +1,705 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * TCallbackClientScript class. - * - * The TCallbackClientScript class provides corresponding methods that can be - * executed on the client-side (i.e. the browser client that is viewing - * the page) during a callback response. - * - * The avaiable methods includes setting/clicking input elements, changing Css - * styles, hiding/showing elements, and adding visual effects to elements on the - * page. The client-side methods can be access through the CallbackClient - * property available in TPage. - * - * For example, to hide "$myTextBox" element during callback response, do - * - * $this->getPage()->getCallbackClient()->hide($myTextBox); - * - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackClientScript extends TApplicationComponent -{ - /** - * @var TList list of client functions to execute. - */ - private $_actions; - - /** - * Constructor. - */ - public function __construct() - { - $this->_actions = new TList; - } - - /** - * @return array list of client function to be executed during callback - * response. - */ - public function getClientFunctionsToExecute() - { - return $this->_actions->toArray(); - } - - /** - * Executes a client-side statement. - * @param string javascript function name - * @param array list of arguments for the function - */ - public function callClientFunction($function, $params=null) - { - if(!is_array($params)) - $params = array($params); - - if(count($params) > 0) - { - if($params[0] instanceof TControl) - $params[0] = $params[0]->getClientID(); - } - $this->_actions->add(array($function => $params)); - } - - /** - * Client script to set the value of a particular input element. - * @param TControl control element to set the new value - * @param string new value - */ - public function setValue($input, $text) - { - $this->callClientFunction('Prado.Element.setValue', array($input, $text)); - } - - /** - * Client script to select/clear/check a drop down list, check box list, - * or radio button list. - * The second parameter determines the selection method. Valid methods are - * - Value, select or check by value - * - Values, select or check by a list of values - * - Index, select or check by index (zero based index) - * - Indices, select or check by a list of index (zero based index) - * - Clear, clears or selections or checks in the list - * - All, select all - * - Invert, invert the selection. - * @param TControl list control - * @param string selection method - * @param string|int the value or index to select/check. - * @param string selection control type, either 'check' or 'select' - */ - public function select($control, $method='Value', $value=null, $type=null) - { - $method = TPropertyValue::ensureEnum($method, - 'Value', 'Index', 'Clear', 'Indices', 'Values', 'All', 'Invert'); - $type = ($type===null) ? $this->getSelectionControlType($control) : $type; - $total = $this->getSelectionControlIsListType($control) ? $control->getItemCount() : 1; - $this->callClientFunction('Prado.Element.select', - array($control, $type.$method, $value, $total)); - } - - private function getSelectionControlType($control) - { - if(is_string($control)) return 'check'; - if($control instanceof TCheckBoxList) - return 'check'; - if($control instanceof TCheckBox) - return 'check'; - return 'select'; - } - - private function getSelectionControlIsListType($control) - { - return $control instanceof TListControl; - } - - /** - * Client script to click on an element. This client-side function is unpredictable. - * - * @param TControl control element or element id - */ - public function click($control) - { - $this->callClientFunction('Prado.Element.click', $control); - } - - /** - * Client script to check or uncheck a checkbox or radio button. - * @param TControl control element or element id - * @param boolean check or uncheck the checkbox or radio button. - */ - public function check($checkbox, $checked=true) - { - $this->select($checkbox, "Value", $checked); - } - - /** - * Raise the client side event (given by $eventName) on a particular element. - * @param TControl control element or element id - * @param string Event name, e.g. "click" - */ - public function raiseClientEvent($control, $eventName) - { - $this->callClientFunction('Event.fireEvent', - array($control, strtolower($eventName))); - } - - /** - * Sets the attribute of a particular control. - * @param TControl control element or element id - * @param string attribute name - * @param string attribute value - */ - public function setAttribute($control, $name, $value) - { - // Attributes should be applied on Surrounding tag, except for 'disabled' attribute - if ($control instanceof ISurroundable && strtolower($name)!=='disabled') - $control=$control->getSurroundingTagID(); - $this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value)); - } - - /** - * Sets the options of a select input element. - * @param TControl control element or element id - * @param TCollection a list of new options - */ - public function setListItems($control, $items) - { - $options = array(); - if($control instanceof TListControl) - { - $promptText = $control->getPromptText(); - $promptValue = $control->getPromptValue(); - - if($promptValue==='') - $promptValue = $promptText; - - if($promptValue!=='') - $options[] = array($promptText, $promptValue); - } - - foreach($items as $item) - { - if($item->getHasAttributes()) - $options[] = array($item->getText(),$item->getValue(), $item->getAttributes()->itemAt('Group')); - else - $options[] = array($item->getText(),$item->getValue()); - } - $this->callClientFunction('Prado.Element.setOptions', array($control, $options)); - } - - /** - * Shows an element by changing its CSS display style as empty. - * @param TControl control element or element id - */ - public function show($element) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.show', $element); - } - - /** - * Hides an element by changing its CSS display style to "none". - * @param TControl control element or element id - */ - public function hide($element) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.hide', $element); - } - - /** - * Toggles the visibility of the element. - * @param TControl control element or element id - * @param string visual effect, such as, 'appear' or 'slide' or 'blind'. - * @param array additional options. - */ - public function toggle($element, $effect=null, $options=array()) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.toggle', array($element,$effect,$options)); - } - - /** - * Removes an element from the HTML page. - * @param TControl control element or element id - */ - public function remove($element) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.remove', $element); - } - - public function addPostDataLoader($name) - { - $this->callClientFunction('Prado.CallbackRequest.addPostLoaders', $name); - } - - /** - * Update the element's innerHTML with new content. - * @param TControl control element or element id - * @param TControl new HTML content, if content is of a TControl, the - * controls render method is called. - */ - public function update($element, $content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content, 'Element.update'); - } - - /** - * Add a Css class name to the element. - * @param TControl control element or element id - * @param string CssClass name to add. - */ - public function addCssClass($element, $cssClass) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.addClassName', array($element, $cssClass)); - } - - /** - * Remove a Css class name from the element. - * @param TControl control element or element id - * @param string CssClass name to remove. - */ - public function removeCssClass($element, $cssClass) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.removeClassName', array($element, $cssClass)); - } - - /** - * Sets the CssClass of an element. - * @param TControl control element or element id - * @param string new CssClass name for the element. - */ - /*public function setCssClass($element, $cssClass) - { - $this->callClientFunction('Prado.Element.CssClass.set', array($element, $cssClass)); - }*/ - - /** - * Scroll the top of the browser viewing area to the location of the - * element. - * @param TControl control element or element id - */ - public function scrollTo($element) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Element.scrollTo', $element); - } - - /** - * Focus on a particular element. - * @param TControl control element or element id. - */ - public function focus($element) - { - $this->callClientFunction('Prado.Element.focus', $element); - } - - /** - * Sets the style of element. The style must be a key-value array where the - * key is the style property and the value is the style value. - * @param TControl control element or element id - * @param array list of key-value pairs as style property and style value. - */ - public function setStyle($element, $styles) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction('Prado.Element.setStyle', array($element, $styles)); - } - - /** - * Append a HTML fragement to the element. - * @param TControl control element or element id - * @param string HTML fragement or the control to be rendered - */ - public function appendContent($element, $content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content, 'Prado.Element.Insert.append'); - } - - /** - * Prepend a HTML fragement to the element. - * @param TControl control element or element id - * @param string HTML fragement or the control to be rendered - */ - public function prependContent($element, $content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content, 'Prado.Element.Insert.prepend'); - } - - /** - * Insert a HTML fragement after the element. - * @param TControl control element or element id - * @param string HTML fragement or the control to be rendered - */ - public function insertContentAfter($element, $content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content, 'Prado.Element.Insert.after'); - } - - /** - * Insert a HTML fragement in before the element. - * @param TControl control element or element id - * @param string HTML fragement or the control to be rendered - */ - public function insertContentBefore($element, $content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content, 'Prado.Element.Insert.before'); - } - - /** - * Replace the content of an element with new content. The new content can - * be a string or a TControl component. If the content parameter is - * a TControl component, its rendered method will be called and its contents - * will be used for replacement. - * @param TControl control element or HTML element id. - * @param string HTML fragement or the control to be rendered - * @param string replacement method, default is to replace the outter - * html content. - * @param string provide a custom boundary. - * @see insertAbout - * @see insertBelow - * @see insertBefore - * @see insertAfter - */ - protected function replace($element, $content, $method="Element.replace", $boundary=null) - { - if($content instanceof TControl) - { - $boundary = $this->getRenderedContentBoundary($content); - $content = null; - } - else if($content instanceof THtmlWriter) - { - $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 control element or HTML element id. - * @param string HTML fragement or the control to be rendered - */ - public function replaceContent($element,$content) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->replace($element, $content); - } - - /** - * 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'); - } - - /** - * Appends a block of inline javascript enclosed in a boundary. - * Similar to to evaluateScript(), but functions declared in the - * inline block will be available to page elements. - * @param THtmlWriter writer for the content. - */ - public function appendScriptBlock($content) - { - if($content instanceof TControl) - { - $boundary = $this->getRenderedContentBoundary($content); - } - else if($content instanceof THtmlWriter) - { - $boundary = $this->getResponseContentBoundary($content); - } - - $this->callClientFunction('Prado.Element.appendScriptBlock', array($boundary)); - } - - /** - * Renders the control and return the content boundary from - * TCallbackResponseWriter. This method should only be used by framework - * component developers. The render() method is defered to be called in the - * TActivePageAdapter class. - * @param TControl control to be rendered on callback response. - * @return string the boundary for which the rendered content is wrapped. - */ - private function getRenderedContentBoundary($control) - { - $writer = $this->getResponse()->createHtmlWriter(); - $adapter = $control->getPage()->getAdapter(); - $adapter->registerControlToRender($control, $writer); - return $writer->getWriter()->getBoundary(); - } - - /** - * @param THtmlWriter the writer responsible for rendering html content. - * @return string content boundary. - */ - private function getResponseContentBoundary($html) - { - if($html instanceof THtmlWriter) - { - if($html->getWriter() instanceof TCallbackResponseWriter) - return $html->getWriter()->getBoundary(); - } - return null; - } - - /** - * Add a visual effect the element. - * @param string visual effect function name. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function visualEffect($type, $element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->callClientFunction($type, array($element, $options)); - } - - /** - * Visual Effect: Gradually make the element appear. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function appear($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Appear', $element, $options); - } - - /** - * Visual Effect: Blind down. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function blindDown($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.BlindDown', $element, $options); - } - - /** - * Visual Effect: Blind up. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function blindUp($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.BlindUp', $element, $options); - - } - - /** - * Visual Effect: Drop out. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function dropOut($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.DropOut', $element, $options); - } - - /** - * Visual Effect: Gradually fade the element. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function fade($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Fade', $element, $options); - } - - /** - * Visual Effect: Fold. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function fold($element, $options = null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Fold', $element, $options); - } - - /** - * Visual Effect: Gradually make an element grow to a predetermined size. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function grow($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Grow', $element, $options); - } - - /** - * Visual Effect: Gradually grow and fade the element. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function puff($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Puff', $element, $options); - } - - /** - * Visual Effect: Pulsate. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function pulsate($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Pulsate', $element, $options); - } - - /** - * Visual Effect: Shake the element. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function shake($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Shake', $element, $options); - } - - /** - * Visual Effect: Shrink the element. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function shrink($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Shrink', $element, $options); - } - - /** - * Visual Effect: Slide down. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function slideDown($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.SlideDown', $element, $options); - } - - /** - * Visual Effect: Side up. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function slideUp($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.SlideUp', $element, $options); - } - - /** - * Visual Effect: Squish the element. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function squish($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.Squish', $element, $options); - } - - /** - * Visual Effect: Switch Off effect. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function switchOff($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Effect.SwitchOff', $element, $options); - } - - /** - * Visual Effect: High light the element for about 2 seconds. - * @param TControl control element or element id - * @param array visual effect key-value pair options. - */ - public function highlight($element, $options=null) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $this->visualEffect('Prado.Effect.Highlight', $element, $options); - } - - /** - * Set the opacity on a html element or control. - * @param TControl control element or element id - * @param float opacity value between 1 and 0 - */ - public function setOpacity($element, $value) - { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); - $value = TPropertyValue::ensureFloat($value); - $this->callClientFunction('Element.setOpacity', array($element,$value)); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackClientScript class. + * + * The TCallbackClientScript class provides corresponding methods that can be + * executed on the client-side (i.e. the browser client that is viewing + * the page) during a callback response. + * + * The avaiable methods includes setting/clicking input elements, changing Css + * styles, hiding/showing elements, and adding visual effects to elements on the + * page. The client-side methods can be access through the CallbackClient + * property available in TPage. + * + * For example, to hide "$myTextBox" element during callback response, do + * + * $this->getPage()->getCallbackClient()->hide($myTextBox); + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackClientScript extends TApplicationComponent +{ + /** + * @var TList list of client functions to execute. + */ + private $_actions; + + /** + * Constructor. + */ + public function __construct() + { + $this->_actions = new TList; + } + + /** + * @return array list of client function to be executed during callback + * response. + */ + public function getClientFunctionsToExecute() + { + return $this->_actions->toArray(); + } + + /** + * Executes a client-side statement. + * @param string javascript function name + * @param array list of arguments for the function + */ + public function callClientFunction($function, $params=null) + { + if(!is_array($params)) + $params = array($params); + + if(count($params) > 0) + { + if($params[0] instanceof TControl) + $params[0] = $params[0]->getClientID(); + } + $this->_actions->add(array($function => $params)); + } + + /** + * Client script to set the value of a particular input element. + * @param TControl control element to set the new value + * @param string new value + */ + public function setValue($input, $text) + { + $this->callClientFunction('Prado.Element.setValue', array($input, $text)); + } + + /** + * Client script to select/clear/check a drop down list, check box list, + * or radio button list. + * The second parameter determines the selection method. Valid methods are + * - Value, select or check by value + * - Values, select or check by a list of values + * - Index, select or check by index (zero based index) + * - Indices, select or check by a list of index (zero based index) + * - Clear, clears or selections or checks in the list + * - All, select all + * - Invert, invert the selection. + * @param TControl list control + * @param string selection method + * @param string|int the value or index to select/check. + * @param string selection control type, either 'check' or 'select' + */ + public function select($control, $method='Value', $value=null, $type=null) + { + $method = TPropertyValue::ensureEnum($method, + 'Value', 'Index', 'Clear', 'Indices', 'Values', 'All', 'Invert'); + $type = ($type===null) ? $this->getSelectionControlType($control) : $type; + $total = $this->getSelectionControlIsListType($control) ? $control->getItemCount() : 1; + $this->callClientFunction('Prado.Element.select', + array($control, $type.$method, $value, $total)); + } + + private function getSelectionControlType($control) + { + if(is_string($control)) return 'check'; + if($control instanceof TCheckBoxList) + return 'check'; + if($control instanceof TCheckBox) + return 'check'; + return 'select'; + } + + private function getSelectionControlIsListType($control) + { + return $control instanceof TListControl; + } + + /** + * Client script to click on an element. This client-side function is unpredictable. + * + * @param TControl control element or element id + */ + public function click($control) + { + $this->callClientFunction('Prado.Element.click', $control); + } + + /** + * Client script to check or uncheck a checkbox or radio button. + * @param TControl control element or element id + * @param boolean check or uncheck the checkbox or radio button. + */ + public function check($checkbox, $checked=true) + { + $this->select($checkbox, "Value", $checked); + } + + /** + * Raise the client side event (given by $eventName) on a particular element. + * @param TControl control element or element id + * @param string Event name, e.g. "click" + */ + public function raiseClientEvent($control, $eventName) + { + $this->callClientFunction('Event.fireEvent', + array($control, strtolower($eventName))); + } + + /** + * Sets the attribute of a particular control. + * @param TControl control element or element id + * @param string attribute name + * @param string attribute value + */ + public function setAttribute($control, $name, $value) + { + // Attributes should be applied on Surrounding tag, except for 'disabled' attribute + if ($control instanceof ISurroundable && strtolower($name)!=='disabled') + $control=$control->getSurroundingTagID(); + $this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value)); + } + + /** + * Sets the options of a select input element. + * @param TControl control element or element id + * @param TCollection a list of new options + */ + public function setListItems($control, $items) + { + $options = array(); + if($control instanceof TListControl) + { + $promptText = $control->getPromptText(); + $promptValue = $control->getPromptValue(); + + if($promptValue==='') + $promptValue = $promptText; + + if($promptValue!=='') + $options[] = array($promptText, $promptValue); + } + + foreach($items as $item) + { + if($item->getHasAttributes()) + $options[] = array($item->getText(),$item->getValue(), $item->getAttributes()->itemAt('Group')); + else + $options[] = array($item->getText(),$item->getValue()); + } + $this->callClientFunction('Prado.Element.setOptions', array($control, $options)); + } + + /** + * Shows an element by changing its CSS display style as empty. + * @param TControl control element or element id + */ + public function show($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.show', $element); + } + + /** + * Hides an element by changing its CSS display style to "none". + * @param TControl control element or element id + */ + public function hide($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.hide', $element); + } + + /** + * Toggles the visibility of the element. + * @param TControl control element or element id + * @param string visual effect, such as, 'appear' or 'slide' or 'blind'. + * @param array additional options. + */ + public function toggle($element, $effect=null, $options=array()) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.toggle', array($element,$effect,$options)); + } + + /** + * Removes an element from the HTML page. + * @param TControl control element or element id + */ + public function remove($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.remove', $element); + } + + public function addPostDataLoader($name) + { + $this->callClientFunction('Prado.CallbackRequest.addPostLoaders', $name); + } + + /** + * Update the element's innerHTML with new content. + * @param TControl control element or element id + * @param TControl new HTML content, if content is of a TControl, the + * controls render method is called. + */ + public function update($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Element.update'); + } + + /** + * Add a Css class name to the element. + * @param TControl control element or element id + * @param string CssClass name to add. + */ + public function addCssClass($element, $cssClass) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.addClassName', array($element, $cssClass)); + } + + /** + * Remove a Css class name from the element. + * @param TControl control element or element id + * @param string CssClass name to remove. + */ + public function removeCssClass($element, $cssClass) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.removeClassName', array($element, $cssClass)); + } + + /** + * Sets the CssClass of an element. + * @param TControl control element or element id + * @param string new CssClass name for the element. + */ + /*public function setCssClass($element, $cssClass) + { + $this->callClientFunction('Prado.Element.CssClass.set', array($element, $cssClass)); + }*/ + + /** + * Scroll the top of the browser viewing area to the location of the + * element. + * @param TControl control element or element id + */ + public function scrollTo($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.scrollTo', $element); + } + + /** + * Focus on a particular element. + * @param TControl control element or element id. + */ + public function focus($element) + { + $this->callClientFunction('Prado.Element.focus', $element); + } + + /** + * Sets the style of element. The style must be a key-value array where the + * key is the style property and the value is the style value. + * @param TControl control element or element id + * @param array list of key-value pairs as style property and style value. + */ + public function setStyle($element, $styles) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Prado.Element.setStyle', array($element, $styles)); + } + + /** + * Append a HTML fragement to the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function appendContent($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.append'); + } + + /** + * Prepend a HTML fragement to the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function prependContent($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.prepend'); + } + + /** + * Insert a HTML fragement after the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function insertContentAfter($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.after'); + } + + /** + * Insert a HTML fragement in before the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function insertContentBefore($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.before'); + } + + /** + * Replace the content of an element with new content. The new content can + * be a string or a TControl component. If the content parameter is + * a TControl component, its rendered method will be called and its contents + * will be used for replacement. + * @param TControl control element or HTML element id. + * @param string HTML fragement or the control to be rendered + * @param string replacement method, default is to replace the outter + * html content. + * @param string provide a custom boundary. + * @see insertAbout + * @see insertBelow + * @see insertBefore + * @see insertAfter + */ + protected function replace($element, $content, $method="Element.replace", $boundary=null) + { + if($content instanceof TControl) + { + $boundary = $this->getRenderedContentBoundary($content); + $content = null; + } + else if($content instanceof THtmlWriter) + { + $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 control element or HTML element id. + * @param string HTML fragement or the control to be rendered + */ + public function replaceContent($element,$content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content); + } + + /** + * 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'); + } + + /** + * Appends a block of inline javascript enclosed in a boundary. + * Similar to to evaluateScript(), but functions declared in the + * inline block will be available to page elements. + * @param THtmlWriter writer for the content. + */ + public function appendScriptBlock($content) + { + if($content instanceof TControl) + { + $boundary = $this->getRenderedContentBoundary($content); + } + else if($content instanceof THtmlWriter) + { + $boundary = $this->getResponseContentBoundary($content); + } + + $this->callClientFunction('Prado.Element.appendScriptBlock', array($boundary)); + } + + /** + * Renders the control and return the content boundary from + * TCallbackResponseWriter. This method should only be used by framework + * component developers. The render() method is defered to be called in the + * TActivePageAdapter class. + * @param TControl control to be rendered on callback response. + * @return string the boundary for which the rendered content is wrapped. + */ + private function getRenderedContentBoundary($control) + { + $writer = $this->getResponse()->createHtmlWriter(); + $adapter = $control->getPage()->getAdapter(); + $adapter->registerControlToRender($control, $writer); + return $writer->getWriter()->getBoundary(); + } + + /** + * @param THtmlWriter the writer responsible for rendering html content. + * @return string content boundary. + */ + private function getResponseContentBoundary($html) + { + if($html instanceof THtmlWriter) + { + if($html->getWriter() instanceof TCallbackResponseWriter) + return $html->getWriter()->getBoundary(); + } + return null; + } + + /** + * Add a visual effect the element. + * @param string visual effect function name. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function visualEffect($type, $element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction($type, array($element, $options)); + } + + /** + * Visual Effect: Gradually make the element appear. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function appear($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Appear', $element, $options); + } + + /** + * Visual Effect: Blind down. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function blindDown($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.BlindDown', $element, $options); + } + + /** + * Visual Effect: Blind up. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function blindUp($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.BlindUp', $element, $options); + + } + + /** + * Visual Effect: Drop out. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function dropOut($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.DropOut', $element, $options); + } + + /** + * Visual Effect: Gradually fade the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function fade($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Fade', $element, $options); + } + + /** + * Visual Effect: Fold. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function fold($element, $options = null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Fold', $element, $options); + } + + /** + * Visual Effect: Gradually make an element grow to a predetermined size. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function grow($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Grow', $element, $options); + } + + /** + * Visual Effect: Gradually grow and fade the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function puff($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Puff', $element, $options); + } + + /** + * Visual Effect: Pulsate. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function pulsate($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Pulsate', $element, $options); + } + + /** + * Visual Effect: Shake the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function shake($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Shake', $element, $options); + } + + /** + * Visual Effect: Shrink the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function shrink($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Shrink', $element, $options); + } + + /** + * Visual Effect: Slide down. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function slideDown($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SlideDown', $element, $options); + } + + /** + * Visual Effect: Side up. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function slideUp($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SlideUp', $element, $options); + } + + /** + * Visual Effect: Squish the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function squish($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Squish', $element, $options); + } + + /** + * Visual Effect: Switch Off effect. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function switchOff($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SwitchOff', $element, $options); + } + + /** + * Visual Effect: High light the element for about 2 seconds. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function highlight($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Prado.Effect.Highlight', $element, $options); + } + + /** + * Set the opacity on a html element or control. + * @param TControl control element or element id + * @param float opacity value between 1 and 0 + */ + public function setOpacity($element, $value) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $value = TPropertyValue::ensureFloat($value); + $this->callClientFunction('Element.setOpacity', array($element,$value)); + } +} + diff --git a/framework/Web/UI/ActiveControls/TCallbackClientSide.php b/framework/Web/UI/ActiveControls/TCallbackClientSide.php index 10231a1c..0d602fa5 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientSide.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientSide.php @@ -1,322 +1,322 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * TCallbackClientSide class. - * - * The following client side events are executing in order if the callback - * request and response are send and received successfuly. - * - * - onPreDispatch executed before a request is dispatched. - * - onUninitialized executed when callback request is uninitialized. - * - onLoading* executed when callback request is initiated - * - onLoaded* executed when callback request begins. - * - onInteractive executed when callback request is in progress. - * - onCompleteexecuted when callback response returns. - * - onSuccess executed when callback request returns and is successful. - * - onFailure executed when callback request returns and fails. - * - onException raised when callback request fails due to request/response errors. - * - * * Note that theses 2 events are not fired correctly by Opera. To make - * them work in this browser, Prado will fire them just after onPreDispatch. - * - * In a general way, onUninitialized, onLoading, onLoaded and onInteractive events - * are not implemented consistently in all browsers.When cross browser compatibility is - * needed, it is best to avoid use them - * - * The OnSuccess and OnFailure events are raised when the - * response is returned. A successful request/response will raise - * OnSuccess event otherwise OnFailure will be raised. - * - * - PostState true to collect the form inputs and post them during callback, default is true. - * - RequestTimeOut The request timeout in milliseconds. - * - HasPriority true to ensure that the callback request will be sent - * immediately and will abort existing prioritized requests. It does not affect - * callbacks that are not prioritized. - * - EnablePageStateUpdate enable the callback response to enable the - * viewstate update. This will automatically set HasPriority to true when enabled. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackClientSide extends TClientSideOptions -{ - /** - * Returns javascript statement enclosed within a javascript function. - * @param string javascript statement - * @return string javascript statement wrapped in a javascript function - */ - protected function ensureFunction($javascript) - { - return "function(sender, parameter){ {$javascript} }"; - } - - /** - * @param string javascript code to be executed before a request is dispatched. - */ - public function setOnPreDispatch($javascript) - { - $this->setFunction('onPreDispatch', $javascript); - } - - /** - * @return string javascript code to be executed before a request is dispatched. - */ - public function getOnPreDispatch() - { - return $this->getOption('onPreDispatch'); - } - - /** - * @return string javascript code for client-side onUninitialized event - */ - public function getOnUninitialized() - { - return $this->getOption('onUninitialized'); - } - - /** - * @param string javascript code for client-side onUninitialized event. - */ - public function setOnUninitialized($javascript) - { - $this->setFunction('onUninitialized', $javascript); - } - - /** - * @return string javascript code for client-side onLoading event - */ - public function getOnLoading() - { - return $this->getOption('onLoading'); - } - - /** - * @param string javascript code for client-side onLoading event. - */ - public function setOnLoading($javascript) - { - $this->setFunction('onLoading', $javascript); - } - - /** - * @return string javascript code for client-side onLoaded event - */ - public function getOnLoaded() - { - return $this->getOption('onLoaded'); - } - - /** - * @param string javascript code for client-side onLoaded event. - */ - public function setOnLoaded($javascript) - { - $this->setFunction('onLoaded', $javascript); - } - /** - * @return string javascript code for client-side onInteractive event - */ - public function getOnInteractive() - { - return $this->getOption('onInteractive'); - } - - /** - * @param string javascript code for client-side onInteractive event. - */ - public function setOnInteractive($javascript) - { - $this->setFunction('onInteractive', $javascript); - } - /** - * @return string javascript code for client-side onComplete event - */ - public function getOnComplete() - { - return $this->getOption('onComplete'); - } - - /** - * @param string javascript code for client-side onComplete event. - */ - public function setOnComplete($javascript) - { - $this->setFunction('onComplete', $javascript); - } - /** - * @return string javascript code for client-side onSuccess event - */ - public function getOnSuccess() - { - return $this->getOption('onSuccess'); - } - - /** - * @param string javascript code for client-side onSuccess event. - */ - public function setOnSuccess($javascript) - { - $this->setFunction('onSuccess', $javascript); - } - - /** - * @return string javascript code for client-side onFailure event - */ - public function getOnFailure() - { - return $this->getOption('onFailure'); - } - - /** - * @param string javascript code for client-side onFailure event. - */ - public function setOnFailure($javascript) - { - $this->setFunction('onFailure', $javascript); - } - - /** - * @return string javascript code for client-side onException event - */ - public function getOnException() - { - return $this->getOption('onException'); - } - - /** - * @param string javascript code for client-side onException event. - */ - public function setOnException($javascript) - { - $this->setFunction('onException', $javascript); - } - - /** - * @return boolean true to post the inputs of the form on callback, default - * is post the inputs on callback. - */ - public function getPostState() - { - return $this->getOption('PostInputs'); - } - - /** - * @param boolean true to post the inputs of the form with callback - * requests. Default is to post the inputs. - */ - public function setPostState($value) - { - $this->setOption('PostInputs', TPropertyValue::ensureBoolean($value)); - } - - /** - * @return integer callback request timeout. - */ - public function getRequestTimeOut() - { - return $this->getOption('RequestTimeOut'); - } - - /** - * @param integer callback request timeout - */ - public function setRequestTimeOut($value) - { - $this->setOption('RequestTimeOut', TPropertyValue::ensureInteger($value)); - } - - /** - * @return boolean true if the callback request has priority and will abort - * existing prioritized request in order to send immediately. It does not - * affect callbacks that are not prioritized. Default is true. - */ - public function getHasPriority() - { - $option = $this->getOption('HasPriority'); - return ($option===null) ? true : $option; - } - - /** - * @param boolean true to ensure that the callback request will be sent - * immediately and will abort existing prioritized requests. It does not - * affect callbacks that are not prioritized. - */ - public function setHasPriority($value) - { - $hasPriority = TPropertyValue::ensureBoolean($value); - $this->setOption('HasPriority', $hasPriority); - if(!$hasPriority) - $this->setEnablePageStateUpdate(false); - } - - /** - * Set to true to enable the callback response to enable the viewstate - * update. This will automatically set HasPrority to true. - * @param boolean true enables the callback response to update the - * viewstate. - */ - public function setEnablePageStateUpdate($value) - { - $enabled = TPropertyValue::ensureBoolean($value); - $this->setOption('EnablePageStateUpdate', $enabled); - if($enabled) - $this->setHasPriority(true); - } - - /** - * @return boolean client-side viewstate will be updated on callback - * response if true. Default is true. - */ - public function getEnablePageStateUpdate() - { - $option = $this->getOption('EnablePageStateUpdate'); - return ($option===null) ? 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) - $value = $value->getUniqueID(); - $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); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackClientSide class. + * + * The following client side events are executing in order if the callback + * request and response are send and received successfuly. + * + * - onPreDispatch executed before a request is dispatched. + * - onUninitialized executed when callback request is uninitialized. + * - onLoading* executed when callback request is initiated + * - onLoaded* executed when callback request begins. + * - onInteractive executed when callback request is in progress. + * - onCompleteexecuted when callback response returns. + * - onSuccess executed when callback request returns and is successful. + * - onFailure executed when callback request returns and fails. + * - onException raised when callback request fails due to request/response errors. + * + * * Note that theses 2 events are not fired correctly by Opera. To make + * them work in this browser, Prado will fire them just after onPreDispatch. + * + * In a general way, onUninitialized, onLoading, onLoaded and onInteractive events + * are not implemented consistently in all browsers.When cross browser compatibility is + * needed, it is best to avoid use them + * + * The OnSuccess and OnFailure events are raised when the + * response is returned. A successful request/response will raise + * OnSuccess event otherwise OnFailure will be raised. + * + * - PostState true to collect the form inputs and post them during callback, default is true. + * - RequestTimeOut The request timeout in milliseconds. + * - HasPriority true to ensure that the callback request will be sent + * immediately and will abort existing prioritized requests. It does not affect + * callbacks that are not prioritized. + * - EnablePageStateUpdate enable the callback response to enable the + * viewstate update. This will automatically set HasPriority to true when enabled. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackClientSide extends TClientSideOptions +{ + /** + * Returns javascript statement enclosed within a javascript function. + * @param string javascript statement + * @return string javascript statement wrapped in a javascript function + */ + protected function ensureFunction($javascript) + { + return "function(sender, parameter){ {$javascript} }"; + } + + /** + * @param string javascript code to be executed before a request is dispatched. + */ + public function setOnPreDispatch($javascript) + { + $this->setFunction('onPreDispatch', $javascript); + } + + /** + * @return string javascript code to be executed before a request is dispatched. + */ + public function getOnPreDispatch() + { + return $this->getOption('onPreDispatch'); + } + + /** + * @return string javascript code for client-side onUninitialized event + */ + public function getOnUninitialized() + { + return $this->getOption('onUninitialized'); + } + + /** + * @param string javascript code for client-side onUninitialized event. + */ + public function setOnUninitialized($javascript) + { + $this->setFunction('onUninitialized', $javascript); + } + + /** + * @return string javascript code for client-side onLoading event + */ + public function getOnLoading() + { + return $this->getOption('onLoading'); + } + + /** + * @param string javascript code for client-side onLoading event. + */ + public function setOnLoading($javascript) + { + $this->setFunction('onLoading', $javascript); + } + + /** + * @return string javascript code for client-side onLoaded event + */ + public function getOnLoaded() + { + return $this->getOption('onLoaded'); + } + + /** + * @param string javascript code for client-side onLoaded event. + */ + public function setOnLoaded($javascript) + { + $this->setFunction('onLoaded', $javascript); + } + /** + * @return string javascript code for client-side onInteractive event + */ + public function getOnInteractive() + { + return $this->getOption('onInteractive'); + } + + /** + * @param string javascript code for client-side onInteractive event. + */ + public function setOnInteractive($javascript) + { + $this->setFunction('onInteractive', $javascript); + } + /** + * @return string javascript code for client-side onComplete event + */ + public function getOnComplete() + { + return $this->getOption('onComplete'); + } + + /** + * @param string javascript code for client-side onComplete event. + */ + public function setOnComplete($javascript) + { + $this->setFunction('onComplete', $javascript); + } + /** + * @return string javascript code for client-side onSuccess event + */ + public function getOnSuccess() + { + return $this->getOption('onSuccess'); + } + + /** + * @param string javascript code for client-side onSuccess event. + */ + public function setOnSuccess($javascript) + { + $this->setFunction('onSuccess', $javascript); + } + + /** + * @return string javascript code for client-side onFailure event + */ + public function getOnFailure() + { + return $this->getOption('onFailure'); + } + + /** + * @param string javascript code for client-side onFailure event. + */ + public function setOnFailure($javascript) + { + $this->setFunction('onFailure', $javascript); + } + + /** + * @return string javascript code for client-side onException event + */ + public function getOnException() + { + return $this->getOption('onException'); + } + + /** + * @param string javascript code for client-side onException event. + */ + public function setOnException($javascript) + { + $this->setFunction('onException', $javascript); + } + + /** + * @return boolean true to post the inputs of the form on callback, default + * is post the inputs on callback. + */ + public function getPostState() + { + return $this->getOption('PostInputs'); + } + + /** + * @param boolean true to post the inputs of the form with callback + * requests. Default is to post the inputs. + */ + public function setPostState($value) + { + $this->setOption('PostInputs', TPropertyValue::ensureBoolean($value)); + } + + /** + * @return integer callback request timeout. + */ + public function getRequestTimeOut() + { + return $this->getOption('RequestTimeOut'); + } + + /** + * @param integer callback request timeout + */ + public function setRequestTimeOut($value) + { + $this->setOption('RequestTimeOut', TPropertyValue::ensureInteger($value)); + } + + /** + * @return boolean true if the callback request has priority and will abort + * existing prioritized request in order to send immediately. It does not + * affect callbacks that are not prioritized. Default is true. + */ + public function getHasPriority() + { + $option = $this->getOption('HasPriority'); + return ($option===null) ? true : $option; + } + + /** + * @param boolean true to ensure that the callback request will be sent + * immediately and will abort existing prioritized requests. It does not + * affect callbacks that are not prioritized. + */ + public function setHasPriority($value) + { + $hasPriority = TPropertyValue::ensureBoolean($value); + $this->setOption('HasPriority', $hasPriority); + if(!$hasPriority) + $this->setEnablePageStateUpdate(false); + } + + /** + * Set to true to enable the callback response to enable the viewstate + * update. This will automatically set HasPrority to true. + * @param boolean true enables the callback response to update the + * viewstate. + */ + public function setEnablePageStateUpdate($value) + { + $enabled = TPropertyValue::ensureBoolean($value); + $this->setOption('EnablePageStateUpdate', $enabled); + if($enabled) + $this->setHasPriority(true); + } + + /** + * @return boolean client-side viewstate will be updated on callback + * response if true. Default is true. + */ + public function getEnablePageStateUpdate() + { + $option = $this->getOption('EnablePageStateUpdate'); + return ($option===null) ? 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) + $value = $value->getUniqueID(); + $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); + } +} + diff --git a/framework/Web/UI/ActiveControls/TCallbackEventParameter.php b/framework/Web/UI/ActiveControls/TCallbackEventParameter.php index d4038fb5..7c532588 100644 --- a/framework/Web/UI/ActiveControls/TCallbackEventParameter.php +++ b/framework/Web/UI/ActiveControls/TCallbackEventParameter.php @@ -1,87 +1,87 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * TCallbackEventParameter class. - * - * The TCallbackEventParameter provides the parameter passed during the callback - * request in the {@link getCallbackParameter CallbackParameter} property. The - * callback response content (e.g. new HTML content) must be rendered - * using an THtmlWriter obtained from the {@link getNewWriter NewWriter} - * property, which returns a NEW instance of TCallbackResponseWriter. - * - * Each instance TCallbackResponseWriter is associated with a unique - * boundary delimited. By default each panel only renders its own content. - * To replace the content of ONE panel with that of rendered from multiple panels - * use the same writer instance for the panels to be rendered. - * - * The response data (i.e., passing results back to the client-side - * callback handler function) can be set using {@link setResponseData ResponseData} property. - * - * @author Wei Zhuo - * @version $Id: TActivePageAdapter.php 1648 2007-01-24 05:52:22Z wei $ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackEventParameter extends TEventParameter -{ - /** - * @var THttpResponse output content. - */ - private $_response; - /** - * @var mixed callback request parameter. - */ - private $_parameter; - - /** - * Creates a new TCallbackEventParameter. - */ - public function __construct($response, $parameter) - { - $this->_response = $response; - $this->_parameter = $parameter; - } - - /** - * @return TCallbackResponseWriter holds the response content. - */ - public function getNewWriter() - { - return $this->_response->createHtmlWriter(null); - } - - /** - * @return mixed callback request parameter. - */ - public function getCallbackParameter() - { - return $this->_parameter; - } - - /** - * @param mixed callback response data. - */ - public function setResponseData($value) - { - $this->_response->getAdapter()->setResponseData($value); - } - - /** - * @return mixed callback response data. - */ - public function getResponseData() - { - return $this->_response->getAdapter()->getResponseData(); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackEventParameter class. + * + * The TCallbackEventParameter provides the parameter passed during the callback + * request in the {@link getCallbackParameter CallbackParameter} property. The + * callback response content (e.g. new HTML content) must be rendered + * using an THtmlWriter obtained from the {@link getNewWriter NewWriter} + * property, which returns a NEW instance of TCallbackResponseWriter. + * + * Each instance TCallbackResponseWriter is associated with a unique + * boundary delimited. By default each panel only renders its own content. + * To replace the content of ONE panel with that of rendered from multiple panels + * use the same writer instance for the panels to be rendered. + * + * The response data (i.e., passing results back to the client-side + * callback handler function) can be set using {@link setResponseData ResponseData} property. + * + * @author Wei Zhuo + * @version $Id: TActivePageAdapter.php 1648 2007-01-24 05:52:22Z wei $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackEventParameter extends TEventParameter +{ + /** + * @var THttpResponse output content. + */ + private $_response; + /** + * @var mixed callback request parameter. + */ + private $_parameter; + + /** + * Creates a new TCallbackEventParameter. + */ + public function __construct($response, $parameter) + { + $this->_response = $response; + $this->_parameter = $parameter; + } + + /** + * @return TCallbackResponseWriter holds the response content. + */ + public function getNewWriter() + { + return $this->_response->createHtmlWriter(null); + } + + /** + * @return mixed callback request parameter. + */ + public function getCallbackParameter() + { + return $this->_parameter; + } + + /** + * @param mixed callback response data. + */ + public function setResponseData($value) + { + $this->_response->getAdapter()->setResponseData($value); + } + + /** + * @return mixed callback response data. + */ + public function getResponseData() + { + return $this->_response->getAdapter()->getResponseData(); + } +} + diff --git a/framework/Web/UI/ActiveControls/TCallbackOptions.php b/framework/Web/UI/ActiveControls/TCallbackOptions.php index baba32e1..e75c2fde 100644 --- a/framework/Web/UI/ActiveControls/TCallbackOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackOptions.php @@ -1,53 +1,53 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @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 - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackOptions extends TControl -{ - /** - * @var TCallbackClientSide client side callback options. - */ - private $_clientSide; - - /** - * Callback client-side options can be set by setting the properties of - * the ClientSide property. E.g. - * See {@link TCallbackClientSide} for details on the properties of - * ClientSide. - * @return TCallbackClientSide client-side callback options. - */ - public function getClientSide() - { - if($this->_clientSide===null) - $this->_clientSide = $this->createClientSide(); - return $this->_clientSide; - } - - /** - * @return TCallbackClientSide callback client-side options. - */ - protected function createClientSide() - { - return Prado::createComponent('System.Web.UI.ActiveControls.TCallbackClientSide'); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @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 + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackOptions extends TControl +{ + /** + * @var TCallbackClientSide client side callback options. + */ + private $_clientSide; + + /** + * Callback client-side options can be set by setting the properties of + * the ClientSide property. E.g. + * See {@link TCallbackClientSide} for details on the properties of + * ClientSide. + * @return TCallbackClientSide client-side callback options. + */ + public function getClientSide() + { + if($this->_clientSide===null) + $this->_clientSide = $this->createClientSide(); + return $this->_clientSide; + } + + /** + * @return TCallbackClientSide callback client-side options. + */ + protected function createClientSide() + { + return Prado::createComponent('System.Web.UI.ActiveControls.TCallbackClientSide'); + } +} + diff --git a/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php b/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php index c28b435c..38aaabec 100644 --- a/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php +++ b/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php @@ -1,95 +1,95 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); - -/** - * TEventTriggeredCallback Class - * - * Triggers a new callback request when a particular {@link setEventName EventName} - * on a control with ID given by {@link setControlID ControlID} is raised. - * - * The default action of the event on the client-side can be prevented when - * {@link setPreventDefaultAction PreventDefaultAction} is set to true. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TEventTriggeredCallback extends TTriggeredCallback -{ - /** - * @return string The client-side event name the trigger listens to. - */ - public function getEventName() - { - return $this->getViewState('EventName', ''); - } - - /** - * Sets the client-side event name that fires the callback request. - * @param string The client-side event name the trigger listens to. - */ - public function setEventName($value) - { - $this->setViewState('EventName', $value, ''); - } - - /** - * @param boolean true to prevent/stop default event action. - */ - public function setPreventDefaultAction($value) - { - $this->setViewState('StopEvent', TPropertyValue::ensureBoolean($value), false); - } - - /** - * @return boolean true to prevent/stop default event action. - */ - public function getPreventDefaultAction() - { - return $this->getViewState('StopEvent', false); - } - - /** - * @return array list of timer options for client-side. - */ - protected function getTriggerOptions() - { - $options = parent::getTriggerOptions(); - $name = preg_replace('/^on/', '', $this->getEventName()); - $options['EventName'] = strtolower($name); - $options['StopEvent'] = $this->getPreventDefaultAction(); - return $options; - } - - /** - * Registers the javascript code for initializing the active control. - * @param THtmlWriter the renderer. - */ - public function render($writer) - { - parent::render($writer); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getTriggerOptions()); - } - - /** - * @return string corresponding javascript class name for TEventTriggeredCallback. - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TEventTriggeredCallback'; - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); + +/** + * TEventTriggeredCallback Class + * + * Triggers a new callback request when a particular {@link setEventName EventName} + * on a control with ID given by {@link setControlID ControlID} is raised. + * + * The default action of the event on the client-side can be prevented when + * {@link setPreventDefaultAction PreventDefaultAction} is set to true. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TEventTriggeredCallback extends TTriggeredCallback +{ + /** + * @return string The client-side event name the trigger listens to. + */ + public function getEventName() + { + return $this->getViewState('EventName', ''); + } + + /** + * Sets the client-side event name that fires the callback request. + * @param string The client-side event name the trigger listens to. + */ + public function setEventName($value) + { + $this->setViewState('EventName', $value, ''); + } + + /** + * @param boolean true to prevent/stop default event action. + */ + public function setPreventDefaultAction($value) + { + $this->setViewState('StopEvent', TPropertyValue::ensureBoolean($value), false); + } + + /** + * @return boolean true to prevent/stop default event action. + */ + public function getPreventDefaultAction() + { + return $this->getViewState('StopEvent', false); + } + + /** + * @return array list of timer options for client-side. + */ + protected function getTriggerOptions() + { + $options = parent::getTriggerOptions(); + $name = preg_replace('/^on/', '', $this->getEventName()); + $options['EventName'] = strtolower($name); + $options['StopEvent'] = $this->getPreventDefaultAction(); + return $options; + } + + /** + * Registers the javascript code for initializing the active control. + * @param THtmlWriter the renderer. + */ + public function render($writer) + { + parent::render($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getTriggerOptions()); + } + + /** + * @return string corresponding javascript class name for TEventTriggeredCallback. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TEventTriggeredCallback'; + } +} + diff --git a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php index aba477b9..47c8aeac 100644 --- a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php +++ b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php @@ -1,290 +1,290 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); - -/** - * TInPlaceTextBox Class - * * - * TInPlaceTextBox is a component rendered as a label and allows its - * contents to be edited by changing the label to a textbox when - * the label is clicked or when another control or html element with - * ID given by {@link setEditTriggerControlID EditTriggerControlID} is clicked. - * - * If the {@link OnLoadingText} event is handled, a callback request is - * made when the label is clicked, while the request is being made the - * textbox is disabled from editing. The {@link OnLoadingText} event allows - * you to update the content of the textbox before the client is allowed - * to edit the content. After the callback request returns successfully, - * the textbox is enabled and the contents is then allowed to be edited. - * - * Once the textbox loses focus, if {@link setAutoPostBack AutoPostBack} - * is true and the textbox content has changed, a callback request is made and - * the {@link OnTextChanged} event is raised like that of the TActiveTextBox. - * During the request, the textbox is disabled. - * - * After the callback request returns sucessfully, the textbox is enabled. - * If the {@link setAutoHideTextBox AutoHideTextBox} property is true, then - * the textbox will be hidden and the label is then shown. - * - * Since 3.1.2, you can set the {@link setReadOnly ReadOnly} property to make - * the control not editable. This property can be also changed on callback - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TInPlaceTextBox extends TActiveTextBox -{ - /** - * Sets the auto post back to true by default. - */ - public function __construct() - { - parent::__construct(); - $this->setAutoPostBack(true); - } - - /** - * @param boolean true to hide the textbox after losing focus. - */ - public function setAutoHideTextBox($value) - { - $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); - } - - /** - * @return boolean true will hide the textbox after losing focus. - */ - public function getAutoHideTextBox() - { - return $this->getViewState('AutoHide', true); - } - - /** - * @param boolean true to display the edit textbox - */ - public function setDisplayTextBox($value) - { - $value = TPropertyValue::ensureBoolean($value); - $this->setViewState('DisplayTextBox', $value,false); - if($this->getActiveControl()->canUpdateClientSide()) - $this->callClientFunction('setDisplayTextBox',$value); - } - - /** - * @return boolean true to display the edit textbox - */ - public function getDisplayTextBox() - { - return $this->getViewState('DisplayTextBox', false); - } - - /** - * Calls the client-side static method for this control class. - * @param string static method name - * @param mixed method parmaeter - */ - protected function callClientFunction($func,$value) - { - $client = $this->getPage()->getCallbackClient(); - $code = $this->getClientClassName().'.'.$func; - $client->callClientFunction($code,array($this,$value)); - } - - /** - * @param string ID of the control that can trigger to edit the textbox - */ - public function setEditTriggerControlID($value) - { - $this->setViewState('EditTriggerControlID', $value); - } - - /** - * @return string ID of the control that can trigger to edit the textbox - */ - public function getEditTriggerControlID() - { - return $this->getViewState('EditTriggerControlID'); - } - - /** - * @return string edit trigger control client ID. - */ - protected function getExternalControlID() - { - $extID = $this->getEditTriggerControlID(); - if($extID===null) return ''; - if(($control = $this->findControl($extID))!==null) - return $control->getClientID(); - return $extID; - } - - /** - * On callback response, the inner HTMl of the label and the - * value of the textbox is updated - * @param string the text value of the label - */ - public function setText($value) - { - TTextBox::setText($value); - if($this->getActiveControl()->canUpdateClientSide()) - { - $client = $this->getPage()->getCallbackClient(); - $client->update($this->getLabelClientID(), $value); - $client->setValue($this, $value); - } - } - - /** - * Update ClientSide Readonly property - * @param boolean value - * @since 3.1.2 - */ - public function setReadOnly ($value) - { - $value=TPropertyValue::ensureBoolean($value); - TTextBox::setReadOnly($value); - if ($this->getActiveControl()->canUpdateClientSide()) - { - $this->callClientFunction('setReadOnly', $value); - } - } - - /** - * @return string tag name of the label. - */ - protected function getTagName() - { - return 'span'; - } - - /** - * Renders the body content of the label. - * @param THtmlWriter the writer for rendering - */ - public function renderContents($writer) - { - if(($text=$this->getText())==='') - parent::renderContents($writer); - else - $writer->write($text); - } - - /** - * @return string label client ID - */ - protected function getLabelClientID() - { - return $this->getClientID().'__label'; - } - - /** - * 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) - { - $action = $param->getCallbackParameter(); - if(is_array($action) && $action[0] === '__InlineEditor_loadExternalText__') - { - $parameter = new TCallbackEventParameter($this->getResponse(), $action[1]); - $this->onLoadingText($parameter); - } - $this->raiseEvent('OnCallback', $this, $param); - } - - /** - * @return array callback options. - */ - protected function getPostBackOptions() - { - $options = parent::getPostBackOptions(); - $options['ID'] = $this->getLabelClientID(); - $options['TextBoxID'] = $this->getClientID(); - $options['ExternalControl'] = $this->getExternalControlID(); - $options['AutoHide'] = $this->getAutoHideTextBox() == false ? '' : true; - $options['AutoPostBack'] = $this->getAutoPostBack() == false ? '' : true; - $options['Columns'] = $this->getColumns(); - if($this->getTextMode()==='MultiLine') - { - $options['Rows'] = $this->getRows(); - $options['Wrap'] = $this->getWrap()== false ? '' : true; - } - else - { - $length = $this->getMaxLength(); - $options['MaxLength'] = $length > 0 ? $length : ''; - } - - if($this->hasEventHandler('OnLoadingText')) - $options['LoadTextOnEdit'] = true; - - $options['ReadOnly']=$this->getReadOnly(); - return $options; - } - - /** - * Raised when editing the content is requsted to be loaded from the - * server side. - * @param TCallbackEventParameter event parameter to be passed to the event handlers - */ - public function onLoadingText($param) - { - $this->raiseEvent('OnLoadingText',$this,$param); - } - - /** - * @return string corresponding javascript class name for this TInPlaceTextBox - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TInPlaceTextBox'; - } - - /** - * Ensure that the ID attribute is rendered and registers the javascript code - * for initializing the active control. - */ - protected function addAttributesToRender($writer) - { - //calls the TWebControl to avoid rendering other attribute normally render for a textbox. - TWebControl::addAttributesToRender($writer); - $writer->addAttribute('id',$this->getLabelClientID()); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); - } - - /** - * Registers CSS and JS. - * This method is invoked right before the control rendering, if the control is visible. - * @param mixed event parameter - */ - public function onPreRender($param) - { - parent::onPreRender($param); - $this->registerClientScript(); - } - - /** - * Registers the relevant JavaScript. - */ - protected function registerClientScript() - { - $cs=$this->getPage()->getClientScript(); - $cs->registerPradoScript('inlineeditor'); - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); + +/** + * TInPlaceTextBox Class + * * + * TInPlaceTextBox is a component rendered as a label and allows its + * contents to be edited by changing the label to a textbox when + * the label is clicked or when another control or html element with + * ID given by {@link setEditTriggerControlID EditTriggerControlID} is clicked. + * + * If the {@link OnLoadingText} event is handled, a callback request is + * made when the label is clicked, while the request is being made the + * textbox is disabled from editing. The {@link OnLoadingText} event allows + * you to update the content of the textbox before the client is allowed + * to edit the content. After the callback request returns successfully, + * the textbox is enabled and the contents is then allowed to be edited. + * + * Once the textbox loses focus, if {@link setAutoPostBack AutoPostBack} + * is true and the textbox content has changed, a callback request is made and + * the {@link OnTextChanged} event is raised like that of the TActiveTextBox. + * During the request, the textbox is disabled. + * + * After the callback request returns sucessfully, the textbox is enabled. + * If the {@link setAutoHideTextBox AutoHideTextBox} property is true, then + * the textbox will be hidden and the label is then shown. + * + * Since 3.1.2, you can set the {@link setReadOnly ReadOnly} property to make + * the control not editable. This property can be also changed on callback + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TInPlaceTextBox extends TActiveTextBox +{ + /** + * Sets the auto post back to true by default. + */ + public function __construct() + { + parent::__construct(); + $this->setAutoPostBack(true); + } + + /** + * @param boolean true to hide the textbox after losing focus. + */ + public function setAutoHideTextBox($value) + { + $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return boolean true will hide the textbox after losing focus. + */ + public function getAutoHideTextBox() + { + return $this->getViewState('AutoHide', true); + } + + /** + * @param boolean true to display the edit textbox + */ + public function setDisplayTextBox($value) + { + $value = TPropertyValue::ensureBoolean($value); + $this->setViewState('DisplayTextBox', $value,false); + if($this->getActiveControl()->canUpdateClientSide()) + $this->callClientFunction('setDisplayTextBox',$value); + } + + /** + * @return boolean true to display the edit textbox + */ + public function getDisplayTextBox() + { + return $this->getViewState('DisplayTextBox', false); + } + + /** + * Calls the client-side static method for this control class. + * @param string static method name + * @param mixed method parmaeter + */ + protected function callClientFunction($func,$value) + { + $client = $this->getPage()->getCallbackClient(); + $code = $this->getClientClassName().'.'.$func; + $client->callClientFunction($code,array($this,$value)); + } + + /** + * @param string ID of the control that can trigger to edit the textbox + */ + public function setEditTriggerControlID($value) + { + $this->setViewState('EditTriggerControlID', $value); + } + + /** + * @return string ID of the control that can trigger to edit the textbox + */ + public function getEditTriggerControlID() + { + return $this->getViewState('EditTriggerControlID'); + } + + /** + * @return string edit trigger control client ID. + */ + protected function getExternalControlID() + { + $extID = $this->getEditTriggerControlID(); + if($extID===null) return ''; + if(($control = $this->findControl($extID))!==null) + return $control->getClientID(); + return $extID; + } + + /** + * On callback response, the inner HTMl of the label and the + * value of the textbox is updated + * @param string the text value of the label + */ + public function setText($value) + { + TTextBox::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $client->update($this->getLabelClientID(), $value); + $client->setValue($this, $value); + } + } + + /** + * Update ClientSide Readonly property + * @param boolean value + * @since 3.1.2 + */ + public function setReadOnly ($value) + { + $value=TPropertyValue::ensureBoolean($value); + TTextBox::setReadOnly($value); + if ($this->getActiveControl()->canUpdateClientSide()) + { + $this->callClientFunction('setReadOnly', $value); + } + } + + /** + * @return string tag name of the label. + */ + protected function getTagName() + { + return 'span'; + } + + /** + * Renders the body content of the label. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string label client ID + */ + protected function getLabelClientID() + { + return $this->getClientID().'__label'; + } + + /** + * 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) + { + $action = $param->getCallbackParameter(); + if(is_array($action) && $action[0] === '__InlineEditor_loadExternalText__') + { + $parameter = new TCallbackEventParameter($this->getResponse(), $action[1]); + $this->onLoadingText($parameter); + } + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * @return array callback options. + */ + protected function getPostBackOptions() + { + $options = parent::getPostBackOptions(); + $options['ID'] = $this->getLabelClientID(); + $options['TextBoxID'] = $this->getClientID(); + $options['ExternalControl'] = $this->getExternalControlID(); + $options['AutoHide'] = $this->getAutoHideTextBox() == false ? '' : true; + $options['AutoPostBack'] = $this->getAutoPostBack() == false ? '' : true; + $options['Columns'] = $this->getColumns(); + if($this->getTextMode()==='MultiLine') + { + $options['Rows'] = $this->getRows(); + $options['Wrap'] = $this->getWrap()== false ? '' : true; + } + else + { + $length = $this->getMaxLength(); + $options['MaxLength'] = $length > 0 ? $length : ''; + } + + if($this->hasEventHandler('OnLoadingText')) + $options['LoadTextOnEdit'] = true; + + $options['ReadOnly']=$this->getReadOnly(); + return $options; + } + + /** + * Raised when editing the content is requsted to be loaded from the + * server side. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onLoadingText($param) + { + $this->raiseEvent('OnLoadingText',$this,$param); + } + + /** + * @return string corresponding javascript class name for this TInPlaceTextBox + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TInPlaceTextBox'; + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + //calls the TWebControl to avoid rendering other attribute normally render for a textbox. + TWebControl::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getLabelClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->registerClientScript(); + } + + /** + * Registers the relevant JavaScript. + */ + protected function registerClientScript() + { + $cs=$this->getPage()->getClientScript(); + $cs->registerPradoScript('inlineeditor'); + } +} diff --git a/framework/Web/UI/ActiveControls/TTriggeredCallback.php b/framework/Web/UI/ActiveControls/TTriggeredCallback.php index fd1fabb1..4b89cb8e 100644 --- a/framework/Web/UI/ActiveControls/TTriggeredCallback.php +++ b/framework/Web/UI/ActiveControls/TTriggeredCallback.php @@ -1,71 +1,71 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TCallback'); -/** - * TTriggeredCallback abstract Class - * - * Base class for triggered callback controls. The {@link setControlID ControlID} - * property sets the control ID to observe the trigger. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -abstract class TTriggeredCallback extends TCallback -{ - /** - * @return string The ID of the server control the trigger is bounded to. - */ - public function getControlID() - { - return $this->getViewState('ControlID', ''); - } - - /** - * @param string The ID of the server control the trigger is bounded to. - */ - public function setControlID($value) - { - $this->setViewState('ControlID', $value, ''); - } - - /** - * @return string target control client ID or html element ID if - * control is not found in hierarchy. - */ - protected function getTargetControl() - { - $id = $this->getControlID(); - if(($control=$this->findControl($id)) instanceof TControl) - return $control->getClientID(); - if($id==='') - { - throw new TConfigurationException( - 'ttriggeredcallback_invalid_controlid', get_class($this)); - } - return $id; - } - - /** - * @return array list of trigger callback options. - */ - protected function getTriggerOptions() - { - $options['ID'] = $this->getClientID(); - $options['EventTarget'] = $this->getUniqueID(); - $options['ControlID'] = $this->getTargetControl(); - return $options; - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallback'); +/** + * TTriggeredCallback abstract Class + * + * Base class for triggered callback controls. The {@link setControlID ControlID} + * property sets the control ID to observe the trigger. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +abstract class TTriggeredCallback extends TCallback +{ + /** + * @return string The ID of the server control the trigger is bounded to. + */ + public function getControlID() + { + return $this->getViewState('ControlID', ''); + } + + /** + * @param string The ID of the server control the trigger is bounded to. + */ + public function setControlID($value) + { + $this->setViewState('ControlID', $value, ''); + } + + /** + * @return string target control client ID or html element ID if + * control is not found in hierarchy. + */ + protected function getTargetControl() + { + $id = $this->getControlID(); + if(($control=$this->findControl($id)) instanceof TControl) + return $control->getClientID(); + if($id==='') + { + throw new TConfigurationException( + 'ttriggeredcallback_invalid_controlid', get_class($this)); + } + return $id; + } + + /** + * @return array list of trigger callback options. + */ + protected function getTriggerOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['ControlID'] = $this->getTargetControl(); + return $options; + } +} + diff --git a/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php b/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php index 58a78d08..899419ee 100644 --- a/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php +++ b/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php @@ -1,120 +1,120 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); - -/** - * TValueTriggeredCallback Class - * - * Observes the value with {@link setPropertyName PropertyName} of a - * control with {@link setControlID ControlID}. Changes to the observed - * property value will trigger a new callback request. The property value is checked - * for changes every{@link setInterval Interval} seconds. - * - * A {@link setDecayRate DecayRate} can be set to increase the polling - * interval linearly if no changes are observed. Once a change is - * observed, the polling interval is reset to the original value. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TValueTriggeredCallback extends TTriggeredCallback -{ - /** - * @return string The control property name to observe value changes. - */ - public function getPropertyName() - { - return $this->getViewState('PropertyName', ''); - } - - /** - * Sets the control property name to observe value changes that fires the callback request. - * @param string The control property name to observe value changes. - */ - public function setPropertyName($value) - { - $this->setViewState('PropertyName', $value, ''); - } - - /** - * Sets the polling interval in seconds to observe property changes. - * Default is 1 second. - * @param float polling interval in seconds. - */ - public function setInterval($value) - { - $this->setViewState('Interval', TPropertyValue::ensureFloat($value), 1); - } - - /** - * @return float polling interval, 1 second default. - */ - public function getInterval() - { - return $this->getViewState('Interval', 1); - } - - /** - * Gets the decay rate between callbacks. Default is 0; - * @return float decay rate between callbacks. - */ - public function getDecayRate() - { - return $this->getViewState('Decay', 0); - } - - /** - * Sets the decay rate between callback. Default is 0; - * @param float decay rate between callbacks. - */ - public function setDecayRate($value) - { - $decay = TPropertyValue::ensureFloat($value); - if($decay < 0) - throw new TConfigurationException('callback_decay_be_not_negative', $this->getID()); - $this->setViewState('Decay', $decay); - } - - /** - * @return array list of timer options for client-side. - */ - protected function getTriggerOptions() - { - $options = parent::getTriggerOptions(); - $options['PropertyName'] = $this->getPropertyName(); - $options['Interval'] = $this->getInterval(); - $options['Decay'] = $this->getDecayRate(); - return $options; - } - - /** - * Registers the javascript code for initializing the active control. - * @param THtmlWriter the renderer. - */ - public function render($writer) - { - parent::render($writer); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getTriggerOptions()); - } - - /** - * @return string corresponding javascript class name for TEventTriggeredCallback. - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TValueTriggeredCallback'; - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); + +/** + * TValueTriggeredCallback Class + * + * Observes the value with {@link setPropertyName PropertyName} of a + * control with {@link setControlID ControlID}. Changes to the observed + * property value will trigger a new callback request. The property value is checked + * for changes every{@link setInterval Interval} seconds. + * + * A {@link setDecayRate DecayRate} can be set to increase the polling + * interval linearly if no changes are observed. Once a change is + * observed, the polling interval is reset to the original value. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TValueTriggeredCallback extends TTriggeredCallback +{ + /** + * @return string The control property name to observe value changes. + */ + public function getPropertyName() + { + return $this->getViewState('PropertyName', ''); + } + + /** + * Sets the control property name to observe value changes that fires the callback request. + * @param string The control property name to observe value changes. + */ + public function setPropertyName($value) + { + $this->setViewState('PropertyName', $value, ''); + } + + /** + * Sets the polling interval in seconds to observe property changes. + * Default is 1 second. + * @param float polling interval in seconds. + */ + public function setInterval($value) + { + $this->setViewState('Interval', TPropertyValue::ensureFloat($value), 1); + } + + /** + * @return float polling interval, 1 second default. + */ + public function getInterval() + { + return $this->getViewState('Interval', 1); + } + + /** + * Gets the decay rate between callbacks. Default is 0; + * @return float decay rate between callbacks. + */ + public function getDecayRate() + { + return $this->getViewState('Decay', 0); + } + + /** + * Sets the decay rate between callback. Default is 0; + * @param float decay rate between callbacks. + */ + public function setDecayRate($value) + { + $decay = TPropertyValue::ensureFloat($value); + if($decay < 0) + throw new TConfigurationException('callback_decay_be_not_negative', $this->getID()); + $this->setViewState('Decay', $decay); + } + + /** + * @return array list of timer options for client-side. + */ + protected function getTriggerOptions() + { + $options = parent::getTriggerOptions(); + $options['PropertyName'] = $this->getPropertyName(); + $options['Interval'] = $this->getInterval(); + $options['Decay'] = $this->getDecayRate(); + return $options; + } + + /** + * Registers the javascript code for initializing the active control. + * @param THtmlWriter the renderer. + */ + public function render($writer) + { + parent::render($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getTriggerOptions()); + } + + /** + * @return string corresponding javascript class name for TEventTriggeredCallback. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TValueTriggeredCallback'; + } +} diff --git a/framework/Web/UI/TCachePageStatePersister.php b/framework/Web/UI/TCachePageStatePersister.php index 486c87ca..34b9203a 100644 --- a/framework/Web/UI/TCachePageStatePersister.php +++ b/framework/Web/UI/TCachePageStatePersister.php @@ -1,200 +1,200 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TCachePageStatePersister class - * - * TCachePageStatePersister implements a page state persistent method based on cache. - * Page state are stored in cache (e.g. memcache, DB cache, etc.), and only a small token - * is passed to the client side to identify the state. This greatly reduces the size of - * the page state that needs to be transmitted between the server and the client. Of course, - * this is at the cost of using server side resource. - * - * A cache module has to be loaded in order to use TCachePageStatePersister. - * By default, TCachePageStatePersister will use the primary cache module. - * A non-primary cache module can be used by setting {@link setCacheModuleID CacheModuleID}. - * Any cache module, as long as it implements the interface {@link ICache}, may be used. - * For example, one can use {@link TDbCache}, {@link TMemCache}, {@link TAPCCache}, etc. - * - * TCachePageStatePersister uses {@link setCacheTimeout CacheTimeout} to limit the data - * that stores in cache. - * - * Since server resource is often limited, be cautious if you plan to use TCachePageStatePersister - * for high-traffic Web pages. You may consider using a small {@link setCacheTimeout CacheTimeout}. - * - * There are a couple of ways to use TCachePageStatePersister. - * One can override the page's {@link TPage::getStatePersister()} method and - * create a TCachePageStatePersister instance there. - * Or one can configure the pages to use TCachePageStatePersister in page configurations - * as follows, - * - * - * - * Note in the above, we use StatePersister.CacheModuleID to configure the cache module ID - * for the TCachePageStatePersister instance. - * - * The above configuration will affect the pages under the directory containing - * this configuration and all its subdirectories. - * To configure individual pages to use TCachePageStatePersister, use - * - * - * - * - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.1.1 - */ -class TCachePageStatePersister extends TComponent implements IPageStatePersister -{ - private $_prefix='statepersister'; - private $_page; - private $_cache=null; - private $_cacheModuleID=''; - private $_timeout=1800; - - /** - * @param TPage the page that this persister works for - */ - public function getPage() - { - return $this->_page; - } - - /** - * @param TPage the page that this persister works for. - */ - public function setPage(TPage $page) - { - $this->_page=$page; - } - - /** - * @return string the ID of the cache module. - */ - public function getCacheModuleID() - { - return $this->_cacheModuleID; - } - - /** - * @param string the ID of the cache module. If not set, the primary cache module will be used. - */ - public function setCacheModuleID($value) - { - $this->_cacheModuleID=$value; - } - - /** - * @return ICache the cache module being used for data storage - */ - public function getCache() - { - if($this->_cache===null) - { - if($this->_cacheModuleID!=='') - $cache=Prado::getApplication()->getModule($this->_cacheModuleID); - else - $cache=Prado::getApplication()->getCache(); - if($cache===null || !($cache instanceof ICache)) - { - if($this->_cacheModuleID!=='') - throw new TConfigurationException('cachepagestatepersister_cachemoduleid_invalid',$this->_cacheModuleID); - else - throw new TConfigurationException('cachepagestatepersister_cache_required'); - } - $this->_cache=$cache; - } - return $this->_cache; - } - - /** - * @return integer the number of seconds in which the cached state will expire. Defaults to 1800. - */ - public function getCacheTimeout() - { - return $this->_timeout; - } - - /** - * @param integer the number of seconds in which the cached state will expire. 0 means never expire. - * @throws TInvalidDataValueException if the number is smaller than 0. - */ - public function setCacheTimeout($value) - { - if(($value=TPropertyValue::ensureInteger($value))>=0) - $this->_timeout=$value; - else - throw new TInvalidDataValueException('cachepagestatepersister_timeout_invalid'); - } - - /** - * @return string prefix of cache variable name to avoid conflict with other cache data. Defaults to 'statepersister'. - */ - public function getKeyPrefix() - { - return $this->_prefix; - } - - /** - * @param string prefix of cache variable name to avoid conflict with other cache data - */ - public function setKeyPrefix($value) - { - $this->_prefix=$value; - } - - /** - * @param string micro timestamp when saving state occurs - * @return string a key that is unique per user request - */ - protected function calculateKey($timestamp) - { - return $this->getKeyPrefix().':' - . $this->_page->getRequest()->getUserHostAddress() - . $this->_page->getPagePath() - . $timestamp; - } - - /** - * Saves state in cache. - * @param mixed state to be stored - */ - public function save($data) - { - $timestamp=(string)microtime(true); - $key=$this->calculateKey($timestamp); - $this->getCache()->add($key,$data,$this->_timeout); - $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); - } - - /** - * Loads page state from cache. - * @return mixed the restored state - * @throws THttpException if page state is corrupted - */ - public function load() - { - if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) - { - $key=$this->calculateKey($timestamp); - if(($data=$this->getCache()->get($key))!==false) - return $data; - } - throw new THttpException(400,'cachepagestatepersister_pagestate_corrupted'); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TCachePageStatePersister class + * + * TCachePageStatePersister implements a page state persistent method based on cache. + * Page state are stored in cache (e.g. memcache, DB cache, etc.), and only a small token + * is passed to the client side to identify the state. This greatly reduces the size of + * the page state that needs to be transmitted between the server and the client. Of course, + * this is at the cost of using server side resource. + * + * A cache module has to be loaded in order to use TCachePageStatePersister. + * By default, TCachePageStatePersister will use the primary cache module. + * A non-primary cache module can be used by setting {@link setCacheModuleID CacheModuleID}. + * Any cache module, as long as it implements the interface {@link ICache}, may be used. + * For example, one can use {@link TDbCache}, {@link TMemCache}, {@link TAPCCache}, etc. + * + * TCachePageStatePersister uses {@link setCacheTimeout CacheTimeout} to limit the data + * that stores in cache. + * + * Since server resource is often limited, be cautious if you plan to use TCachePageStatePersister + * for high-traffic Web pages. You may consider using a small {@link setCacheTimeout CacheTimeout}. + * + * There are a couple of ways to use TCachePageStatePersister. + * One can override the page's {@link TPage::getStatePersister()} method and + * create a TCachePageStatePersister instance there. + * Or one can configure the pages to use TCachePageStatePersister in page configurations + * as follows, + * + * + * + * Note in the above, we use StatePersister.CacheModuleID to configure the cache module ID + * for the TCachePageStatePersister instance. + * + * The above configuration will affect the pages under the directory containing + * this configuration and all its subdirectories. + * To configure individual pages to use TCachePageStatePersister, use + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.1.1 + */ +class TCachePageStatePersister extends TComponent implements IPageStatePersister +{ + private $_prefix='statepersister'; + private $_page; + private $_cache=null; + private $_cacheModuleID=''; + private $_timeout=1800; + + /** + * @param TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for. + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * @return string the ID of the cache module. + */ + public function getCacheModuleID() + { + return $this->_cacheModuleID; + } + + /** + * @param string the ID of the cache module. If not set, the primary cache module will be used. + */ + public function setCacheModuleID($value) + { + $this->_cacheModuleID=$value; + } + + /** + * @return ICache the cache module being used for data storage + */ + public function getCache() + { + if($this->_cache===null) + { + if($this->_cacheModuleID!=='') + $cache=Prado::getApplication()->getModule($this->_cacheModuleID); + else + $cache=Prado::getApplication()->getCache(); + if($cache===null || !($cache instanceof ICache)) + { + if($this->_cacheModuleID!=='') + throw new TConfigurationException('cachepagestatepersister_cachemoduleid_invalid',$this->_cacheModuleID); + else + throw new TConfigurationException('cachepagestatepersister_cache_required'); + } + $this->_cache=$cache; + } + return $this->_cache; + } + + /** + * @return integer the number of seconds in which the cached state will expire. Defaults to 1800. + */ + public function getCacheTimeout() + { + return $this->_timeout; + } + + /** + * @param integer the number of seconds in which the cached state will expire. 0 means never expire. + * @throws TInvalidDataValueException if the number is smaller than 0. + */ + public function setCacheTimeout($value) + { + if(($value=TPropertyValue::ensureInteger($value))>=0) + $this->_timeout=$value; + else + throw new TInvalidDataValueException('cachepagestatepersister_timeout_invalid'); + } + + /** + * @return string prefix of cache variable name to avoid conflict with other cache data. Defaults to 'statepersister'. + */ + public function getKeyPrefix() + { + return $this->_prefix; + } + + /** + * @param string prefix of cache variable name to avoid conflict with other cache data + */ + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + + /** + * @param string micro timestamp when saving state occurs + * @return string a key that is unique per user request + */ + protected function calculateKey($timestamp) + { + return $this->getKeyPrefix().':' + . $this->_page->getRequest()->getUserHostAddress() + . $this->_page->getPagePath() + . $timestamp; + } + + /** + * Saves state in cache. + * @param mixed state to be stored + */ + public function save($data) + { + $timestamp=(string)microtime(true); + $key=$this->calculateKey($timestamp); + $this->getCache()->add($key,$data,$this->_timeout); + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); + } + + /** + * Loads page state from cache. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + { + $key=$this->calculateKey($timestamp); + if(($data=$this->getCache()->get($key))!==false) + return $data; + } + throw new THttpException(400,'cachepagestatepersister_pagestate_corrupted'); + } +} + diff --git a/framework/Web/UI/TCompositeControl.php b/framework/Web/UI/TCompositeControl.php index 285a8129..e7766ab8 100644 --- a/framework/Web/UI/TCompositeControl.php +++ b/framework/Web/UI/TCompositeControl.php @@ -1,38 +1,38 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TCompositeControl class. - * TCompositeControl is the base class for controls that are composed - * by other controls. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TCompositeControl extends TControl implements INamingContainer -{ - /** - * Performs the OnInit step for the control and all its child controls. - * This method overrides the parent implementation - * by ensuring child controls are created first. - * Only framework developers should use this method. - * @param TControl the naming container control - */ - protected function initRecursive($namingContainer=null) - { - $this->ensureChildControls(); - parent::initRecursive($namingContainer); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TCompositeControl class. + * TCompositeControl is the base class for controls that are composed + * by other controls. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TCompositeControl extends TControl implements INamingContainer +{ + /** + * Performs the OnInit step for the control and all its child controls. + * This method overrides the parent implementation + * by ensuring child controls are created first. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + parent::initRecursive($namingContainer); + } +} + diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 316098a1..2adabb55 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -1,2395 +1,2395 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * Includes TAttributeCollection and TControlAdapter class - */ -Prado::using('System.Collections.TAttributeCollection'); -Prado::using('System.Web.UI.TControlAdapter'); - -/** - * TControl class - * - * TControl is the base class for all components on a page hierarchy. - * It implements the following features for UI-related functionalities: - * - databinding feature - * - parent and child relationship - * - naming container and containee relationship - * - viewstate and controlstate features - * - rendering scheme - * - control lifecycles - * - * A property can be data-bound with an expression. By calling {@link dataBind}, - * expressions bound to properties will be evaluated and the results will be - * set to the corresponding properties. - * - * Parent and child relationship determines how the presentation of controls are - * enclosed within each other. A parent will determine where to place - * the presentation of its child controls. For example, a TPanel will enclose - * all its child controls' presentation within a div html tag. A control's parent - * can be obtained via {@link getParent Parent} property, and its - * {@link getControls Controls} property returns a list of the control's children, - * including controls and static texts. The property can be manipulated - * like an array for adding or removing a child (see {@link TList} for more details). - * - * A naming container control implements INamingContainer and ensures that - * its containee controls can be differentiated by their ID property values. - * Naming container and containee realtionship specifies a protocol to uniquely - * identify an arbitrary control on a page hierarchy by an ID path (concatenation - * of all naming containers' IDs and the target control's ID). - * - * Viewstate and controlstate are two approaches to preserve state across - * page postback requests. ViewState is mainly related with UI specific state - * and can be disabled if not needed. ControlState represents crucial logic state - * and cannot be disabled. - * - * A control is rendered via its {@link render()} method (the method is invoked - * by the framework.) Descendant control classes may override this method for - * customized rendering. By default, {@link render()} invokes {@link renderChildren()} - * which is responsible for rendering of children of the control. - * Control's {@link getVisible Visible} property governs whether the control - * should be rendered or not. - * - * Each control on a page will undergo a series of lifecycles, including - * control construction, Init, Load, PreRender, Render, and OnUnload. - * They work together with page lifecycles to process a page request. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TControl extends TApplicationComponent implements IRenderable, IBindable -{ - /** - * format of control ID - */ - const ID_FORMAT='/^[a-zA-Z_]\\w*$/'; - /** - * separator char between IDs in a UniqueID - */ - const ID_SEPARATOR='$'; - /** - * separator char between IDs in a ClientID - */ - const CLIENT_ID_SEPARATOR='_'; - /** - * prefix to an ID automatically generated - */ - const AUTOMATIC_ID_PREFIX='ctl'; - - /** - * the stage of lifecycles that the control is currently at - */ - const CS_CONSTRUCTED=0; - const CS_CHILD_INITIALIZED=1; - const CS_INITIALIZED=2; - const CS_STATE_LOADED=3; - const CS_LOADED=4; - const CS_PRERENDERED=5; - - /** - * State bits. - */ - const IS_ID_SET=0x01; - const IS_DISABLE_VIEWSTATE=0x02; - const IS_SKIN_APPLIED=0x04; - const IS_STYLESHEET_APPLIED=0x08; - const IS_DISABLE_THEMING=0x10; - const IS_CHILD_CREATED=0x20; - const IS_CREATING_CHILD=0x40; - - /** - * Indexes for the rare fields. - * In order to save memory, rare fields will only be created if they are needed. - */ - const RF_CONTROLS=0; // child controls - const RF_CHILD_STATE=1; // child state field - const RF_NAMED_CONTROLS=2; // list of controls whose namingcontainer is this control - const RF_NAMED_CONTROLS_ID=3; // counter for automatic id - const RF_SKIN_ID=4; // skin ID - const RF_DATA_BINDINGS=5; // data bindings - const RF_EVENTS=6; // event handlers - const RF_CONTROLSTATE=7; // controlstate - const RF_NAMED_OBJECTS=8; // controls declared with ID on template - const RF_ADAPTER=9; // adapter - const RF_AUTO_BINDINGS=10; // auto data bindings - - /** - * @var string control ID - */ - private $_id=''; - /** - * @var string control unique ID - */ - private $_uid; - /** - * @var TControl parent of the control - */ - private $_parent; - /** - * @var TPage page that the control resides in - */ - private $_page; - /** - * @var TControl naming container of the control - */ - private $_namingContainer; - /** - * @var TTemplateControl control whose template contains the control - */ - private $_tplControl; - /** - * @var array viewstate data - */ - private $_viewState=array(); - /** - * @var array temporary state (usually set in template) - */ - private $_tempState=array(); - /** - * @var boolean whether we should keep state in viewstate - */ - private $_trackViewState=true; - /** - * @var integer the current stage of the control lifecycles - */ - private $_stage=0; - /** - * @var integer representation of the state bits - */ - private $_flags=0; - /** - * @var array a collection of rare control data - */ - private $_rf=array(); - - /** - * Constructor. - */ - public function __construct() - { - } - - /** - * Returns a property value by name or a control by ID. - * This overrides the parent implementation by allowing accessing - * a control via its ID using the following syntax, - * - * $menuBar=$this->menuBar; - * - * Note, the control must be configured in the template - * with explicit ID. If the name matches both a property and a control ID, - * the control ID will take the precedence. - * - * @param string the property name or control ID - * @return mixed the property value or the target control - * @throws TInvalidOperationException if the property is not defined. - * @see registerObject - */ - public function __get($name) - { - if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) - return $this->_rf[self::RF_NAMED_OBJECTS][$name]; - else - return parent::__get($name); - } - - /** - * @return boolean whether there is an adapter for this control - */ - public function getHasAdapter() - { - return isset($this->_rf[self::RF_ADAPTER]); - } - - /** - * @return TControlAdapter control adapter. Null if not exists. - */ - public function getAdapter() - { - return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null; - } - - /** - * @param TControlAdapter control adapter - */ - public function setAdapter(TControlAdapter $adapter) - { - $this->_rf[self::RF_ADAPTER]=$adapter; - } - - /** - * @return TControl the parent of this control - */ - public function getParent() - { - return $this->_parent; - } - - /** - * @return TControl the naming container of this control - */ - public function getNamingContainer() - { - if(!$this->_namingContainer && $this->_parent) - { - if($this->_parent instanceof INamingContainer) - $this->_namingContainer=$this->_parent; - else - $this->_namingContainer=$this->_parent->getNamingContainer(); - } - return $this->_namingContainer; - } - - /** - * @return TPage the page that contains this control - */ - public function getPage() - { - if(!$this->_page) - { - if($this->_parent) - $this->_page=$this->_parent->getPage(); - else if($this->_tplControl) - $this->_page=$this->_tplControl->getPage(); - } - return $this->_page; - } - - /** - * Sets the page for a control. - * Only framework developers should use this method. - * @param TPage the page that contains this control - */ - public function setPage($page) - { - $this->_page=$page; - } - - /** - * Sets the control whose template contains this control. - * Only framework developers should use this method. - * @param TTemplateControl the control whose template contains this control - */ - public function setTemplateControl($control) - { - $this->_tplControl=$control; - } - - /** - * @return TTemplateControl the control whose template contains this control - */ - public function getTemplateControl() - { - if(!$this->_tplControl && $this->_parent) - $this->_tplControl=$this->_parent->getTemplateControl(); - return $this->_tplControl; - } - - /** - * @return TTemplateControl the control whose template is loaded from - * some external storage, such as file, db, and whose template ultimately - * contains this control. - */ - public function getSourceTemplateControl() - { - $control=$this; - while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null) - { - if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl()) - return $control; - } - return $this->getPage(); - } - - /** - * Gets the lifecycle step the control is currently at. - * This method should only be used by control developers. - * @return integer the lifecycle step the control is currently at. - * The value can be CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, - * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. - */ - protected function getControlStage() - { - return $this->_stage; - } - - /** - * Sets the lifecycle step the control is currently at. - * This method should only be used by control developers. - * @param integer the lifecycle step the control is currently at. - * Valid values include CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, - * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. - */ - protected function setControlStage($value) - { - $this->_stage=$value; - } - - /** - * Returns the id of the control. - * Control ID can be either manually set or automatically generated. - * If $hideAutoID is true, automatically generated ID will be returned as an empty string. - * @param boolean whether to hide automatically generated ID - * @return string the ID of the control - */ - public function getID($hideAutoID=true) - { - if($hideAutoID) - return ($this->_flags & self::IS_ID_SET) ? $this->_id : ''; - else - return $this->_id; - } - - /** - * @param string the new control ID. The value must consist of word characters [a-zA-Z0-9_] only - * @throws TInvalidDataValueException if ID is in a bad format - */ - public function setID($id) - { - if(!preg_match(self::ID_FORMAT,$id)) - throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id); - $this->_id=$id; - $this->_flags |= self::IS_ID_SET; - $this->clearCachedUniqueID($this instanceof INamingContainer); - if($this->_namingContainer) - $this->_namingContainer->clearNameTable(); - } - - /** - * Returns a unique ID that identifies the control in the page hierarchy. - * A unique ID is the contenation of all naming container controls' IDs and the control ID. - * These IDs are separated by '$' character. - * Control users should not rely on the specific format of UniqueID, however. - * @return string a unique ID that identifies the control in the page hierarchy - */ - public function getUniqueID() - { - if($this->_uid==='' || $this->_uid===null) // need to build the UniqueID - { - $this->_uid=''; // set to not-null, so that clearCachedUniqueID() may take action - if($namingContainer=$this->getNamingContainer()) - { - if($this->getPage()===$namingContainer) - return ($this->_uid=$this->_id); - else if(($prefix=$namingContainer->getUniqueID())==='') - return $this->_id; - else - return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id); - } - else // no naming container - return $this->_id; - } - else - return $this->_uid; - } - - /** - * Sets input focus to this control. - */ - public function focus() - { - $this->getPage()->setFocus($this); - } - - /** - * Returns the client ID of the control. - * The client ID can be used to uniquely identify - * the control in client-side scripts (such as JavaScript). - * Do not rely on the explicit format of the return ID. - * @return string the client ID of the control - */ - public function getClientID() - { - return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); - } - - /** - * Converts a unique ID to a client ID. - * @param string the unique ID of a control - * @return string the client ID of the control - */ - public static function convertUniqueIdToClientId($uniqueID) - { - return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); - } - - /** - * @return string the skin ID of this control, '' if not set - */ - public function getSkinID() - { - return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:''; - } - - /** - * @param string the skin ID of this control - * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. - */ - public function setSkinID($value) - { - if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED) - throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this)); - else - $this->_rf[self::RF_SKIN_ID]=$value; - } - - /** - * @param string the skin ID of this control - * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. - */ - public function getIsSkinApplied() - { - return ($this->_flags & self::IS_SKIN_APPLIED); - } - - /** - * @return boolean whether theming is enabled for this control. - * The theming is enabled if the control and all its parents have it enabled. - */ - public function getEnableTheming() - { - if($this->_flags & self::IS_DISABLE_THEMING) - return false; - else - return $this->_parent?$this->_parent->getEnableTheming():true; - } - - /** - * @param boolean whether to enable theming - * @throws TInvalidOperationException if this method is invoked after OnPreInit - */ - public function setEnableTheming($value) - { - if($this->_stage>=self::CS_CHILD_INITIALIZED) - throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID()); - else if(TPropertyValue::ensureBoolean($value)) - $this->_flags &= ~self::IS_DISABLE_THEMING; - else - $this->_flags |= self::IS_DISABLE_THEMING; - } - - /** - * Returns custom data associated with this control. - * A control may be associated with some custom data for various purposes. - * For example, a button may be associated with a string to identify itself - * in a generic OnClick event handler. - * @return mixed custom data associated with this control. Defaults to null. - */ - public function getCustomData() - { - return $this->getViewState('CustomData',null); - } - - /** - * Associates custom data with this control. - * Note, the custom data must be serializable and unserializable. - * @param mixed custom data - */ - public function setCustomData($value) - { - $this->setViewState('CustomData',$value,null); - } - - /** - * @return boolean whether the control has child controls - */ - public function getHasControls() - { - return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0; - } - - /** - * @return TControlCollection the child control collection - */ - public function getControls() - { - if(!isset($this->_rf[self::RF_CONTROLS])) - $this->_rf[self::RF_CONTROLS]=$this->createControlCollection(); - return $this->_rf[self::RF_CONTROLS]; - } - - /** - * Creates a control collection object that is to be used to hold child controls - * @return TControlCollection control collection - * @see getControls - */ - protected function createControlCollection() - { - return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this); - } - - /** - * Checks if a control is visible. - * If parent check is required, then a control is visible only if the control - * and all its ancestors are visible. - * @param boolean whether the parents should also be checked if visible - * @return boolean whether the control is visible (default=true). - */ - public function getVisible($checkParents=true) - { - if($checkParents) - { - for($control=$this;$control;$control=$control->_parent) - if(!$control->getVisible(false)) - return false; - return true; - } - else - return $this->getViewState('Visible',true); - } - - /** - * @param boolean whether the control is visible - */ - public function setVisible($value) - { - $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Returns a value indicating whether the control is enabled. - * A control is enabled if it allows client user interaction. - * If $checkParents is true, all parent controls will be checked, - * and unless they are all enabled, false will be returned. - * The property Enabled is mainly used for {@link TWebControl} - * derived controls. - * @param boolean whether the parents should also be checked enabled - * @return boolean whether the control is enabled. - */ - public function getEnabled($checkParents=false) - { - if($checkParents) - { - for($control=$this;$control;$control=$control->_parent) - if(!$control->getViewState('Enabled',true)) - return false; - return true; - } - else - return $this->getViewState('Enabled',true); - } - - /** - * @param boolean whether the control is to be enabled. - */ - public function setEnabled($value) - { - $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the control has custom attributes - */ - public function getHasAttributes() - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->getCount()>0; - else - return false; - } - - /** - * Returns the list of custom attributes. - * Custom attributes are name-value pairs that may be rendered - * as HTML tags' attributes. - * @return TAttributeCollection the list of custom attributes - */ - public function getAttributes() - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes; - else - { - $attributes=new TAttributeCollection; - $this->setViewState('Attributes',$attributes,null); - return $attributes; - } - } - - /** - * @return boolean whether the named attribute exists - */ - public function hasAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->contains($name); - else - return false; - } - - /** - * @return string attribute value, null if attribute does not exist - */ - public function getAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->itemAt($name); - else - return null; - } - - /** - * Sets a custom control attribute. - * @param string attribute name - * @param string value of the attribute - */ - public function setAttribute($name,$value) - { - $this->getAttributes()->add($name,$value); - } - - /** - * Removes the named attribute. - * @param string the name of the attribute to be removed. - * @return string attribute value removed, null if attribute does not exist. - */ - public function removeAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->remove($name); - else - return null; - } - - /** - * @return boolean whether viewstate is enabled - */ - public function getEnableViewState($checkParents=false) - { - if($checkParents) - { - for($control=$this;$control!==null;$control=$control->getParent()) - if($control->_flags & self::IS_DISABLE_VIEWSTATE) - return false; - return true; - } - else - return !($this->_flags & self::IS_DISABLE_VIEWSTATE); - } - - /** - * @param boolean set whether to enable viewstate - */ - public function setEnableViewState($value) - { - if(TPropertyValue::ensureBoolean($value)) - $this->_flags &= ~self::IS_DISABLE_VIEWSTATE; - else - $this->_flags |= self::IS_DISABLE_VIEWSTATE; - } - - /** - * Returns a controlstate value. - * - * This function is mainly used in defining getter functions for control properties - * that must be kept in controlstate. - * @param string the name of the controlstate value to be returned - * @param mixed the default value. If $key is not found in controlstate, $defaultValue will be returned - * @return mixed the controlstate value corresponding to $key - */ - protected function getControlState($key,$defaultValue=null) - { - return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue; - } - - /** - * Sets a controlstate value. - * - * This function is very useful in defining setter functions for control properties - * that must be kept in controlstate. - * Make sure that the controlstate value must be serializable and unserializable. - * @param string the name of the controlstate value - * @param mixed the controlstate value to be set - * @param mixed default value. If $value===$defaultValue, the item will be cleared from controlstate - */ - protected function setControlState($key,$value,$defaultValue=null) - { - if($value===$defaultValue) - unset($this->_rf[self::RF_CONTROLSTATE][$key]); - else - $this->_rf[self::RF_CONTROLSTATE][$key]=$value; - } - - /** - * Clears a controlstate value. - * @param string the name of the controlstate value to be cleared - */ - protected function clearControlState($key) - { - unset($this->_rf[self::RF_CONTROLSTATE][$key]); - } - - /** - * Sets a value indicating whether we should keep data in viewstate. - * When it is false, data saved via setViewState() will not be persisted. - * By default, it is true, meaning data will be persisted across postbacks. - * @param boolean whether data should be persisted - */ - public function trackViewState($enabled) - { - $this->_trackViewState=TPropertyValue::ensureBoolean($enabled); - } - - /** - * Returns a viewstate value. - * - * This function is very useful in defining getter functions for component properties - * that must be kept in viewstate. - * @param string the name of the viewstate value to be returned - * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned - * @return mixed the viewstate value corresponding to $key - */ - public function getViewState($key,$defaultValue=null) - { - if(isset($this->_viewState[$key])) - return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue; - else if(isset($this->_tempState[$key])) - { - if(is_object($this->_tempState[$key]) && $this->_trackViewState) - $this->_viewState[$key]=$this->_tempState[$key]; - return $this->_tempState[$key]; - } - else - return $defaultValue; - } - - /** - * Sets a viewstate value. - * - * This function is very useful in defining setter functions for control properties - * that must be kept in viewstate. - * Make sure that the viewstate value must be serializable and unserializable. - * @param string the name of the viewstate value - * @param mixed the viewstate value to be set - * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. - */ - public function setViewState($key,$value,$defaultValue=null) - { - if($this->_trackViewState) - { - $this->_viewState[$key]=$value; - unset($this->_tempState[$key]); - } - else - { - unset($this->_viewState[$key]); - $this->_tempState[$key]=$value; - } - } - - /** - * Clears a viewstate value. - * @param string the name of the viewstate value to be cleared - */ - public function clearViewState($key) - { - unset($this->_viewState[$key]); - unset($this->_tempState[$key]); - } - - /** - * Sets up the binding between a property (or property path) and an expression. - * The context of the expression is the template control (or the control itself if it is a page). - * @param string the property name, or property path - * @param string the expression - */ - public function bindProperty($name,$expression) - { - $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression; - } - - /** - * Breaks the binding between a property (or property path) and an expression. - * @param string the property name (or property path) - */ - public function unbindProperty($name) - { - unset($this->_rf[self::RF_DATA_BINDINGS][$name]); - } - - /** - * Sets up the binding between a property (or property path) and an expression. - * Unlike regular databinding, the expression bound by this method - * is automatically evaluated during {@link prerenderRecursive()}. - * The context of the expression is the template control (or the control itself if it is a page). - * @param string the property name, or property path - * @param string the expression - */ - public function autoBindProperty($name,$expression) - { - $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression; - } - - /** - * Performs the databinding for this control. - */ - public function dataBind() - { - $this->dataBindProperties(); - $this->onDataBinding(null); - $this->dataBindChildren(); - } - - /** - * Databinding properties of the control. - */ - protected function dataBindProperties() - { - Prado::trace("Data bind properties",'System.Web.UI.TControl'); - if(isset($this->_rf[self::RF_DATA_BINDINGS])) - { - if(($context=$this->getTemplateControl())===null) - $context=$this; - foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression) - $this->setSubProperty($property,$context->evaluateExpression($expression)); - } - } - - /** - * Auto databinding properties of the control. - */ - protected function autoDataBindProperties() - { - if(isset($this->_rf[self::RF_AUTO_BINDINGS])) - { - if(($context=$this->getTemplateControl())===null) - $context=$this; - foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression) - $this->setSubProperty($property,$context->evaluateExpression($expression)); - } - } - - /** - * Databinding child controls. - */ - protected function dataBindChildren() - { - Prado::trace("dataBindChildren()",'System.Web.UI.TControl'); - if(isset($this->_rf[self::RF_CONTROLS])) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - if($control instanceof IBindable) - $control->dataBind(); - } - } - - /** - * @return boolean whether child controls have been created - */ - final protected function getChildControlsCreated() - { - return ($this->_flags & self::IS_CHILD_CREATED)!==0; - } - - /** - * Sets a value indicating whether child controls are created. - * If false, any existing child controls will be cleared up. - * @param boolean whether child controls are created - */ - final protected function setChildControlsCreated($value) - { - if($value) - $this->_flags |= self::IS_CHILD_CREATED; - else - { - if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED)) - $this->getControls()->clear(); - $this->_flags &= ~self::IS_CHILD_CREATED; - } - } - - /** - * Ensures child controls are created. - * If child controls are not created yet, this method will invoke - * {@link createChildControls} to create them. - */ - public function ensureChildControls() - { - if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD)) - { - try - { - $this->_flags |= self::IS_CREATING_CHILD; - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->createChildControls(); - else - $this->createChildControls(); - $this->_flags &= ~self::IS_CREATING_CHILD; - $this->_flags |= self::IS_CHILD_CREATED; - } - catch(Exception $e) - { - $this->_flags &= ~self::IS_CREATING_CHILD; - $this->_flags |= self::IS_CHILD_CREATED; - throw $e; - } - } - } - - /** - * Creates child controls. - * This method can be overriden for controls who want to have their controls. - * Do not call this method directly. Instead, call {@link ensureChildControls} - * to ensure child controls are created only once. - */ - public function createChildControls() - { - } - - /** - * Finds a control by ID path within the current naming container. - * The current naming container is either the control itself - * if it implements {@link INamingContainer} or the control's naming container. - * The ID path is an ID sequence separated by {@link TControl::ID_SEPARATOR}. - * For example, 'Repeater1.Item1.Button1' looks for a control with ID 'Button1' - * whose naming container is 'Item1' whose naming container is 'Repeater1'. - * @param string ID of the control to be looked up - * @return TControl|null the control found, null if not found - * @throws TInvalidDataValueException if a control's ID is found not unique within its naming container. - */ - public function findControl($id) - { - $id=strtr($id,'.',self::ID_SEPARATOR); - $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer(); - if(!$container || !$container->getHasControls()) - return null; - if(!isset($container->_rf[self::RF_NAMED_CONTROLS])) - { - $container->_rf[self::RF_NAMED_CONTROLS]=array(); - $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]); - } - if(($pos=strpos($id,self::ID_SEPARATOR))===false) - return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null; - else - { - $cid=substr($id,0,$pos); - $sid=substr($id,$pos+1); - if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid])) - return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid); - else - return null; - } - } - - /** - * Finds all child and grand-child controls that are of the specified type. - * @param string the class name - * @param boolean whether the type comparison is strict or not. If false, controls of the parent classes of the specified class will also be returned. - * @return array list of controls found - */ - public function findControlsByType($type,$strict=true) - { - $controls=array(); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type)))) - $controls[]=$control; - if(($control instanceof TControl) && $control->getHasControls()) - $controls=array_merge($controls,$control->findControlsByType($type,$strict)); - } - } - return $controls; - } - - /** - * Finds all child and grand-child controls with the specified ID. - * Note, this method is different from {@link findControl} in that - * it searches through all controls that have this control as the ancestor - * while {@link findcontrol} only searches through controls that have this - * control as the direct naming container. - * @param string the ID being looked for - * @return array list of controls found - */ - public function findControlsByID($id) - { - $controls=array(); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - { - if($control->_id===$id) - $controls[]=$control; - $controls=array_merge($controls,$control->findControlsByID($id)); - } - } - } - return $controls; - } - - /** - * Resets the control as a naming container. - * Only framework developers should use this method. - */ - public function clearNamingContainer() - { - unset($this->_rf[self::RF_NAMED_CONTROLS_ID]); - $this->clearNameTable(); - } - - /** - * Registers an object by a name. - * A registered object can be accessed like a public member variable. - * This method should only be used by framework and control developers. - * @param string name of the object - * @param object object to be declared - * @see __get - */ - public function registerObject($name,$object) - { - if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) - throw new TInvalidOperationException('control_object_reregistered',$name); - $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object; - } - - /** - * Unregisters an object by name. - * @param string name of the object - * @see registerObject - */ - public function unregisterObject($name) - { - unset($this->_rf[self::RF_NAMED_OBJECTS][$name]); - } - - /** - * @return boolean whether an object has been registered with the name - * @see registerObject - */ - public function isObjectRegistered($name) - { - return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]); - } - - /** - * @return boolean true if the child control has been initialized. - */ - public function getHasChildInitialized() - { - return $this->getControlStage() >= self::CS_CHILD_INITIALIZED; - } - - /** - * @return boolean true if the onInit event has raised. - */ - public function getHasInitialized() - { - return $this->getControlStage() >= self::CS_INITIALIZED; - } - - /** - * @return boolean true if the control has loaded post data. - */ - public function getHasLoadedPostData() - { - return $this->getControlStage() >= self::CS_STATE_LOADED; - } - - /** - * @return boolean true if the onLoad event has raised. - */ - public function getHasLoaded() - { - return $this->getControlStage() >= self::CS_LOADED; - } - - /** - * @return boolean true if onPreRender event has raised. - */ - public function getHasPreRendered() - { - return $this->getControlStage() >= self::CS_PRERENDERED; - } - - /** - * Returns the named registered object. - * A component with explicit ID on a template will be registered to - * the template owner. This method allows you to obtain this component - * with the ID. - * @return mixed the named registered object. Null if object is not found. - */ - public function getRegisteredObject($name) - { - return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null; - } - - /** - * @return boolean whether body contents are allowed for this control. Defaults to true. - */ - public function getAllowChildControls() - { - return true; - } - - /** - * Adds the object instantiated on a template to the child control collection. - * This method overrides the parent implementation. - * Only framework developers and control developers should use this method. - * @param string|TComponent text string or component parsed and instantiated in template - * @see createdOnTemplate - */ - public function addParsedObject($object) - { - $this->getControls()->add($object); - } - - /** - * Clears up the child state data. - * After a control loads its state, those state that do not belong to - * any existing child controls are stored as child state. - * This method will remove these state. - * Only frameworker developers and control developers should use this method. - */ - final protected function clearChildState() - { - unset($this->_rf[self::RF_CHILD_STATE]); - } - - /** - * @param TControl the potential ancestor control - * @return boolean if the control is a descendent (parent, parent of parent, etc.) - * of the specified control - */ - final protected function isDescendentOf($ancestor) - { - $control=$this; - while($control!==$ancestor && $control->_parent) - $control=$control->_parent; - return $control===$ancestor; - } - - /** - * Adds a control into the child collection of the control. - * Control lifecycles will be caught up during the addition. - * Only framework developers should use this method. - * @param TControl the new child control - */ - public function addedControl($control) - { - if($control->_parent) - $control->_parent->getControls()->remove($control); - $control->_parent=$this; - $control->_page=$this->getPage(); - $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer; - if($namingContainer) - { - $control->_namingContainer=$namingContainer; - if($control->_id==='') - $control->generateAutomaticID(); - else - $namingContainer->clearNameTable(); - $control->clearCachedUniqueID($control instanceof INamingContainer); - } - - if($this->_stage>=self::CS_CHILD_INITIALIZED) - { - $control->initRecursive($namingContainer); - if($this->_stage>=self::CS_STATE_LOADED) - { - if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id])) - { - $state=$this->_rf[self::RF_CHILD_STATE][$control->_id]; - unset($this->_rf[self::RF_CHILD_STATE][$control->_id]); - } - else - $state=null; - $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE)); - if($this->_stage>=self::CS_LOADED) - { - $control->loadRecursive(); - if($this->_stage>=self::CS_PRERENDERED) - $control->preRenderRecursive(); - } - } - } - } - - /** - * Removes a control from the child collection of the control. - * Only framework developers should use this method. - * @param TControl the child control removed - */ - public function removedControl($control) - { - if($this->_namingContainer) - $this->_namingContainer->clearNameTable(); - $control->unloadRecursive(); - $control->_parent=null; - $control->_page=null; - $control->_namingContainer=null; - $control->_tplControl=null; - //$control->_stage=self::CS_CONSTRUCTED; - if(!($control->_flags & self::IS_ID_SET)) - $control->_id=''; - else - unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]); - $control->clearCachedUniqueID(true); - } - - /** - * Performs the Init step for the control and all its child controls. - * Only framework developers should use this method. - * @param TControl the naming container control - */ - protected function initRecursive($namingContainer=null) - { - $this->ensureChildControls(); - if($this->getHasControls()) - { - if($this instanceof INamingContainer) - $namingContainer=$this; - $page=$this->getPage(); - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - { - $control->_namingContainer=$namingContainer; - $control->_page=$page; - if($control->_id==='' && $namingContainer) - $control->generateAutomaticID(); - $control->initRecursive($namingContainer); - } - } - } - if($this->_stage_stage=self::CS_CHILD_INITIALIZED; - if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED)) - { - $page->applyControlSkin($this); - $this->_flags |= self::IS_SKIN_APPLIED; - } - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->onInit(null); - else - $this->onInit(null); - $this->_stage=self::CS_INITIALIZED; - } - } - - /** - * Performs the Load step for the control and all its child controls. - * Only framework developers should use this method. - */ - protected function loadRecursive() - { - if($this->_stage_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->onLoad(null); - else - $this->onLoad(null); - } - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - $control->loadRecursive(); - } - } - if($this->_stage_stage=self::CS_LOADED; - } - - /** - * Performs the PreRender step for the control and all its child controls. - * Only framework developers should use this method. - */ - protected function preRenderRecursive() - { - $this->autoDataBindProperties(); - - if($this->getVisible(false)) - { - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->onPreRender(null); - else - $this->onPreRender(null); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - $control->preRenderRecursive(); - else if($control instanceof TCompositeLiteral) - $control->evaluateDynamicContent(); - } - } - $this->addToPostDataLoader(); - } - $this->_stage=self::CS_PRERENDERED; - } - - /** - * Add controls implementing IPostBackDataHandler to post data loaders. - */ - protected function addToPostDataLoader() - { - if($this instanceof IPostBackDataHandler) - $this->getPage()->registerPostDataLoader($this); - } - - /** - * Performs the Unload step for the control and all its child controls. - * Only framework developers should use this method. - */ - protected function unloadRecursive() - { - if(!($this->_flags & self::IS_ID_SET)) - $this->_id=''; - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - if($control instanceof TControl) - $control->unloadRecursive(); - } - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->onUnload(null); - else - $this->onUnload(null); - } - - /** - * This method is invoked when the control enters 'OnInit' stage. - * The method raises 'OnInit' event. - * 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 - */ - public function onInit($param) - { - $this->raiseEvent('OnInit',$this,$param); - } - - /** - * This method is invoked when the control enters 'OnLoad' stage. - * The method raises 'OnLoad' event. - * 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 - */ - public function onLoad($param) - { - $this->raiseEvent('OnLoad',$this,$param); - } - - /** - * Raises 'OnDataBinding' event. - * This method is invoked when {@link dataBind} is invoked. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onDataBinding($param) - { - Prado::trace("onDataBinding()",'System.Web.UI.TControl'); - $this->raiseEvent('OnDataBinding',$this,$param); - } - - - /** - * This method is invoked when the control enters 'OnUnload' stage. - * The method raises 'OnUnload' event. - * 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 - */ - public function onUnload($param) - { - $this->raiseEvent('OnUnload',$this,$param); - } - - /** - * This method is invoked when the control enters 'OnPreRender' stage. - * The method raises 'OnPreRender' event. - * 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 - */ - public function onPreRender($param) - { - $this->raiseEvent('OnPreRender',$this,$param); - } - - /** - * Invokes the parent's bubbleEvent method. - * A control who wants to bubble an event must call this method in its onEvent method. - * @param TControl sender of the event - * @param TEventParameter event parameter - * @see bubbleEvent - */ - protected function raiseBubbleEvent($sender,$param) - { - $control=$this; - while($control=$control->_parent) - { - if($control->bubbleEvent($sender,$param)) - break; - } - } - - /** - * This method responds to a bubbled event. - * This method should be overriden to provide customized response to a bubbled event. - * Check the type of event parameter to determine what event is bubbled currently. - * @param TControl sender of the event - * @param TEventParameter event parameters - * @return boolean true if the event bubbling is handled and no more bubbling. - * @see raiseBubbleEvent - */ - public function bubbleEvent($sender,$param) - { - return false; - } - - /** - * Broadcasts an event. - * The event will be sent to all controls on the current page hierarchy. - * If a control defines the event, the event will be raised for the control. - * If a control implements {@link IBroadcastEventReceiver}, its - * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} method will - * be invoked which gives the control a chance to respond to the event. - * For example, when broadcasting event 'OnClick', all controls having 'OnClick' - * event will have this event raised, and all controls implementing - * {@link IBroadcastEventReceiver} will also have its - * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} - * invoked. - * @param string name of the broadcast event - * @param TControl sender of this event - * @param TEventParameter event parameter - */ - public function broadcastEvent($name,$sender,$param) - { - $rootControl=(($page=$this->getPage())===null)?$this:$page; - $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param)); - } - - /** - * Recursively broadcasts an event. - * This method should only be used by framework developers. - * @param string name of the broadcast event - * @param TControl sender of the event - * @param TBroadcastEventParameter event parameter - */ - private function broadcastEventInternal($name,$sender,$param) - { - if($this->hasEvent($name)) - $this->raiseEvent($name,$sender,$param->getParameter()); - if($this instanceof IBroadcastEventReceiver) - $this->broadcastEventReceived($sender,$param); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - $control->broadcastEventInternal($name,$sender,$param); - } - } - } - - /** - * Traverse the whole control hierarchy rooted at this control. - * Callback function may be invoked for each control being visited. - * A pre-callback is invoked before traversing child controls; - * A post-callback is invoked after traversing child controls. - * Callback functions can be global functions or class methods. - * They must be of the following signature: - * - * function callback_func($control,$param) {...} - * - * where $control refers to the control being visited and $param - * is the parameter that is passed originally when calling this traverse function. - * - * @param mixed parameter to be passed to callbacks for each control - * @param callback callback invoked before traversing child controls. If null, it is ignored. - * @param callback callback invoked after traversing child controls. If null, it is ignored. - */ - protected function traverseChildControls($param,$preCallback=null,$postCallback=null) - { - if($preCallback!==null) - call_user_func($preCallback,$this,$param); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - { - $control->traverseChildControls($param,$preCallback,$postCallback); - } - } - } - if($postCallback!==null) - call_user_func($postCallback,$this,$param); - } - - /** - * Renders the control. - * Only when the control is visible will the control be rendered. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderControl($writer) - { - if($this instanceof IActiveControl || $this->getVisible(false)) - { - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->render($writer); - else - $this->render($writer); - } - } - - /** - * Renders the control. - * This method is invoked by {@link renderControl} when the control is visible. - * You can override this method to provide customized rendering of the control. - * By default, the control simply renders all its child contents. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - $this->renderChildren($writer); - } - - /** - * Renders the children of the control. - * This method iterates through all child controls and static text strings - * and renders them in order. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderChildren($writer) - { - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if(is_string($control)) - $writer->write($control); - else if($control instanceof TControl) - $control->renderControl($writer); - else if($control instanceof IRenderable) - $control->render($writer); - } - } - } - - /** - * This method is invoked when control state is to be saved. - * You can override this method to do last step state saving. - * Parent implementation must be invoked. - */ - public function saveState() - { - } - - /** - * This method is invoked right after the control has loaded its state. - * You can override this method to initialize data from the control state. - * Parent implementation must be invoked. - */ - public function loadState() - { - } - - /** - * Loads state (viewstate and controlstate) into a control and its children. - * This method should only be used by framework developers. - * @param array the collection of the state - * @param boolean whether the viewstate should be loaded - */ - protected function loadStateRecursive(&$state,$needViewState=true) - { - if(is_array($state)) - { - // A null state means the stateful properties all take default values. - // So if the state is enabled, we have to assign the null value. - $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); - if(isset($state[1])) - { - $this->_rf[self::RF_CONTROLSTATE]=&$state[1]; - unset($state[1]); - } - else - unset($this->_rf[self::RF_CONTROLSTATE]); - if($needViewState) - { - if(isset($state[0])) - $this->_viewState=&$state[0]; - else - $this->_viewState=array(); - } - unset($state[0]); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - { - if(isset($state[$control->_id])) - { - $control->loadStateRecursive($state[$control->_id],$needViewState); - unset($state[$control->_id]); - } - } - } - } - if(!empty($state)) - $this->_rf[self::RF_CHILD_STATE]=&$state; - } - $this->_stage=self::CS_STATE_LOADED; - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->loadState(); - else - $this->loadState(); - } - - /** - * Saves all control state (viewstate and controlstate) as a collection. - * This method should only be used by framework developers. - * @param boolean whether the viewstate should be saved - * @return array the collection of the control state (including its children's state). - */ - protected function &saveStateRecursive($needViewState=true) - { - if(isset($this->_rf[self::RF_ADAPTER])) - $this->_rf[self::RF_ADAPTER]->saveState(); - else - $this->saveState(); - $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); - $state=array(); - if($this->getHasControls()) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - { - if($control instanceof TControl) - $state[$control->_id]=&$control->saveStateRecursive($needViewState); - } - } - if($needViewState && !empty($this->_viewState)) - $state[0]=&$this->_viewState; - if(isset($this->_rf[self::RF_CONTROLSTATE])) - $state[1]=&$this->_rf[self::RF_CONTROLSTATE]; - return $state; - } - - /** - * Applies a stylesheet skin to a control. - * @param TPage the page containing the control - * @throws TInvalidOperationException if the stylesheet skin is applied already - */ - public function applyStyleSheetSkin($page) - { - if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED)) - { - $page->applyControlStyleSheet($this); - $this->_flags |= self::IS_STYLESHEET_APPLIED; - } - else if($this->_flags & self::IS_STYLESHEET_APPLIED) - throw new TInvalidOperationException('control_stylesheet_applied',get_class($this)); - } - - /** - * Clears the cached UniqueID. - * If $recursive=true, all children's cached UniqueID will be cleared as well. - * @param boolean whether the clearing is recursive. - */ - private function clearCachedUniqueID($recursive) - { - if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS])) - { - foreach($this->_rf[self::RF_CONTROLS] as $control) - if($control instanceof TControl) - $control->clearCachedUniqueID($recursive); - } - $this->_uid=null; - } - - /** - * Generates an automatic ID for the control. - */ - private function generateAutomaticID() - { - $this->_flags &= ~self::IS_ID_SET; - if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID])) - $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0; - $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++; - $this->_id=self::AUTOMATIC_ID_PREFIX . $id; - $this->_namingContainer->clearNameTable(); - } - - /** - * Clears the list of the controls whose IDs are managed by the specified naming container. - */ - private function clearNameTable() - { - unset($this->_rf[self::RF_NAMED_CONTROLS]); - } - - /** - * Updates the list of the controls whose IDs are managed by the specified naming container. - * @param TControl the naming container - * @param TControlCollection list of controls - * @throws TInvalidDataValueException if a control's ID is not unique within its naming container. - */ - private function fillNameTable($container,$controls) - { - foreach($controls as $control) - { - if($control instanceof TControl) - { - if($control->_id!=='') - { - if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id])) - throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id); - else - $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control; - } - if(!($control instanceof INamingContainer) && $control->getHasControls()) - $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]); - } - } - } -} - - -/** - * TControlCollection class - * - * TControlCollection implements a collection that enables - * controls to maintain a list of their child controls. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TControlCollection extends TList -{ - /** - * the control that owns this collection. - * @var TControl - */ - private $_o; - - /** - * Constructor. - * @param TControl the control that owns this collection. - * @param boolean whether the list is read-only - */ - public function __construct(TControl $owner,$readOnly=false) - { - $this->_o=$owner; - parent::__construct(null,$readOnly); - } - - /** - * @return TControl the control that owns this collection. - */ - protected function getOwner() - { - return $this->_o; - } - - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional - * operations for each newly added child control. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl. - */ - public function insertAt($index,$item) - { - if($item instanceof TControl) - { - parent::insertAt($index,$item); - $this->_o->addedControl($item); - } - else if(is_string($item) || ($item instanceof IRenderable)) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('controlcollection_control_required'); - } - - /** - * Removes an item at the specified position. - * This overrides the parent implementation by performing additional - * cleanup work when removing a child control. - * @param integer the index of the item to be removed. - * @return mixed the removed item. - */ - public function removeAt($index) - { - $item=parent::removeAt($index); - if($item instanceof TControl) - $this->_o->removedControl($item); - return $item; - } - - /** - * Overrides the parent implementation by invoking {@link TControl::clearNamingContainer} - */ - public function clear() - { - parent::clear(); - if($this->_o instanceof INamingContainer) - $this->_o->clearNamingContainer(); - } -} - -/** - * TEmptyControlCollection class - * - * TEmptyControlCollection implements an empty control list that prohibits adding - * controls to it. This is useful for controls that do not allow child controls. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TEmptyControlCollection extends TControlCollection -{ - /** - * Constructor. - * @param TControl the control that owns this collection. - */ - public function __construct(TControl $owner) - { - parent::__construct($owner,true); - } - - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by ignoring new addition. - * @param integer the speicified position. - * @param mixed new item - */ - public function insertAt($index,$item) - { - if(!is_string($item)) // string is possible if property tag is used. we simply ignore it in this case - parent::insertAt($index,$item); // this will generate an exception in parent implementation - } -} - -/** - * INamingContainer interface. - * INamingContainer marks a control as a naming container. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface INamingContainer -{ -} - -/** - * IPostBackEventHandler interface - * - * If a control wants to respond to postback event, it must implement this interface. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IPostBackEventHandler -{ - /** - * Raises postback event. - * The implementation of this function should raise appropriate event(s) (e.g. OnClick, OnCommand) - * indicating the component is responsible for the postback event. - * @param string the parameter associated with the postback event - */ - public function raisePostBackEvent($param); -} - -/** - * IPostBackDataHandler interface - * - * If a control wants to load post data, it must implement this interface. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IPostBackDataHandler -{ - /** - * Loads user input data. - * The implementation of this function can use $values[$key] to get the user input - * data that are meant for the particular control. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the control has been changed - */ - public function loadPostData($key,$values); - /** - * Raises postdata changed event. - * The implementation of this function should raise appropriate event(s) (e.g. OnTextChanged) - * indicating the control data is changed. - */ - public function raisePostDataChangedEvent(); - /** - * @return boolean whether postback causes the data change. Defaults to false for non-postback state. - */ - public function getDataChanged(); -} - - -/** - * IValidator interface - * - * If a control wants to validate user input, it must implement this interface. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IValidator -{ - /** - * Validates certain data. - * The implementation of this function should validate certain data - * (e.g. data entered into TTextBox control). - * @return boolean whether the data passes the validation - */ - public function validate(); - /** - * @return boolean whether the previous {@link validate()} is successful. - */ - public function getIsValid(); - /** - * @param boolean whether the validator validates successfully - */ - public function setIsValid($value); - /** - * @return string error message during last validate - */ - public function getErrorMessage(); - /** - * @param string error message for the validation - */ - public function setErrorMessage($value); -} - - -/** - * IValidatable interface - * - * If a control wants to be validated by a validator, it must implement this interface. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IValidatable -{ - /** - * @return mixed the value of the property to be validated. - */ - public function getValidationPropertyValue(); - /** - * @return boolean wether this control's validators validated successfully (must default to true) - */ - public function getIsValid(); - /** - * @return boolean wether this control's validators validated successfully - */ - public function setIsValid($value); -} - -/** - * IBroadcastEventReceiver interface - * - * If a control wants to check broadcast event, it must implement this interface. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IBroadcastEventReceiver -{ - /** - * Handles broadcast event. - * This method is invoked automatically when an event is broadcasted. - * Within this method, you may check the event name given in - * the event parameter to determine whether you should respond to - * this event. - * @param TControl sender of the event - * @param TBroadCastEventParameter event parameter - */ - public function broadcastEventReceived($sender,$param); -} - -/** - * ITheme interface. - * - * This interface must be implemented by theme. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface ITheme -{ - /** - * Applies this theme to the specified control. - * @param TControl the control to be applied with this theme - */ - public function applySkin($control); -} - -/** - * ITemplate interface - * - * ITemplate specifies the interface for classes encapsulating - * parsed template structures. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface ITemplate -{ - /** - * Instantiates the template. - * Content in the template will be instantiated as components and text strings - * and passed to the specified parent control. - * @param TControl the parent control - */ - public function instantiateIn($parent); -} - -/** - * IButtonControl interface - * - * IButtonControl specifies the common properties and events that must - * be implemented by a button control, such as {@link TButton}, {@link TLinkButton}, - * {@link TImageButton}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -interface IButtonControl -{ - /** - * @return string caption of the button - */ - public function getText(); - - /** - * @param string caption of the button - */ - public function setText($value); - - /** - * @return boolean whether postback event trigger by this button will cause input validation - */ - public function getCausesValidation(); - - /** - * @param boolean whether postback event trigger by this button will cause input validation - */ - public function setCausesValidation($value); - - /** - * @return string the command name associated with the {@link onCommand OnCommand} event. - */ - public function getCommandName(); - - /** - * @param string the command name associated with the {@link onCommand OnCommand} event. - */ - public function setCommandName($value); - - /** - * @return string the parameter associated with the {@link onCommand OnCommand} event - */ - public function getCommandParameter(); - - /** - * @param string the parameter associated with the {@link onCommand OnCommand} event. - */ - public function setCommandParameter($value); - - /** - * @return string the group of validators which the button causes validation upon postback - */ - public function getValidationGroup(); - - /** - * @param string the group of validators which the button causes validation upon postback - */ - public function setValidationGroup($value); - - /** - * Raises OnClick event. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onClick($param); - - /** - * Raises OnCommand event. - * @param TCommandEventParameter event parameter to be passed to the event handlers - */ - public function onCommand($param); - - /** - * @param boolean set by a panel to register this button as the default button for the panel. - */ - public function setIsDefaultButton($value); - - /** - * @return boolean true if this button is registered as a default button for a panel. - */ - public function getIsDefaultButton(); -} - -/** - * ISurroundable interface - * - * Identifies controls that may create an additional surrounding tag. The id of the - * tag can be obtained with {@link getSurroundingTagID}. - * - * @package System.Web.UI - * @since 3.1.2 - */ -interface ISurroundable -{ - /** - * @return string the id of the embedding tag of the control or the control's clientID if not surrounded - */ - public function getSurroundingTagID(); -} - -/** - * TBroadcastEventParameter class - * - * TBroadcastEventParameter encapsulates the parameter data for - * events that are broadcasted. The name of of the event is specified via - * {@link setName Name} property while the event parameter is via - * {@link setParameter Parameter} property. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TBroadcastEventParameter extends TEventParameter -{ - private $_name; - private $_param; - - /** - * Constructor. - * @param string name of the broadcast event - * @param mixed parameter of the broadcast event - */ - public function __construct($name='',$parameter=null) - { - $this->_name=$name; - $this->_param=$parameter; - } - - /** - * @return string name of the broadcast event - */ - public function getName() - { - return $this->_name; - } - - /** - * @param string name of the broadcast event - */ - public function setName($value) - { - $this->_name=$value; - } - - /** - * @return mixed parameter of the broadcast event - */ - public function getParameter() - { - return $this->_param; - } - - /** - * @param mixed parameter of the broadcast event - */ - public function setParameter($value) - { - $this->_param=$value; - } -} - -/** - * TCommandEventParameter class - * - * TCommandEventParameter encapsulates the parameter data for Command - * event of button controls. You can access the name of the command via - * {@link getCommandName CommandName} property, and the parameter carried - * with the command via {@link getCommandParameter CommandParameter} property. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TCommandEventParameter extends TEventParameter -{ - private $_name; - private $_param; - - /** - * Constructor. - * @param string name of the command - * @param string parameter of the command - */ - public function __construct($name='',$parameter='') - { - $this->_name=$name; - $this->_param=$parameter; - } - - /** - * @return string name of the command - */ - public function getCommandName() - { - return $this->_name; - } - - /** - * @return string parameter of the command - */ - public function getCommandParameter() - { - return $this->_param; - } -} - - -/** - * TCompositeLiteral class - * - * TCompositeLiteral is used internally by {@link TTemplate} for representing - * consecutive static strings, expressions and statements. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TCompositeLiteral extends TComponent implements IRenderable, IBindable -{ - const TYPE_EXPRESSION=0; - const TYPE_STATEMENTS=1; - const TYPE_DATABINDING=2; - private $_container=null; - private $_items=array(); - private $_expressions=array(); - private $_statements=array(); - private $_bindings=array(); - - /** - * Constructor. - * @param array list of items to be represented by TCompositeLiteral - */ - public function __construct($items) - { - $this->_items=array(); - $this->_expressions=array(); - $this->_statements=array(); - foreach($items as $id=>$item) - { - if(is_array($item)) - { - if($item[0]===self::TYPE_EXPRESSION) - $this->_expressions[$id]=$item[1]; - else if($item[0]===self::TYPE_STATEMENTS) - $this->_statements[$id]=$item[1]; - else if($item[0]===self::TYPE_DATABINDING) - $this->_bindings[$id]=$item[1]; - $this->_items[$id]=''; - } - else - $this->_items[$id]=$item; - } - } - - /** - * @return TComponent container of this component. It serves as the evaluation context of expressions and statements. - */ - public function getContainer() - { - return $this->_container; - } - - /** - * @param TComponent container of this component. It serves as the evaluation context of expressions and statements. - */ - public function setContainer(TComponent $value) - { - $this->_container=$value; - } - - /** - * Evaluates the expressions and/or statements in the component. - */ - public function evaluateDynamicContent() - { - $context=$this->_container===null?$this:$this->_container; - foreach($this->_expressions as $id=>$expression) - $this->_items[$id]=$context->evaluateExpression($expression); - foreach($this->_statements as $id=>$statement) - $this->_items[$id]=$context->evaluateStatements($statement); - } - - /** - * Performs databindings. - * This method is required by {@link IBindable} - */ - public function dataBind() - { - $context=$this->_container===null?$this:$this->_container; - foreach($this->_bindings as $id=>$binding) - $this->_items[$id]=$context->evaluateExpression($binding); - } - - /** - * Renders the content stored in this component. - * This method is required by {@link IRenderable} - * @param ITextWriter - */ - public function render($writer) - { - $writer->write(implode('',$this->_items)); - } -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * Includes TAttributeCollection and TControlAdapter class + */ +Prado::using('System.Collections.TAttributeCollection'); +Prado::using('System.Web.UI.TControlAdapter'); + +/** + * TControl class + * + * TControl is the base class for all components on a page hierarchy. + * It implements the following features for UI-related functionalities: + * - databinding feature + * - parent and child relationship + * - naming container and containee relationship + * - viewstate and controlstate features + * - rendering scheme + * - control lifecycles + * + * A property can be data-bound with an expression. By calling {@link dataBind}, + * expressions bound to properties will be evaluated and the results will be + * set to the corresponding properties. + * + * Parent and child relationship determines how the presentation of controls are + * enclosed within each other. A parent will determine where to place + * the presentation of its child controls. For example, a TPanel will enclose + * all its child controls' presentation within a div html tag. A control's parent + * can be obtained via {@link getParent Parent} property, and its + * {@link getControls Controls} property returns a list of the control's children, + * including controls and static texts. The property can be manipulated + * like an array for adding or removing a child (see {@link TList} for more details). + * + * A naming container control implements INamingContainer and ensures that + * its containee controls can be differentiated by their ID property values. + * Naming container and containee realtionship specifies a protocol to uniquely + * identify an arbitrary control on a page hierarchy by an ID path (concatenation + * of all naming containers' IDs and the target control's ID). + * + * Viewstate and controlstate are two approaches to preserve state across + * page postback requests. ViewState is mainly related with UI specific state + * and can be disabled if not needed. ControlState represents crucial logic state + * and cannot be disabled. + * + * A control is rendered via its {@link render()} method (the method is invoked + * by the framework.) Descendant control classes may override this method for + * customized rendering. By default, {@link render()} invokes {@link renderChildren()} + * which is responsible for rendering of children of the control. + * Control's {@link getVisible Visible} property governs whether the control + * should be rendered or not. + * + * Each control on a page will undergo a series of lifecycles, including + * control construction, Init, Load, PreRender, Render, and OnUnload. + * They work together with page lifecycles to process a page request. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TControl extends TApplicationComponent implements IRenderable, IBindable +{ + /** + * format of control ID + */ + const ID_FORMAT='/^[a-zA-Z_]\\w*$/'; + /** + * separator char between IDs in a UniqueID + */ + const ID_SEPARATOR='$'; + /** + * separator char between IDs in a ClientID + */ + const CLIENT_ID_SEPARATOR='_'; + /** + * prefix to an ID automatically generated + */ + const AUTOMATIC_ID_PREFIX='ctl'; + + /** + * the stage of lifecycles that the control is currently at + */ + const CS_CONSTRUCTED=0; + const CS_CHILD_INITIALIZED=1; + const CS_INITIALIZED=2; + const CS_STATE_LOADED=3; + const CS_LOADED=4; + const CS_PRERENDERED=5; + + /** + * State bits. + */ + const IS_ID_SET=0x01; + const IS_DISABLE_VIEWSTATE=0x02; + const IS_SKIN_APPLIED=0x04; + const IS_STYLESHEET_APPLIED=0x08; + const IS_DISABLE_THEMING=0x10; + const IS_CHILD_CREATED=0x20; + const IS_CREATING_CHILD=0x40; + + /** + * Indexes for the rare fields. + * In order to save memory, rare fields will only be created if they are needed. + */ + const RF_CONTROLS=0; // child controls + const RF_CHILD_STATE=1; // child state field + const RF_NAMED_CONTROLS=2; // list of controls whose namingcontainer is this control + const RF_NAMED_CONTROLS_ID=3; // counter for automatic id + const RF_SKIN_ID=4; // skin ID + const RF_DATA_BINDINGS=5; // data bindings + const RF_EVENTS=6; // event handlers + const RF_CONTROLSTATE=7; // controlstate + const RF_NAMED_OBJECTS=8; // controls declared with ID on template + const RF_ADAPTER=9; // adapter + const RF_AUTO_BINDINGS=10; // auto data bindings + + /** + * @var string control ID + */ + private $_id=''; + /** + * @var string control unique ID + */ + private $_uid; + /** + * @var TControl parent of the control + */ + private $_parent; + /** + * @var TPage page that the control resides in + */ + private $_page; + /** + * @var TControl naming container of the control + */ + private $_namingContainer; + /** + * @var TTemplateControl control whose template contains the control + */ + private $_tplControl; + /** + * @var array viewstate data + */ + private $_viewState=array(); + /** + * @var array temporary state (usually set in template) + */ + private $_tempState=array(); + /** + * @var boolean whether we should keep state in viewstate + */ + private $_trackViewState=true; + /** + * @var integer the current stage of the control lifecycles + */ + private $_stage=0; + /** + * @var integer representation of the state bits + */ + private $_flags=0; + /** + * @var array a collection of rare control data + */ + private $_rf=array(); + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Returns a property value by name or a control by ID. + * This overrides the parent implementation by allowing accessing + * a control via its ID using the following syntax, + * + * $menuBar=$this->menuBar; + * + * Note, the control must be configured in the template + * with explicit ID. If the name matches both a property and a control ID, + * the control ID will take the precedence. + * + * @param string the property name or control ID + * @return mixed the property value or the target control + * @throws TInvalidOperationException if the property is not defined. + * @see registerObject + */ + public function __get($name) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + return $this->_rf[self::RF_NAMED_OBJECTS][$name]; + else + return parent::__get($name); + } + + /** + * @return boolean whether there is an adapter for this control + */ + public function getHasAdapter() + { + return isset($this->_rf[self::RF_ADAPTER]); + } + + /** + * @return TControlAdapter control adapter. Null if not exists. + */ + public function getAdapter() + { + return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null; + } + + /** + * @param TControlAdapter control adapter + */ + public function setAdapter(TControlAdapter $adapter) + { + $this->_rf[self::RF_ADAPTER]=$adapter; + } + + /** + * @return TControl the parent of this control + */ + public function getParent() + { + return $this->_parent; + } + + /** + * @return TControl the naming container of this control + */ + public function getNamingContainer() + { + if(!$this->_namingContainer && $this->_parent) + { + if($this->_parent instanceof INamingContainer) + $this->_namingContainer=$this->_parent; + else + $this->_namingContainer=$this->_parent->getNamingContainer(); + } + return $this->_namingContainer; + } + + /** + * @return TPage the page that contains this control + */ + public function getPage() + { + if(!$this->_page) + { + if($this->_parent) + $this->_page=$this->_parent->getPage(); + else if($this->_tplControl) + $this->_page=$this->_tplControl->getPage(); + } + return $this->_page; + } + + /** + * Sets the page for a control. + * Only framework developers should use this method. + * @param TPage the page that contains this control + */ + public function setPage($page) + { + $this->_page=$page; + } + + /** + * Sets the control whose template contains this control. + * Only framework developers should use this method. + * @param TTemplateControl the control whose template contains this control + */ + public function setTemplateControl($control) + { + $this->_tplControl=$control; + } + + /** + * @return TTemplateControl the control whose template contains this control + */ + public function getTemplateControl() + { + if(!$this->_tplControl && $this->_parent) + $this->_tplControl=$this->_parent->getTemplateControl(); + return $this->_tplControl; + } + + /** + * @return TTemplateControl the control whose template is loaded from + * some external storage, such as file, db, and whose template ultimately + * contains this control. + */ + public function getSourceTemplateControl() + { + $control=$this; + while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null) + { + if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl()) + return $control; + } + return $this->getPage(); + } + + /** + * Gets the lifecycle step the control is currently at. + * This method should only be used by control developers. + * @return integer the lifecycle step the control is currently at. + * The value can be CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, + * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. + */ + protected function getControlStage() + { + return $this->_stage; + } + + /** + * Sets the lifecycle step the control is currently at. + * This method should only be used by control developers. + * @param integer the lifecycle step the control is currently at. + * Valid values include CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, + * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. + */ + protected function setControlStage($value) + { + $this->_stage=$value; + } + + /** + * Returns the id of the control. + * Control ID can be either manually set or automatically generated. + * If $hideAutoID is true, automatically generated ID will be returned as an empty string. + * @param boolean whether to hide automatically generated ID + * @return string the ID of the control + */ + public function getID($hideAutoID=true) + { + if($hideAutoID) + return ($this->_flags & self::IS_ID_SET) ? $this->_id : ''; + else + return $this->_id; + } + + /** + * @param string the new control ID. The value must consist of word characters [a-zA-Z0-9_] only + * @throws TInvalidDataValueException if ID is in a bad format + */ + public function setID($id) + { + if(!preg_match(self::ID_FORMAT,$id)) + throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id); + $this->_id=$id; + $this->_flags |= self::IS_ID_SET; + $this->clearCachedUniqueID($this instanceof INamingContainer); + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + } + + /** + * Returns a unique ID that identifies the control in the page hierarchy. + * A unique ID is the contenation of all naming container controls' IDs and the control ID. + * These IDs are separated by '$' character. + * Control users should not rely on the specific format of UniqueID, however. + * @return string a unique ID that identifies the control in the page hierarchy + */ + public function getUniqueID() + { + if($this->_uid==='' || $this->_uid===null) // need to build the UniqueID + { + $this->_uid=''; // set to not-null, so that clearCachedUniqueID() may take action + if($namingContainer=$this->getNamingContainer()) + { + if($this->getPage()===$namingContainer) + return ($this->_uid=$this->_id); + else if(($prefix=$namingContainer->getUniqueID())==='') + return $this->_id; + else + return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id); + } + else // no naming container + return $this->_id; + } + else + return $this->_uid; + } + + /** + * Sets input focus to this control. + */ + public function focus() + { + $this->getPage()->setFocus($this); + } + + /** + * Returns the client ID of the control. + * The client ID can be used to uniquely identify + * the control in client-side scripts (such as JavaScript). + * Do not rely on the explicit format of the return ID. + * @return string the client ID of the control + */ + public function getClientID() + { + return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + + /** + * Converts a unique ID to a client ID. + * @param string the unique ID of a control + * @return string the client ID of the control + */ + public static function convertUniqueIdToClientId($uniqueID) + { + return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + + /** + * @return string the skin ID of this control, '' if not set + */ + public function getSkinID() + { + return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:''; + } + + /** + * @param string the skin ID of this control + * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. + */ + public function setSkinID($value) + { + if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this)); + else + $this->_rf[self::RF_SKIN_ID]=$value; + } + + /** + * @param string the skin ID of this control + * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. + */ + public function getIsSkinApplied() + { + return ($this->_flags & self::IS_SKIN_APPLIED); + } + + /** + * @return boolean whether theming is enabled for this control. + * The theming is enabled if the control and all its parents have it enabled. + */ + public function getEnableTheming() + { + if($this->_flags & self::IS_DISABLE_THEMING) + return false; + else + return $this->_parent?$this->_parent->getEnableTheming():true; + } + + /** + * @param boolean whether to enable theming + * @throws TInvalidOperationException if this method is invoked after OnPreInit + */ + public function setEnableTheming($value) + { + if($this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID()); + else if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_THEMING; + else + $this->_flags |= self::IS_DISABLE_THEMING; + } + + /** + * Returns custom data associated with this control. + * A control may be associated with some custom data for various purposes. + * For example, a button may be associated with a string to identify itself + * in a generic OnClick event handler. + * @return mixed custom data associated with this control. Defaults to null. + */ + public function getCustomData() + { + return $this->getViewState('CustomData',null); + } + + /** + * Associates custom data with this control. + * Note, the custom data must be serializable and unserializable. + * @param mixed custom data + */ + public function setCustomData($value) + { + $this->setViewState('CustomData',$value,null); + } + + /** + * @return boolean whether the control has child controls + */ + public function getHasControls() + { + return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0; + } + + /** + * @return TControlCollection the child control collection + */ + public function getControls() + { + if(!isset($this->_rf[self::RF_CONTROLS])) + $this->_rf[self::RF_CONTROLS]=$this->createControlCollection(); + return $this->_rf[self::RF_CONTROLS]; + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TControlCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this); + } + + /** + * Checks if a control is visible. + * If parent check is required, then a control is visible only if the control + * and all its ancestors are visible. + * @param boolean whether the parents should also be checked if visible + * @return boolean whether the control is visible (default=true). + */ + public function getVisible($checkParents=true) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getVisible(false)) + return false; + return true; + } + else + return $this->getViewState('Visible',true); + } + + /** + * @param boolean whether the control is visible + */ + public function setVisible($value) + { + $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Returns a value indicating whether the control is enabled. + * A control is enabled if it allows client user interaction. + * If $checkParents is true, all parent controls will be checked, + * and unless they are all enabled, false will be returned. + * The property Enabled is mainly used for {@link TWebControl} + * derived controls. + * @param boolean whether the parents should also be checked enabled + * @return boolean whether the control is enabled. + */ + public function getEnabled($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getViewState('Enabled',true)) + return false; + return true; + } + else + return $this->getViewState('Enabled',true); + } + + /** + * @param boolean whether the control is to be enabled. + */ + public function setEnabled($value) + { + $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the control has custom attributes + */ + public function getHasAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->getCount()>0; + else + return false; + } + + /** + * Returns the list of custom attributes. + * Custom attributes are name-value pairs that may be rendered + * as HTML tags' attributes. + * @return TAttributeCollection the list of custom attributes + */ + public function getAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes; + else + { + $attributes=new TAttributeCollection; + $this->setViewState('Attributes',$attributes,null); + return $attributes; + } + } + + /** + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->contains($name); + else + return false; + } + + /** + * @return string attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->itemAt($name); + else + return null; + } + + /** + * Sets a custom control attribute. + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, null if attribute does not exist. + */ + public function removeAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->remove($name); + else + return null; + } + + /** + * @return boolean whether viewstate is enabled + */ + public function getEnableViewState($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control!==null;$control=$control->getParent()) + if($control->_flags & self::IS_DISABLE_VIEWSTATE) + return false; + return true; + } + else + return !($this->_flags & self::IS_DISABLE_VIEWSTATE); + } + + /** + * @param boolean set whether to enable viewstate + */ + public function setEnableViewState($value) + { + if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_VIEWSTATE; + else + $this->_flags |= self::IS_DISABLE_VIEWSTATE; + } + + /** + * Returns a controlstate value. + * + * This function is mainly used in defining getter functions for control properties + * that must be kept in controlstate. + * @param string the name of the controlstate value to be returned + * @param mixed the default value. If $key is not found in controlstate, $defaultValue will be returned + * @return mixed the controlstate value corresponding to $key + */ + protected function getControlState($key,$defaultValue=null) + { + return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue; + } + + /** + * Sets a controlstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in controlstate. + * Make sure that the controlstate value must be serializable and unserializable. + * @param string the name of the controlstate value + * @param mixed the controlstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from controlstate + */ + protected function setControlState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + else + $this->_rf[self::RF_CONTROLSTATE][$key]=$value; + } + + /** + * Clears a controlstate value. + * @param string the name of the controlstate value to be cleared + */ + protected function clearControlState($key) + { + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + } + + /** + * Sets a value indicating whether we should keep data in viewstate. + * When it is false, data saved via setViewState() will not be persisted. + * By default, it is true, meaning data will be persisted across postbacks. + * @param boolean whether data should be persisted + */ + public function trackViewState($enabled) + { + $this->_trackViewState=TPropertyValue::ensureBoolean($enabled); + } + + /** + * Returns a viewstate value. + * + * This function is very useful in defining getter functions for component properties + * that must be kept in viewstate. + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + public function getViewState($key,$defaultValue=null) + { + if(isset($this->_viewState[$key])) + return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue; + else if(isset($this->_tempState[$key])) + { + if(is_object($this->_tempState[$key]) && $this->_trackViewState) + $this->_viewState[$key]=$this->_tempState[$key]; + return $this->_tempState[$key]; + } + else + return $defaultValue; + } + + /** + * Sets a viewstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in viewstate. + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + public function setViewState($key,$value,$defaultValue=null) + { + if($this->_trackViewState) + { + $this->_viewState[$key]=$value; + unset($this->_tempState[$key]); + } + else + { + unset($this->_viewState[$key]); + $this->_tempState[$key]=$value; + } + } + + /** + * Clears a viewstate value. + * @param string the name of the viewstate value to be cleared + */ + public function clearViewState($key) + { + unset($this->_viewState[$key]); + unset($this->_tempState[$key]); + } + + /** + * Sets up the binding between a property (or property path) and an expression. + * The context of the expression is the template control (or the control itself if it is a page). + * @param string the property name, or property path + * @param string the expression + */ + public function bindProperty($name,$expression) + { + $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression; + } + + /** + * Breaks the binding between a property (or property path) and an expression. + * @param string the property name (or property path) + */ + public function unbindProperty($name) + { + unset($this->_rf[self::RF_DATA_BINDINGS][$name]); + } + + /** + * Sets up the binding between a property (or property path) and an expression. + * Unlike regular databinding, the expression bound by this method + * is automatically evaluated during {@link prerenderRecursive()}. + * The context of the expression is the template control (or the control itself if it is a page). + * @param string the property name, or property path + * @param string the expression + */ + public function autoBindProperty($name,$expression) + { + $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression; + } + + /** + * Performs the databinding for this control. + */ + public function dataBind() + { + $this->dataBindProperties(); + $this->onDataBinding(null); + $this->dataBindChildren(); + } + + /** + * Databinding properties of the control. + */ + protected function dataBindProperties() + { + Prado::trace("Data bind properties",'System.Web.UI.TControl'); + if(isset($this->_rf[self::RF_DATA_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + + /** + * Auto databinding properties of the control. + */ + protected function autoDataBindProperties() + { + if(isset($this->_rf[self::RF_AUTO_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + + /** + * Databinding child controls. + */ + protected function dataBindChildren() + { + Prado::trace("dataBindChildren()",'System.Web.UI.TControl'); + if(isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof IBindable) + $control->dataBind(); + } + } + + /** + * @return boolean whether child controls have been created + */ + final protected function getChildControlsCreated() + { + return ($this->_flags & self::IS_CHILD_CREATED)!==0; + } + + /** + * Sets a value indicating whether child controls are created. + * If false, any existing child controls will be cleared up. + * @param boolean whether child controls are created + */ + final protected function setChildControlsCreated($value) + { + if($value) + $this->_flags |= self::IS_CHILD_CREATED; + else + { + if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED)) + $this->getControls()->clear(); + $this->_flags &= ~self::IS_CHILD_CREATED; + } + } + + /** + * Ensures child controls are created. + * If child controls are not created yet, this method will invoke + * {@link createChildControls} to create them. + */ + public function ensureChildControls() + { + if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD)) + { + try + { + $this->_flags |= self::IS_CREATING_CHILD; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->createChildControls(); + else + $this->createChildControls(); + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + } + catch(Exception $e) + { + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + throw $e; + } + } + } + + /** + * Creates child controls. + * This method can be overriden for controls who want to have their controls. + * Do not call this method directly. Instead, call {@link ensureChildControls} + * to ensure child controls are created only once. + */ + public function createChildControls() + { + } + + /** + * Finds a control by ID path within the current naming container. + * The current naming container is either the control itself + * if it implements {@link INamingContainer} or the control's naming container. + * The ID path is an ID sequence separated by {@link TControl::ID_SEPARATOR}. + * For example, 'Repeater1.Item1.Button1' looks for a control with ID 'Button1' + * whose naming container is 'Item1' whose naming container is 'Repeater1'. + * @param string ID of the control to be looked up + * @return TControl|null the control found, null if not found + * @throws TInvalidDataValueException if a control's ID is found not unique within its naming container. + */ + public function findControl($id) + { + $id=strtr($id,'.',self::ID_SEPARATOR); + $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer(); + if(!$container || !$container->getHasControls()) + return null; + if(!isset($container->_rf[self::RF_NAMED_CONTROLS])) + { + $container->_rf[self::RF_NAMED_CONTROLS]=array(); + $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]); + } + if(($pos=strpos($id,self::ID_SEPARATOR))===false) + return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null; + else + { + $cid=substr($id,0,$pos); + $sid=substr($id,$pos+1); + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid])) + return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid); + else + return null; + } + } + + /** + * Finds all child and grand-child controls that are of the specified type. + * @param string the class name + * @param boolean whether the type comparison is strict or not. If false, controls of the parent classes of the specified class will also be returned. + * @return array list of controls found + */ + public function findControlsByType($type,$strict=true) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type)))) + $controls[]=$control; + if(($control instanceof TControl) && $control->getHasControls()) + $controls=array_merge($controls,$control->findControlsByType($type,$strict)); + } + } + return $controls; + } + + /** + * Finds all child and grand-child controls with the specified ID. + * Note, this method is different from {@link findControl} in that + * it searches through all controls that have this control as the ancestor + * while {@link findcontrol} only searches through controls that have this + * control as the direct naming container. + * @param string the ID being looked for + * @return array list of controls found + */ + public function findControlsByID($id) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if($control->_id===$id) + $controls[]=$control; + $controls=array_merge($controls,$control->findControlsByID($id)); + } + } + } + return $controls; + } + + /** + * Resets the control as a naming container. + * Only framework developers should use this method. + */ + public function clearNamingContainer() + { + unset($this->_rf[self::RF_NAMED_CONTROLS_ID]); + $this->clearNameTable(); + } + + /** + * Registers an object by a name. + * A registered object can be accessed like a public member variable. + * This method should only be used by framework and control developers. + * @param string name of the object + * @param object object to be declared + * @see __get + */ + public function registerObject($name,$object) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + throw new TInvalidOperationException('control_object_reregistered',$name); + $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object; + } + + /** + * Unregisters an object by name. + * @param string name of the object + * @see registerObject + */ + public function unregisterObject($name) + { + unset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + + /** + * @return boolean whether an object has been registered with the name + * @see registerObject + */ + public function isObjectRegistered($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + + /** + * @return boolean true if the child control has been initialized. + */ + public function getHasChildInitialized() + { + return $this->getControlStage() >= self::CS_CHILD_INITIALIZED; + } + + /** + * @return boolean true if the onInit event has raised. + */ + public function getHasInitialized() + { + return $this->getControlStage() >= self::CS_INITIALIZED; + } + + /** + * @return boolean true if the control has loaded post data. + */ + public function getHasLoadedPostData() + { + return $this->getControlStage() >= self::CS_STATE_LOADED; + } + + /** + * @return boolean true if the onLoad event has raised. + */ + public function getHasLoaded() + { + return $this->getControlStage() >= self::CS_LOADED; + } + + /** + * @return boolean true if onPreRender event has raised. + */ + public function getHasPreRendered() + { + return $this->getControlStage() >= self::CS_PRERENDERED; + } + + /** + * Returns the named registered object. + * A component with explicit ID on a template will be registered to + * the template owner. This method allows you to obtain this component + * with the ID. + * @return mixed the named registered object. Null if object is not found. + */ + public function getRegisteredObject($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null; + } + + /** + * @return boolean whether body contents are allowed for this control. Defaults to true. + */ + public function getAllowChildControls() + { + return true; + } + + /** + * Adds the object instantiated on a template to the child control collection. + * This method overrides the parent implementation. + * Only framework developers and control developers should use this method. + * @param string|TComponent text string or component parsed and instantiated in template + * @see createdOnTemplate + */ + public function addParsedObject($object) + { + $this->getControls()->add($object); + } + + /** + * Clears up the child state data. + * After a control loads its state, those state that do not belong to + * any existing child controls are stored as child state. + * This method will remove these state. + * Only frameworker developers and control developers should use this method. + */ + final protected function clearChildState() + { + unset($this->_rf[self::RF_CHILD_STATE]); + } + + /** + * @param TControl the potential ancestor control + * @return boolean if the control is a descendent (parent, parent of parent, etc.) + * of the specified control + */ + final protected function isDescendentOf($ancestor) + { + $control=$this; + while($control!==$ancestor && $control->_parent) + $control=$control->_parent; + return $control===$ancestor; + } + + /** + * Adds a control into the child collection of the control. + * Control lifecycles will be caught up during the addition. + * Only framework developers should use this method. + * @param TControl the new child control + */ + public function addedControl($control) + { + if($control->_parent) + $control->_parent->getControls()->remove($control); + $control->_parent=$this; + $control->_page=$this->getPage(); + $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer; + if($namingContainer) + { + $control->_namingContainer=$namingContainer; + if($control->_id==='') + $control->generateAutomaticID(); + else + $namingContainer->clearNameTable(); + $control->clearCachedUniqueID($control instanceof INamingContainer); + } + + if($this->_stage>=self::CS_CHILD_INITIALIZED) + { + $control->initRecursive($namingContainer); + if($this->_stage>=self::CS_STATE_LOADED) + { + if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id])) + { + $state=$this->_rf[self::RF_CHILD_STATE][$control->_id]; + unset($this->_rf[self::RF_CHILD_STATE][$control->_id]); + } + else + $state=null; + $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if($this->_stage>=self::CS_LOADED) + { + $control->loadRecursive(); + if($this->_stage>=self::CS_PRERENDERED) + $control->preRenderRecursive(); + } + } + } + } + + /** + * Removes a control from the child collection of the control. + * Only framework developers should use this method. + * @param TControl the child control removed + */ + public function removedControl($control) + { + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + $control->unloadRecursive(); + $control->_parent=null; + $control->_page=null; + $control->_namingContainer=null; + $control->_tplControl=null; + //$control->_stage=self::CS_CONSTRUCTED; + if(!($control->_flags & self::IS_ID_SET)) + $control->_id=''; + else + unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]); + $control->clearCachedUniqueID(true); + } + + /** + * Performs the Init step for the control and all its child controls. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->getHasControls()) + { + if($this instanceof INamingContainer) + $namingContainer=$this; + $page=$this->getPage(); + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->_namingContainer=$namingContainer; + $control->_page=$page; + if($control->_id==='' && $namingContainer) + $control->generateAutomaticID(); + $control->initRecursive($namingContainer); + } + } + } + if($this->_stage_stage=self::CS_CHILD_INITIALIZED; + if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED)) + { + $page->applyControlSkin($this); + $this->_flags |= self::IS_SKIN_APPLIED; + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onInit(null); + else + $this->onInit(null); + $this->_stage=self::CS_INITIALIZED; + } + } + + /** + * Performs the Load step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function loadRecursive() + { + if($this->_stage_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onLoad(null); + else + $this->onLoad(null); + } + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->loadRecursive(); + } + } + if($this->_stage_stage=self::CS_LOADED; + } + + /** + * Performs the PreRender step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function preRenderRecursive() + { + $this->autoDataBindProperties(); + + if($this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onPreRender(null); + else + $this->onPreRender(null); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->preRenderRecursive(); + else if($control instanceof TCompositeLiteral) + $control->evaluateDynamicContent(); + } + } + $this->addToPostDataLoader(); + } + $this->_stage=self::CS_PRERENDERED; + } + + /** + * Add controls implementing IPostBackDataHandler to post data loaders. + */ + protected function addToPostDataLoader() + { + if($this instanceof IPostBackDataHandler) + $this->getPage()->registerPostDataLoader($this); + } + + /** + * Performs the Unload step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function unloadRecursive() + { + if(!($this->_flags & self::IS_ID_SET)) + $this->_id=''; + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->unloadRecursive(); + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onUnload(null); + else + $this->onUnload(null); + } + + /** + * This method is invoked when the control enters 'OnInit' stage. + * The method raises 'OnInit' event. + * 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 + */ + public function onInit($param) + { + $this->raiseEvent('OnInit',$this,$param); + } + + /** + * This method is invoked when the control enters 'OnLoad' stage. + * The method raises 'OnLoad' event. + * 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 + */ + public function onLoad($param) + { + $this->raiseEvent('OnLoad',$this,$param); + } + + /** + * Raises 'OnDataBinding' event. + * This method is invoked when {@link dataBind} is invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onDataBinding($param) + { + Prado::trace("onDataBinding()",'System.Web.UI.TControl'); + $this->raiseEvent('OnDataBinding',$this,$param); + } + + + /** + * This method is invoked when the control enters 'OnUnload' stage. + * The method raises 'OnUnload' event. + * 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 + */ + public function onUnload($param) + { + $this->raiseEvent('OnUnload',$this,$param); + } + + /** + * This method is invoked when the control enters 'OnPreRender' stage. + * The method raises 'OnPreRender' event. + * 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 + */ + public function onPreRender($param) + { + $this->raiseEvent('OnPreRender',$this,$param); + } + + /** + * Invokes the parent's bubbleEvent method. + * A control who wants to bubble an event must call this method in its onEvent method. + * @param TControl sender of the event + * @param TEventParameter event parameter + * @see bubbleEvent + */ + protected function raiseBubbleEvent($sender,$param) + { + $control=$this; + while($control=$control->_parent) + { + if($control->bubbleEvent($sender,$param)) + break; + } + } + + /** + * This method responds to a bubbled event. + * This method should be overriden to provide customized response to a bubbled event. + * Check the type of event parameter to determine what event is bubbled currently. + * @param TControl sender of the event + * @param TEventParameter event parameters + * @return boolean true if the event bubbling is handled and no more bubbling. + * @see raiseBubbleEvent + */ + public function bubbleEvent($sender,$param) + { + return false; + } + + /** + * Broadcasts an event. + * The event will be sent to all controls on the current page hierarchy. + * If a control defines the event, the event will be raised for the control. + * If a control implements {@link IBroadcastEventReceiver}, its + * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} method will + * be invoked which gives the control a chance to respond to the event. + * For example, when broadcasting event 'OnClick', all controls having 'OnClick' + * event will have this event raised, and all controls implementing + * {@link IBroadcastEventReceiver} will also have its + * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} + * invoked. + * @param string name of the broadcast event + * @param TControl sender of this event + * @param TEventParameter event parameter + */ + public function broadcastEvent($name,$sender,$param) + { + $rootControl=(($page=$this->getPage())===null)?$this:$page; + $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param)); + } + + /** + * Recursively broadcasts an event. + * This method should only be used by framework developers. + * @param string name of the broadcast event + * @param TControl sender of the event + * @param TBroadcastEventParameter event parameter + */ + private function broadcastEventInternal($name,$sender,$param) + { + if($this->hasEvent($name)) + $this->raiseEvent($name,$sender,$param->getParameter()); + if($this instanceof IBroadcastEventReceiver) + $this->broadcastEventReceived($sender,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->broadcastEventInternal($name,$sender,$param); + } + } + } + + /** + * Traverse the whole control hierarchy rooted at this control. + * Callback function may be invoked for each control being visited. + * A pre-callback is invoked before traversing child controls; + * A post-callback is invoked after traversing child controls. + * Callback functions can be global functions or class methods. + * They must be of the following signature: + * + * function callback_func($control,$param) {...} + * + * where $control refers to the control being visited and $param + * is the parameter that is passed originally when calling this traverse function. + * + * @param mixed parameter to be passed to callbacks for each control + * @param callback callback invoked before traversing child controls. If null, it is ignored. + * @param callback callback invoked after traversing child controls. If null, it is ignored. + */ + protected function traverseChildControls($param,$preCallback=null,$postCallback=null) + { + if($preCallback!==null) + call_user_func($preCallback,$this,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->traverseChildControls($param,$preCallback,$postCallback); + } + } + } + if($postCallback!==null) + call_user_func($postCallback,$this,$param); + } + + /** + * Renders the control. + * Only when the control is visible will the control be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderControl($writer) + { + if($this instanceof IActiveControl || $this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->render($writer); + else + $this->render($writer); + } + } + + /** + * Renders the control. + * This method is invoked by {@link renderControl} when the control is visible. + * You can override this method to provide customized rendering of the control. + * By default, the control simply renders all its child contents. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + $this->renderChildren($writer); + } + + /** + * Renders the children of the control. + * This method iterates through all child controls and static text strings + * and renders them in order. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderChildren($writer) + { + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_string($control)) + $writer->write($control); + else if($control instanceof TControl) + $control->renderControl($writer); + else if($control instanceof IRenderable) + $control->render($writer); + } + } + } + + /** + * This method is invoked when control state is to be saved. + * You can override this method to do last step state saving. + * Parent implementation must be invoked. + */ + public function saveState() + { + } + + /** + * This method is invoked right after the control has loaded its state. + * You can override this method to initialize data from the control state. + * Parent implementation must be invoked. + */ + public function loadState() + { + } + + /** + * Loads state (viewstate and controlstate) into a control and its children. + * This method should only be used by framework developers. + * @param array the collection of the state + * @param boolean whether the viewstate should be loaded + */ + protected function loadStateRecursive(&$state,$needViewState=true) + { + if(is_array($state)) + { + // A null state means the stateful properties all take default values. + // So if the state is enabled, we have to assign the null value. + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if(isset($state[1])) + { + $this->_rf[self::RF_CONTROLSTATE]=&$state[1]; + unset($state[1]); + } + else + unset($this->_rf[self::RF_CONTROLSTATE]); + if($needViewState) + { + if(isset($state[0])) + $this->_viewState=&$state[0]; + else + $this->_viewState=array(); + } + unset($state[0]); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if(isset($state[$control->_id])) + { + $control->loadStateRecursive($state[$control->_id],$needViewState); + unset($state[$control->_id]); + } + } + } + } + if(!empty($state)) + $this->_rf[self::RF_CHILD_STATE]=&$state; + } + $this->_stage=self::CS_STATE_LOADED; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->loadState(); + else + $this->loadState(); + } + + /** + * Saves all control state (viewstate and controlstate) as a collection. + * This method should only be used by framework developers. + * @param boolean whether the viewstate should be saved + * @return array the collection of the control state (including its children's state). + */ + protected function &saveStateRecursive($needViewState=true) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->saveState(); + else + $this->saveState(); + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + $state=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $state[$control->_id]=&$control->saveStateRecursive($needViewState); + } + } + if($needViewState && !empty($this->_viewState)) + $state[0]=&$this->_viewState; + if(isset($this->_rf[self::RF_CONTROLSTATE])) + $state[1]=&$this->_rf[self::RF_CONTROLSTATE]; + return $state; + } + + /** + * Applies a stylesheet skin to a control. + * @param TPage the page containing the control + * @throws TInvalidOperationException if the stylesheet skin is applied already + */ + public function applyStyleSheetSkin($page) + { + if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED)) + { + $page->applyControlStyleSheet($this); + $this->_flags |= self::IS_STYLESHEET_APPLIED; + } + else if($this->_flags & self::IS_STYLESHEET_APPLIED) + throw new TInvalidOperationException('control_stylesheet_applied',get_class($this)); + } + + /** + * Clears the cached UniqueID. + * If $recursive=true, all children's cached UniqueID will be cleared as well. + * @param boolean whether the clearing is recursive. + */ + private function clearCachedUniqueID($recursive) + { + if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->clearCachedUniqueID($recursive); + } + $this->_uid=null; + } + + /** + * Generates an automatic ID for the control. + */ + private function generateAutomaticID() + { + $this->_flags &= ~self::IS_ID_SET; + if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID])) + $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0; + $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++; + $this->_id=self::AUTOMATIC_ID_PREFIX . $id; + $this->_namingContainer->clearNameTable(); + } + + /** + * Clears the list of the controls whose IDs are managed by the specified naming container. + */ + private function clearNameTable() + { + unset($this->_rf[self::RF_NAMED_CONTROLS]); + } + + /** + * Updates the list of the controls whose IDs are managed by the specified naming container. + * @param TControl the naming container + * @param TControlCollection list of controls + * @throws TInvalidDataValueException if a control's ID is not unique within its naming container. + */ + private function fillNameTable($container,$controls) + { + foreach($controls as $control) + { + if($control instanceof TControl) + { + if($control->_id!=='') + { + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id])) + throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id); + else + $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control; + } + if(!($control instanceof INamingContainer) && $control->getHasControls()) + $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]); + } + } + } +} + + +/** + * TControlCollection class + * + * TControlCollection implements a collection that enables + * controls to maintain a list of their child controls. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TControlCollection extends TList +{ + /** + * the control that owns this collection. + * @var TControl + */ + private $_o; + + /** + * Constructor. + * @param TControl the control that owns this collection. + * @param boolean whether the list is read-only + */ + public function __construct(TControl $owner,$readOnly=false) + { + $this->_o=$owner; + parent::__construct(null,$readOnly); + } + + /** + * @return TControl the control that owns this collection. + */ + protected function getOwner() + { + return $this->_o; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added child control. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + { + parent::insertAt($index,$item); + $this->_o->addedControl($item); + } + else if(is_string($item) || ($item instanceof IRenderable)) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('controlcollection_control_required'); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a child control. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TControl) + $this->_o->removedControl($item); + return $item; + } + + /** + * Overrides the parent implementation by invoking {@link TControl::clearNamingContainer} + */ + public function clear() + { + parent::clear(); + if($this->_o instanceof INamingContainer) + $this->_o->clearNamingContainer(); + } +} + +/** + * TEmptyControlCollection class + * + * TEmptyControlCollection implements an empty control list that prohibits adding + * controls to it. This is useful for controls that do not allow child controls. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TEmptyControlCollection extends TControlCollection +{ + /** + * Constructor. + * @param TControl the control that owns this collection. + */ + public function __construct(TControl $owner) + { + parent::__construct($owner,true); + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by ignoring new addition. + * @param integer the speicified position. + * @param mixed new item + */ + public function insertAt($index,$item) + { + if(!is_string($item)) // string is possible if property tag is used. we simply ignore it in this case + parent::insertAt($index,$item); // this will generate an exception in parent implementation + } +} + +/** + * INamingContainer interface. + * INamingContainer marks a control as a naming container. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface INamingContainer +{ +} + +/** + * IPostBackEventHandler interface + * + * If a control wants to respond to postback event, it must implement this interface. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IPostBackEventHandler +{ + /** + * Raises postback event. + * The implementation of this function should raise appropriate event(s) (e.g. OnClick, OnCommand) + * indicating the component is responsible for the postback event. + * @param string the parameter associated with the postback event + */ + public function raisePostBackEvent($param); +} + +/** + * IPostBackDataHandler interface + * + * If a control wants to load post data, it must implement this interface. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IPostBackDataHandler +{ + /** + * Loads user input data. + * The implementation of this function can use $values[$key] to get the user input + * data that are meant for the particular control. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the control has been changed + */ + public function loadPostData($key,$values); + /** + * Raises postdata changed event. + * The implementation of this function should raise appropriate event(s) (e.g. OnTextChanged) + * indicating the control data is changed. + */ + public function raisePostDataChangedEvent(); + /** + * @return boolean whether postback causes the data change. Defaults to false for non-postback state. + */ + public function getDataChanged(); +} + + +/** + * IValidator interface + * + * If a control wants to validate user input, it must implement this interface. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IValidator +{ + /** + * Validates certain data. + * The implementation of this function should validate certain data + * (e.g. data entered into TTextBox control). + * @return boolean whether the data passes the validation + */ + public function validate(); + /** + * @return boolean whether the previous {@link validate()} is successful. + */ + public function getIsValid(); + /** + * @param boolean whether the validator validates successfully + */ + public function setIsValid($value); + /** + * @return string error message during last validate + */ + public function getErrorMessage(); + /** + * @param string error message for the validation + */ + public function setErrorMessage($value); +} + + +/** + * IValidatable interface + * + * If a control wants to be validated by a validator, it must implement this interface. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IValidatable +{ + /** + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue(); + /** + * @return boolean wether this control's validators validated successfully (must default to true) + */ + public function getIsValid(); + /** + * @return boolean wether this control's validators validated successfully + */ + public function setIsValid($value); +} + +/** + * IBroadcastEventReceiver interface + * + * If a control wants to check broadcast event, it must implement this interface. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IBroadcastEventReceiver +{ + /** + * Handles broadcast event. + * This method is invoked automatically when an event is broadcasted. + * Within this method, you may check the event name given in + * the event parameter to determine whether you should respond to + * this event. + * @param TControl sender of the event + * @param TBroadCastEventParameter event parameter + */ + public function broadcastEventReceived($sender,$param); +} + +/** + * ITheme interface. + * + * This interface must be implemented by theme. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface ITheme +{ + /** + * Applies this theme to the specified control. + * @param TControl the control to be applied with this theme + */ + public function applySkin($control); +} + +/** + * ITemplate interface + * + * ITemplate specifies the interface for classes encapsulating + * parsed template structures. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface ITemplate +{ + /** + * Instantiates the template. + * Content in the template will be instantiated as components and text strings + * and passed to the specified parent control. + * @param TControl the parent control + */ + public function instantiateIn($parent); +} + +/** + * IButtonControl interface + * + * IButtonControl specifies the common properties and events that must + * be implemented by a button control, such as {@link TButton}, {@link TLinkButton}, + * {@link TImageButton}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +interface IButtonControl +{ + /** + * @return string caption of the button + */ + public function getText(); + + /** + * @param string caption of the button + */ + public function setText($value); + + /** + * @return boolean whether postback event trigger by this button will cause input validation + */ + public function getCausesValidation(); + + /** + * @param boolean whether postback event trigger by this button will cause input validation + */ + public function setCausesValidation($value); + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName(); + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value); + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter(); + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value); + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup(); + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value); + + /** + * Raises OnClick event. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param); + + /** + * Raises OnCommand event. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param); + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value); + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton(); +} + +/** + * ISurroundable interface + * + * Identifies controls that may create an additional surrounding tag. The id of the + * tag can be obtained with {@link getSurroundingTagID}. + * + * @package System.Web.UI + * @since 3.1.2 + */ +interface ISurroundable +{ + /** + * @return string the id of the embedding tag of the control or the control's clientID if not surrounded + */ + public function getSurroundingTagID(); +} + +/** + * TBroadcastEventParameter class + * + * TBroadcastEventParameter encapsulates the parameter data for + * events that are broadcasted. The name of of the event is specified via + * {@link setName Name} property while the event parameter is via + * {@link setParameter Parameter} property. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TBroadcastEventParameter extends TEventParameter +{ + private $_name; + private $_param; + + /** + * Constructor. + * @param string name of the broadcast event + * @param mixed parameter of the broadcast event + */ + public function __construct($name='',$parameter=null) + { + $this->_name=$name; + $this->_param=$parameter; + } + + /** + * @return string name of the broadcast event + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string name of the broadcast event + */ + public function setName($value) + { + $this->_name=$value; + } + + /** + * @return mixed parameter of the broadcast event + */ + public function getParameter() + { + return $this->_param; + } + + /** + * @param mixed parameter of the broadcast event + */ + public function setParameter($value) + { + $this->_param=$value; + } +} + +/** + * TCommandEventParameter class + * + * TCommandEventParameter encapsulates the parameter data for Command + * event of button controls. You can access the name of the command via + * {@link getCommandName CommandName} property, and the parameter carried + * with the command via {@link getCommandParameter CommandParameter} property. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TCommandEventParameter extends TEventParameter +{ + private $_name; + private $_param; + + /** + * Constructor. + * @param string name of the command + * @param string parameter of the command + */ + public function __construct($name='',$parameter='') + { + $this->_name=$name; + $this->_param=$parameter; + } + + /** + * @return string name of the command + */ + public function getCommandName() + { + return $this->_name; + } + + /** + * @return string parameter of the command + */ + public function getCommandParameter() + { + return $this->_param; + } +} + + +/** + * TCompositeLiteral class + * + * TCompositeLiteral is used internally by {@link TTemplate} for representing + * consecutive static strings, expressions and statements. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TCompositeLiteral extends TComponent implements IRenderable, IBindable +{ + const TYPE_EXPRESSION=0; + const TYPE_STATEMENTS=1; + const TYPE_DATABINDING=2; + private $_container=null; + private $_items=array(); + private $_expressions=array(); + private $_statements=array(); + private $_bindings=array(); + + /** + * Constructor. + * @param array list of items to be represented by TCompositeLiteral + */ + public function __construct($items) + { + $this->_items=array(); + $this->_expressions=array(); + $this->_statements=array(); + foreach($items as $id=>$item) + { + if(is_array($item)) + { + if($item[0]===self::TYPE_EXPRESSION) + $this->_expressions[$id]=$item[1]; + else if($item[0]===self::TYPE_STATEMENTS) + $this->_statements[$id]=$item[1]; + else if($item[0]===self::TYPE_DATABINDING) + $this->_bindings[$id]=$item[1]; + $this->_items[$id]=''; + } + else + $this->_items[$id]=$item; + } + } + + /** + * @return TComponent container of this component. It serves as the evaluation context of expressions and statements. + */ + public function getContainer() + { + return $this->_container; + } + + /** + * @param TComponent container of this component. It serves as the evaluation context of expressions and statements. + */ + public function setContainer(TComponent $value) + { + $this->_container=$value; + } + + /** + * Evaluates the expressions and/or statements in the component. + */ + public function evaluateDynamicContent() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_expressions as $id=>$expression) + $this->_items[$id]=$context->evaluateExpression($expression); + foreach($this->_statements as $id=>$statement) + $this->_items[$id]=$context->evaluateStatements($statement); + } + + /** + * Performs databindings. + * This method is required by {@link IBindable} + */ + public function dataBind() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_bindings as $id=>$binding) + $this->_items[$id]=$context->evaluateExpression($binding); + } + + /** + * Renders the content stored in this component. + * This method is required by {@link IRenderable} + * @param ITextWriter + */ + public function render($writer) + { + $writer->write(implode('',$this->_items)); + } +} + +?> diff --git a/framework/Web/UI/TControlAdapter.php b/framework/Web/UI/TControlAdapter.php index 5f06c360..a1afd353 100644 --- a/framework/Web/UI/TControlAdapter.php +++ b/framework/Web/UI/TControlAdapter.php @@ -1,143 +1,143 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TControlAdapter class - * - * TControlAdapter is the base class for adapters that customize - * various behaviors for the control to which the adapter is attached. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TControlAdapter extends TApplicationComponent -{ - /** - * @var TControl the control to which the adapter is attached - */ - protected $_control; - - /** - * Constructor. - * @param TControl the control to which the adapter is attached - */ - public function __construct($control) - { - $this->_control=$control; - } - - /** - * @return TControl the control to which this adapter is attached - */ - public function getControl() - { - return $this->_control; - } - - /** - * @return TPage the page that contains the attached control - */ - public function getPage() - { - return $this->_control?$this->_control->getPage():null; - } - - /** - * Creates child controls for the attached control. - * Default implementation calls the attached control's corresponding method. - */ - public function createChildControls() - { - $this->_control->createChildControls(); - } - - /** - * Loads additional persistent control state. - * Default implementation calls the attached control's corresponding method. - */ - public function loadState() - { - $this->_control->loadState(); - } - - /** - * Saves additional persistent control state. - * Default implementation calls the attached control's corresponding method. - */ - public function saveState() - { - $this->_control->saveState(); - } - - /** - * This method is invoked when the control enters 'OnInit' stage. - * Default implementation calls the attached control's corresponding method. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onInit($param) - { - $this->_control->onInit($param); - } - - /** - * This method is invoked when the control enters 'OnLoad' stage. - * Default implementation calls the attached control's corresponding method. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onLoad($param) - { - $this->_control->onLoad($param); - } - - /** - * This method is invoked when the control enters 'OnPreRender' stage. - * Default implementation calls the attached control's corresponding method. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onPreRender($param) - { - $this->_control->onPreRender($param); - } - - /** - * This method is invoked when the control enters 'OnUnload' stage. - * Default implementation calls the attached control's corresponding method. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onUnload($param) - { - $this->_control->onUnload($param); - } - - /** - * This method is invoked when the control renders itself. - * Default implementation calls the attached control's corresponding method. - * @param THtmlWriter writer for the rendering purpose - */ - public function render($writer) - { - $this->_control->render($writer); - } - - /** - * Renders the control's children. - * Default implementation calls the attached control's corresponding method. - * @param THtmlWriter writer for the rendering purpose - */ - public function renderChildren($writer) - { - $this->_control->renderChildren($writer); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TControlAdapter class + * + * TControlAdapter is the base class for adapters that customize + * various behaviors for the control to which the adapter is attached. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TControlAdapter extends TApplicationComponent +{ + /** + * @var TControl the control to which the adapter is attached + */ + protected $_control; + + /** + * Constructor. + * @param TControl the control to which the adapter is attached + */ + public function __construct($control) + { + $this->_control=$control; + } + + /** + * @return TControl the control to which this adapter is attached + */ + public function getControl() + { + return $this->_control; + } + + /** + * @return TPage the page that contains the attached control + */ + public function getPage() + { + return $this->_control?$this->_control->getPage():null; + } + + /** + * Creates child controls for the attached control. + * Default implementation calls the attached control's corresponding method. + */ + public function createChildControls() + { + $this->_control->createChildControls(); + } + + /** + * Loads additional persistent control state. + * Default implementation calls the attached control's corresponding method. + */ + public function loadState() + { + $this->_control->loadState(); + } + + /** + * Saves additional persistent control state. + * Default implementation calls the attached control's corresponding method. + */ + public function saveState() + { + $this->_control->saveState(); + } + + /** + * This method is invoked when the control enters 'OnInit' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onInit($param) + { + $this->_control->onInit($param); + } + + /** + * This method is invoked when the control enters 'OnLoad' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onLoad($param) + { + $this->_control->onLoad($param); + } + + /** + * This method is invoked when the control enters 'OnPreRender' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onPreRender($param) + { + $this->_control->onPreRender($param); + } + + /** + * This method is invoked when the control enters 'OnUnload' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onUnload($param) + { + $this->_control->onUnload($param); + } + + /** + * This method is invoked when the control renders itself. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + $this->_control->render($writer); + } + + /** + * Renders the control's children. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderChildren($writer) + { + $this->_control->renderChildren($writer); + } +} + diff --git a/framework/Web/UI/TForm.php b/framework/Web/UI/TForm.php index f57f7482..2d60a4e4 100644 --- a/framework/Web/UI/TForm.php +++ b/framework/Web/UI/TForm.php @@ -1,171 +1,171 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TForm class - * - * TForm displays an HTML form. Besides regular body content, - * it displays hidden fields, javascript blocks and files that are registered - * through {@link TClientScriptManager}. - * - * A TForm is required for a page that needs postback. - * Each page can contain at most one TForm. If multiple HTML forms are needed, - * please use regular HTML form tags for those forms that post to different - * URLs. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TForm extends TControl -{ - /** - * Registers the form with the page. - * @param mixed event parameter - */ - public function onInit($param) - { - parent::onInit($param); - $this->getPage()->setForm($this); - } - - /** - * Adds form specific attributes to renderer. - * @param THtmlWriter writer - */ - protected function addAttributesToRender($writer) - { - $writer->addAttribute('id',$this->getClientID()); - $writer->addAttribute('method',$this->getMethod()); - $uri=$this->getRequest()->getRequestURI(); - $writer->addAttribute('action',str_replace('&','&',str_replace('&','&',$uri))); - if(($enctype=$this->getEnctype())!=='') - $writer->addAttribute('enctype',$enctype); - - $attributes=$this->getAttributes(); - $attributes->remove('action'); - $writer->addAttributes($attributes); - - if(($butt=$this->getDefaultButton())!=='') - { - if(($button=$this->findControl($butt))!==null) - $this->getPage()->getClientScript()->registerDefaultButton($this, $button); - else - throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt); - } - } - - /** - * Renders the form. - * @param THtmlWriter writer - */ - public function render($writer) - { - $page=$this->getPage(); - - $this->addAttributesToRender($writer); - $writer->renderBeginTag('form'); - - $cs=$page->getClientScript(); - if($page->getClientSupportsJavaScript()) - { - $cs->renderHiddenFieldsBegin($writer); - $cs->renderScriptFilesBegin($writer); - $cs->renderBeginScripts($writer); - - $page->beginFormRender($writer); - $this->renderChildren($writer); - $cs->renderHiddenFieldsEnd($writer); - $page->endFormRender($writer); - - $cs->renderScriptFilesEnd($writer); - $cs->renderEndScripts($writer); - } - else - { - $cs->renderHiddenFieldsBegin($writer); - - $page->beginFormRender($writer); - $this->renderChildren($writer); - $page->endFormRender($writer); - - $cs->renderHiddenFieldsEnd($writer); - } - - $writer->renderEndTag(); - } - - /** - * @return string id path to the default button control. - */ - public function getDefaultButton() - { - return $this->getViewState('DefaultButton',''); - } - - /** - * Sets a button to be default one in a form. - * A default button will be clicked if a user presses 'Enter' key within - * the form. - * @param string id path to the default button control. - */ - public function setDefaultButton($value) - { - $this->setViewState('DefaultButton',$value,''); - } - - /** - * @return string form submission method. Defaults to 'post'. - */ - public function getMethod() - { - return $this->getViewState('Method','post'); - } - - /** - * @param string form submission method. Valid values include 'post' and 'get'. - */ - public function setMethod($value) - { - $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post'); - } - - /** - * @return string the encoding type a browser uses to post data back to the server - */ - public function getEnctype() - { - return $this->getViewState('Enctype',''); - } - - /** - * @param string the encoding type a browser uses to post data back to the server. - * Commonly used types include - * - application/x-www-form-urlencoded : Form data is encoded as name/value pairs. This is the standard encoding format. - * - multipart/form-data : Form data is encoded as a message with a separate part for each control on the page. - * - text/plain : Form data is encoded in plain text, without any control or formatting characters. - */ - public function setEnctype($value) - { - $this->setViewState('Enctype',$value,''); - } - - /** - * @return string form name, which is equal to {@link getUniqueID UniqueID}. - */ - public function getName() - { - return $this->getUniqueID(); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TForm class + * + * TForm displays an HTML form. Besides regular body content, + * it displays hidden fields, javascript blocks and files that are registered + * through {@link TClientScriptManager}. + * + * A TForm is required for a page that needs postback. + * Each page can contain at most one TForm. If multiple HTML forms are needed, + * please use regular HTML form tags for those forms that post to different + * URLs. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TForm extends TControl +{ + /** + * Registers the form with the page. + * @param mixed event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->getPage()->setForm($this); + } + + /** + * Adds form specific attributes to renderer. + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $writer->addAttribute('method',$this->getMethod()); + $uri=$this->getRequest()->getRequestURI(); + $writer->addAttribute('action',str_replace('&','&',str_replace('&','&',$uri))); + if(($enctype=$this->getEnctype())!=='') + $writer->addAttribute('enctype',$enctype); + + $attributes=$this->getAttributes(); + $attributes->remove('action'); + $writer->addAttributes($attributes); + + if(($butt=$this->getDefaultButton())!=='') + { + if(($button=$this->findControl($butt))!==null) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + else + throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt); + } + } + + /** + * Renders the form. + * @param THtmlWriter writer + */ + public function render($writer) + { + $page=$this->getPage(); + + $this->addAttributesToRender($writer); + $writer->renderBeginTag('form'); + + $cs=$page->getClientScript(); + if($page->getClientSupportsJavaScript()) + { + $cs->renderHiddenFieldsBegin($writer); + $cs->renderScriptFilesBegin($writer); + $cs->renderBeginScripts($writer); + + $page->beginFormRender($writer); + $this->renderChildren($writer); + $cs->renderHiddenFieldsEnd($writer); + $page->endFormRender($writer); + + $cs->renderScriptFilesEnd($writer); + $cs->renderEndScripts($writer); + } + else + { + $cs->renderHiddenFieldsBegin($writer); + + $page->beginFormRender($writer); + $this->renderChildren($writer); + $page->endFormRender($writer); + + $cs->renderHiddenFieldsEnd($writer); + } + + $writer->renderEndTag(); + } + + /** + * @return string id path to the default button control. + */ + public function getDefaultButton() + { + return $this->getViewState('DefaultButton',''); + } + + /** + * Sets a button to be default one in a form. + * A default button will be clicked if a user presses 'Enter' key within + * the form. + * @param string id path to the default button control. + */ + public function setDefaultButton($value) + { + $this->setViewState('DefaultButton',$value,''); + } + + /** + * @return string form submission method. Defaults to 'post'. + */ + public function getMethod() + { + return $this->getViewState('Method','post'); + } + + /** + * @param string form submission method. Valid values include 'post' and 'get'. + */ + public function setMethod($value) + { + $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post'); + } + + /** + * @return string the encoding type a browser uses to post data back to the server + */ + public function getEnctype() + { + return $this->getViewState('Enctype',''); + } + + /** + * @param string the encoding type a browser uses to post data back to the server. + * Commonly used types include + * - application/x-www-form-urlencoded : Form data is encoded as name/value pairs. This is the standard encoding format. + * - multipart/form-data : Form data is encoded as a message with a separate part for each control on the page. + * - text/plain : Form data is encoded in plain text, without any control or formatting characters. + */ + public function setEnctype($value) + { + $this->setViewState('Enctype',$value,''); + } + + /** + * @return string form name, which is equal to {@link getUniqueID UniqueID}. + */ + public function getName() + { + return $this->getUniqueID(); + } +} + diff --git a/framework/Web/UI/THtmlWriter.php b/framework/Web/UI/THtmlWriter.php index 96e1a5e1..9e264c77 100644 --- a/framework/Web/UI/THtmlWriter.php +++ b/framework/Web/UI/THtmlWriter.php @@ -1,229 +1,229 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * THtmlWriter class - * - * THtmlWriter is a writer that renders valid XHTML outputs. - * It provides functions to render tags, their attributes and stylesheet fields. - * Attribute and stylesheet values will be automatically HTML-encoded if - * they require so. For example, the 'value' attribute in an input tag - * will be encoded. - * - * A common usage of THtmlWriter is as the following sequence: - * - * $writer->addAttribute($name1,$value1); - * $writer->addAttribute($name2,$value2); - * $writer->renderBeginTag($tagName); - * // ... render contents enclosed within the tag here - * $writer->renderEndTag(); - * - * Make sure each invocation of {@link renderBeginTag} is accompanied with - * a {@link renderEndTag} and they are properly nested, like nesting - * tags in HTML and XHTML. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class THtmlWriter extends TApplicationComponent implements ITextWriter -{ - /** - * @var array list of tags are do not need a closing tag - */ - private static $_simpleTags=array( - 'area'=>true, - 'base'=>true, - 'basefont'=>true, - 'bgsound'=>true, - 'col'=>true, - 'embed'=>true, - 'frame'=>true, - 'hr'=>true, - 'img'=>true, - 'input'=>true, - 'isindex'=>true, - 'link'=>true, - 'meta'=>true, - 'wbr'=>true, - ); - /** - * @var array list of attributes to be rendered for a tag - */ - private $_attributes=array(); - /** - * @var array list of openning tags - */ - private $_openTags=array(); - /** - * @var array list of style attributes - */ - private $_styles=array(); - /** - * @var ITextWriter writer - */ - private $_writer=null; - - /** - * Constructor. - * @param ITextWriter a writer that THtmlWriter will pass its rendering result to - */ - public function __construct($writer) - { - $this->_writer=$writer; - } - - public function getWriter() - { - return $this->_writer; - } - - public function setWriter($writer) - { - $this->_writer = $writer; - } - /** - * Adds a list of attributes to be rendered. - * @param array list of attributes to be rendered - */ - public function addAttributes($attrs) - { - foreach($attrs as $name=>$value) - $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); - } - - /** - * Adds an attribute to be rendered. - * @param string name of the attribute - * @param string value of the attribute - */ - public function addAttribute($name,$value) - { - $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); - } - - /** - * Removes the named attribute from rendering - * @param string name of the attribute to be removed - */ - public function removeAttribute($name) - { - unset($this->_attributes[THttpUtility::htmlStrip($name)]); - } - - /** - * Adds a list of stylesheet attributes to be rendered. - * @param array list of stylesheet attributes to be rendered - */ - public function addStyleAttributes($attrs) - { - foreach($attrs as $name=>$value) - $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); - } - - /** - * Adds a stylesheet attribute to be rendered - * @param string stylesheet attribute name - * @param string stylesheet attribute value - */ - public function addStyleAttribute($name,$value) - { - $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); - } - - /** - * Removes the named stylesheet attribute from rendering - * @param string name of the stylesheet attribute to be removed - */ - public function removeStyleAttribute($name) - { - unset($this->_styles[THttpUtility::htmlStrip($name)]); - } - - /** - * Flushes the rendering result. - * This will invoke the underlying writer's flush method. - * @return string the content being flushed - */ - public function flush() - { - return $this->_writer->flush(); - } - - /** - * Renders a string. - * @param string string to be rendered - */ - public function write($str) - { - $this->_writer->write($str); - } - - /** - * Renders a string and appends a newline to it. - * @param string string to be rendered - */ - public function writeLine($str='') - { - $this->_writer->write($str."\n"); - } - - /** - * Renders an HTML break. - */ - public function writeBreak() - { - $this->_writer->write('
    '); - } - - /** - * Renders the openning tag. - * @param string tag name - */ - public function renderBeginTag($tagName) - { - $str='<'.$tagName; - foreach($this->_attributes as $name=>$value) - $str.=' '.$name.'="'.$value.'"'; - if(!empty($this->_styles)) - { - $str.=' style="'; - foreach($this->_styles as $name=>$value) - $str.=$name.':'.$value.';'; - $str.='"'; - } - if(isset(self::$_simpleTags[$tagName])) - { - $str.=' />'; - $this->_openTags[] = ''; - } - else - { - $str.='>'; - $this->_openTags[] = $tagName; - } - $this->_writer->write($str); - $this->_attributes=array(); - $this->_styles=array(); - } - - /** - * Renders the closing tag. - */ - public function renderEndTag() - { - if(!empty($this->_openTags) && ($tagName=array_pop($this->_openTags))!=='') - $this->_writer->write(''); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * THtmlWriter class + * + * THtmlWriter is a writer that renders valid XHTML outputs. + * It provides functions to render tags, their attributes and stylesheet fields. + * Attribute and stylesheet values will be automatically HTML-encoded if + * they require so. For example, the 'value' attribute in an input tag + * will be encoded. + * + * A common usage of THtmlWriter is as the following sequence: + * + * $writer->addAttribute($name1,$value1); + * $writer->addAttribute($name2,$value2); + * $writer->renderBeginTag($tagName); + * // ... render contents enclosed within the tag here + * $writer->renderEndTag(); + * + * Make sure each invocation of {@link renderBeginTag} is accompanied with + * a {@link renderEndTag} and they are properly nested, like nesting + * tags in HTML and XHTML. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class THtmlWriter extends TApplicationComponent implements ITextWriter +{ + /** + * @var array list of tags are do not need a closing tag + */ + private static $_simpleTags=array( + 'area'=>true, + 'base'=>true, + 'basefont'=>true, + 'bgsound'=>true, + 'col'=>true, + 'embed'=>true, + 'frame'=>true, + 'hr'=>true, + 'img'=>true, + 'input'=>true, + 'isindex'=>true, + 'link'=>true, + 'meta'=>true, + 'wbr'=>true, + ); + /** + * @var array list of attributes to be rendered for a tag + */ + private $_attributes=array(); + /** + * @var array list of openning tags + */ + private $_openTags=array(); + /** + * @var array list of style attributes + */ + private $_styles=array(); + /** + * @var ITextWriter writer + */ + private $_writer=null; + + /** + * Constructor. + * @param ITextWriter a writer that THtmlWriter will pass its rendering result to + */ + public function __construct($writer) + { + $this->_writer=$writer; + } + + public function getWriter() + { + return $this->_writer; + } + + public function setWriter($writer) + { + $this->_writer = $writer; + } + /** + * Adds a list of attributes to be rendered. + * @param array list of attributes to be rendered + */ + public function addAttributes($attrs) + { + foreach($attrs as $name=>$value) + $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Adds an attribute to be rendered. + * @param string name of the attribute + * @param string value of the attribute + */ + public function addAttribute($name,$value) + { + $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Removes the named attribute from rendering + * @param string name of the attribute to be removed + */ + public function removeAttribute($name) + { + unset($this->_attributes[THttpUtility::htmlStrip($name)]); + } + + /** + * Adds a list of stylesheet attributes to be rendered. + * @param array list of stylesheet attributes to be rendered + */ + public function addStyleAttributes($attrs) + { + foreach($attrs as $name=>$value) + $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Adds a stylesheet attribute to be rendered + * @param string stylesheet attribute name + * @param string stylesheet attribute value + */ + public function addStyleAttribute($name,$value) + { + $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Removes the named stylesheet attribute from rendering + * @param string name of the stylesheet attribute to be removed + */ + public function removeStyleAttribute($name) + { + unset($this->_styles[THttpUtility::htmlStrip($name)]); + } + + /** + * Flushes the rendering result. + * This will invoke the underlying writer's flush method. + * @return string the content being flushed + */ + public function flush() + { + return $this->_writer->flush(); + } + + /** + * Renders a string. + * @param string string to be rendered + */ + public function write($str) + { + $this->_writer->write($str); + } + + /** + * Renders a string and appends a newline to it. + * @param string string to be rendered + */ + public function writeLine($str='') + { + $this->_writer->write($str."\n"); + } + + /** + * Renders an HTML break. + */ + public function writeBreak() + { + $this->_writer->write('
    '); + } + + /** + * Renders the openning tag. + * @param string tag name + */ + public function renderBeginTag($tagName) + { + $str='<'.$tagName; + foreach($this->_attributes as $name=>$value) + $str.=' '.$name.'="'.$value.'"'; + if(!empty($this->_styles)) + { + $str.=' style="'; + foreach($this->_styles as $name=>$value) + $str.=$name.':'.$value.';'; + $str.='"'; + } + if(isset(self::$_simpleTags[$tagName])) + { + $str.=' />'; + $this->_openTags[] = ''; + } + else + { + $str.='>'; + $this->_openTags[] = $tagName; + } + $this->_writer->write($str); + $this->_attributes=array(); + $this->_styles=array(); + } + + /** + * Renders the closing tag. + */ + public function renderEndTag() + { + if(!empty($this->_openTags) && ($tagName=array_pop($this->_openTags))!=='') + $this->_writer->write(''); + } +} + diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 37326031..26a06b64 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -1,1341 +1,1341 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -Prado::using('System.Web.UI.WebControls.*'); -Prado::using('System.Web.UI.TControl'); -Prado::using('System.Web.UI.WebControls.TWebControl'); -Prado::using('System.Web.UI.TCompositeControl'); -Prado::using('System.Web.UI.TTemplateControl'); -Prado::using('System.Web.UI.TForm'); -Prado::using('System.Web.UI.TClientScriptManager'); - -/** - * TPage class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TPage extends TTemplateControl -{ - /** - * system post fields - */ - const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; - const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; - const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; - const FIELD_PAGESTATE='PRADO_PAGESTATE'; - const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; - const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; - - /** - * @var array system post fields - */ - private static $_systemPostFields=array( - 'PRADO_POSTBACK_TARGET'=>true, - 'PRADO_POSTBACK_PARAMETER'=>true, - 'PRADO_LASTFOCUS'=>true, - 'PRADO_PAGESTATE'=>true, - 'PRADO_CALLBACK_TARGET'=>true, - 'PRADO_CALLBACK_PARAMETER'=>true - ); - /** - * @var TForm form instance - */ - private $_form; - /** - * @var THead head instance - */ - private $_head; - /** - * @var array list of registered validators - */ - private $_validators=array(); - /** - * @var boolean if validation has been performed - */ - private $_validated=false; - /** - * @var TTheme page theme - */ - private $_theme; - /** - * @var string page title set when Head is not in page yet - */ - private $_title; - /** - * @var TTheme page stylesheet theme - */ - private $_styleSheet; - /** - * @var TClientScriptManager client script manager - */ - private $_clientScript; - /** - * @var TMap data post back by user - */ - protected $_postData; - /** - * @var TMap postback data that is not handled during first invocation of LoadPostData. - */ - protected $_restPostData; - /** - * @var array list of controls whose data have been changed due to the postback - */ - protected $_controlsPostDataChanged=array(); - /** - * @var array list of controls that need to load post data in the current request - */ - protected $_controlsRequiringPostData=array(); - /** - * @var array list of controls that need to load post data in the next postback - */ - protected $_controlsRegisteredForPostData=array(); - /** - * @var TControl control that needs to raise postback event - */ - private $_postBackEventTarget; - /** - * @var string postback event parameter - */ - private $_postBackEventParameter; - /** - * @var boolean whether the form has been rendered - */ - protected $_formRendered=false; - /** - * @var boolean whether the current rendering is within a form - */ - protected $_inFormRender=false; - /** - * @var TControl|string the control or the ID of the element on the page to be focused when the page is sent back to user - */ - private $_focus; - /** - * @var string page path to this page - */ - private $_pagePath=''; - /** - * @var boolean whether page state should be HMAC validated - */ - private $_enableStateValidation=true; - /** - * @var boolean whether page state should be encrypted - */ - private $_enableStateEncryption=false; - /** - * @var boolean whether page state should be compressed - * @since 3.1.6 - */ - private $_enableStateCompression=true; - /** - * @var string page state persister class name - */ - private $_statePersisterClass='System.Web.UI.TPageStatePersister'; - /** - * @var mixed page state persister - */ - private $_statePersister; - /** - * @var TStack stack used to store currently active caching controls - */ - private $_cachingStack; - /** - * @var string state string to be stored on the client side - */ - private $_clientState=''; - /** - * @var array post data loader IDs. - */ - protected $_postDataLoaders=array(); - /** - * @var boolean true if loading post data. - */ - protected $_isLoadingPostData=false; - /** - * @var boolean whether client supports javascript - */ - private $_enableJavaScript=true; - /** - * @var THtmlWriter current html render writer - */ - private $_writer; - - /** - * Constructor. - * Sets the page object to itself. - * Derived classes must call parent implementation. - */ - public function __construct() - { - $this->setPage($this); - } - - /** - * Runs through the page lifecycles. - * @param THtmlTextWriter the HTML writer - */ - public function run($writer) - { - Prado::trace("Running page life cycles",'System.Web.UI.TPage'); - $this->_writer = $writer; - - $this->determinePostBackMode(); - - if($this->getIsPostBack()) - { - if($this->getIsCallback()) - $this->processCallbackRequest($writer); - else - $this->processPostBackRequest($writer); - } - else - $this->processNormalRequest($writer); - - $this->_writer = null; - } - - protected function processNormalRequest($writer) - { - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - protected function processPostBackRequest($writer) - { - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - $this->_restPostData=new TMap; - Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); - $this->loadPageState(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_postData,true); - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_restPostData,false); - Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); - $this->raiseChangedEvents(); - Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); - $this->raisePostBackEvent(); - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - protected static function decodeUTF8($data, $enc) - { - if(is_array($data)) - { - foreach($data as $k=>$v) - $data[$k]=self::decodeUTF8($v, $enc); - return $data; - } elseif(is_string($data)) { - return iconv('UTF-8',$enc.'//IGNORE',$data); - } else { - return $data; - } - } - - /** - * Sets Adapter to TActivePageAdapter and calls apter to process the - * callback request. - */ - protected function processCallbackRequest($writer) - { - Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); - - $this->setAdapter(new TActivePageAdapter($this)); - - // Decode Callback postData from UTF-8 to current Charset - if (($g=$this->getApplication()->getGlobalization(false))!==null && - strtoupper($enc=$g->getCharset())!='UTF-8') - foreach ($this->_postData as $k=>$v) - $this->_postData[$k]=self::decodeUTF8($v, $enc); - - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - $this->_restPostData=new TMap; - Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); - $this->loadPageState(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_postData,true); - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_restPostData,false); - - Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); - $this->raiseChangedEvents(); - - - $this->getAdapter()->processCallbackEvent($writer); - -/* - Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); - $this->raisePostBackEvent(); -*/ - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - -/* - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); -*/ - $this->getAdapter()->renderCallbackResponse($writer); - - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - /** - * Gets the callback client script handler that allows javascript functions - * to be executed during the callback response. - * @return TCallbackClientScript interface to client-side javascript code. - */ - public function getCallbackClient() - { - if($this->getAdapter() !== null) - return $this->getAdapter()->getCallbackClientHandler(); - else - return new TCallbackClientScript(); - } - - /** - * Set a new callback client handler. - * @param TCallbackClientScript new callback client script handler. - */ - public function setCallbackClient($client) - { - $this->getAdapter()->setCallbackClientHandler($client); - } - - /** - * @return TControl the control responsible for the current callback event, - * null if nonexistent - */ - public function getCallbackEventTarget() - { - return $this->getAdapter()->getCallbackEventTarget(); - } - - /** - * Registers a control to raise callback event in the current request. - * @param TControl control registered to raise callback event. - */ - public function setCallbackEventTarget(TControl $control) - { - $this->getAdapter()->setCallbackEventTarget($control); - } - - /** - * Callback parameter is decoded assuming JSON encoding. - * @return string callback event parameter - */ - public function getCallbackEventParameter() - { - return $this->getAdapter()->getCallbackEventParameter(); - } - - /** - * @param mixed callback event parameter - */ - public function setCallbackEventParameter($value) - { - $this->getAdapter()->setCallbackEventParameter($value); - } - - /** - * Register post data loaders for Callback to collect post data. - * This method should only be called by framework developers. - * @param TControl control that requires post data. - * @see TControl::preRenderRecursive(); - */ - public function registerPostDataLoader($control) - { - $id=is_string($control)?$control:$control->getUniqueID(); - $this->_postDataLoaders[$id] = true; - } - - /** - * Get a list of IDs of controls that are enabled and require post data. - * @return array list of IDs implementing IPostBackDataHandler - */ - public function getPostDataLoaders() - { - return array_keys($this->_postDataLoaders); - } - - /** - * @return TForm the form on the page - */ - public function getForm() - { - return $this->_form; - } - - /** - * Registers a TForm instance to the page. - * Note, a page can contain at most one TForm instance. - * @param TForm the form on the page - * @throws TInvalidOperationException if this method is invoked twice or more. - */ - public function setForm(TForm $form) - { - if($this->_form===null) - $this->_form=$form; - else - throw new TInvalidOperationException('page_form_duplicated'); - } - - /** - * Returns a list of registered validators. - * If validation group is specified, only the validators in that group will be returned. - * @param string validation group - * @return TList registered validators in the requested group. If the group is null, all validators will be returned. - */ - public function getValidators($validationGroup=null) - { - if(!$this->_validators) - $this->_validators=new TList; - if(empty($validationGroup) === true) - return $this->_validators; - else - { - $list=new TList; - foreach($this->_validators as $validator) - if($validator->getValidationGroup()===$validationGroup) - $list->add($validator); - return $list; - } - } - - /** - * Performs input validation. - * This method will invoke the registered validators to perform the actual validation. - * If validation group is specified, only the validators in that group will be invoked. - * @param string validation group. If null, all validators will perform validation. - */ - public function validate($validationGroup=null) - { - Prado::trace("Page validate()",'System.Web.UI.TPage'); - $this->_validated=true; - if($this->_validators && $this->_validators->getCount()) - { - if($validationGroup===null) - { - foreach($this->_validators as $validator) - $validator->validate(); - } - else - { - foreach($this->_validators as $validator) - { - if($validator->getValidationGroup()===$validationGroup) - $validator->validate(); - } - } - } - } - - /** - * Returns whether user input is valid or not. - * This method must be invoked after {@link validate} is called. - * @return boolean whether the user input is valid or not. - * @throws TInvalidOperationException if {@link validate} is not invoked yet. - */ - public function getIsValid() - { - if($this->_validated) - { - if($this->_validators && $this->_validators->getCount()) - { - foreach($this->_validators as $validator) - if(!$validator->getIsValid()) - return false; - } - return true; - } - else - throw new TInvalidOperationException('page_isvalid_unknown'); - } - - /** - * @return TTheme the theme used for the page. Defaults to null. - */ - public function getTheme() - { - if(is_string($this->_theme)) - $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); - return $this->_theme; - } - - /** - * Sets the theme to be used for the page. - * @param string|TTheme the theme name or the theme object to be used for the page. - */ - public function setTheme($value) - { - $this->_theme=empty($value)?null:$value; - } - - - /** - * @return TTheme the stylesheet theme used for the page. Defaults to null. - */ - public function getStyleSheetTheme() - { - if(is_string($this->_styleSheet)) - $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); - return $this->_styleSheet; - } - - /** - * Sets the stylesheet theme to be used for the page. - * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page. - */ - public function setStyleSheetTheme($value) - { - $this->_styleSheet=empty($value)?null:$value; - } - - /** - * Applies a skin in the current theme to a control. - * This method should only be used by framework developers. - * @param TControl a control to be applied skin with - */ - public function applyControlSkin($control) - { - if(($theme=$this->getTheme())!==null) - $theme->applySkin($control); - } - - /** - * Applies a stylesheet skin in the current theme to a control. - * This method should only be used by framework developers. - * @param TControl a control to be applied stylesheet skin with - */ - public function applyControlStyleSheet($control) - { - if(($theme=$this->getStyleSheetTheme())!==null) - $theme->applySkin($control); - } - - /** - * @return TClientScriptManager client script manager - */ - public function getClientScript() - { - if(!$this->_clientScript) { - $className = $classPath = $this->getService()->getClientScriptManagerClass(); - Prado::using($className); - if(($pos=strrpos($className,'.'))!==false) - $className=substr($className,$pos+1); - - if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) - throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); - - $this->_clientScript=new $className($this); - } - return $this->_clientScript; - } - - /** - * Raises OnPreInit event. - * This method is invoked right before {@link onInit OnInit} stage. - * You may override this method to provide additional initialization that - * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or - * {@link setStyleSheetTheme StyleSheetTheme}). - * Remember to call the parent implementation to ensure OnPreInit event is raised. - * @param mixed event parameter - */ - public function onPreInit($param) - { - $this->raiseEvent('OnPreInit',$this,$param); - } - - /** - * Raises OnInitComplete event. - * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage. - * You may override this method to provide additional initialization that - * should be done after {@link onInit OnInit}. - * Remember to call the parent implementation to ensure OnInitComplete event is raised. - * @param mixed event parameter - */ - public function onInitComplete($param) - { - $this->raiseEvent('OnInitComplete',$this,$param); - } - - /** - * Raises OnPreLoad event. - * This method is invoked right before {@link onLoad OnLoad} stage. - * You may override this method to provide additional page loading logic that - * should be done before {@link onLoad OnLoad}. - * Remember to call the parent implementation to ensure OnPreLoad event is raised. - * @param mixed event parameter - */ - public function onPreLoad($param) - { - $this->raiseEvent('OnPreLoad',$this,$param); - } - - /** - * Raises OnLoadComplete event. - * This method is invoked right after {@link onLoad OnLoad} stage. - * You may override this method to provide additional page loading logic that - * should be done after {@link onLoad OnLoad}. - * Remember to call the parent implementation to ensure OnLoadComplete event is raised. - * @param mixed event parameter - */ - public function onLoadComplete($param) - { - $this->raiseEvent('OnLoadComplete',$this,$param); - } - - /** - * Raises OnPreRenderComplete event. - * This method is invoked right after {@link onPreRender OnPreRender} stage. - * You may override this method to provide additional preparation for page rendering - * that should be done after {@link onPreRender OnPreRender}. - * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised. - * @param mixed event parameter - */ - public function onPreRenderComplete($param) - { - $this->raiseEvent('OnPreRenderComplete',$this,$param); - $cs=$this->getClientScript(); - $theme=$this->getTheme(); - if($theme instanceof ITheme) - { - foreach($theme->getStyleSheetFiles() as $url) - $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); - foreach($theme->getJavaScriptFiles() as $url) - $cs->registerHeadScriptFile($url,$url); - } - $styleSheet=$this->getStyleSheetTheme(); - if($styleSheet instanceof ITheme) - { - foreach($styleSheet->getStyleSheetFiles() as $url) - $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); - foreach($styleSheet->getJavaScriptFiles() as $url) - $cs->registerHeadScriptFile($url,$url); - } - - if($cs->getRequiresHead() && $this->getHead()===null) - throw new TConfigurationException('page_head_required'); - } - - /** - * Determines the media type of the CSS file. - * The media type is determined according to the following file name pattern: - * xxx.media-type.extension - * For example, 'mystyle.print.css' means its media type is 'print'. - * @param string CSS URL - * @return string media type of the CSS file - */ - private function getCssMediaType($url) - { - $segs=explode('.',basename($url)); - if(isset($segs[2])) - return $segs[count($segs)-2]; - else - return ''; - } - - /** - * Raises OnSaveStateComplete event. - * This method is invoked right after {@link onSaveState OnSaveState} stage. - * You may override this method to provide additional logic after page state is saved. - * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised. - * @param mixed event parameter - */ - public function onSaveStateComplete($param) - { - $this->raiseEvent('OnSaveStateComplete',$this,$param); - } - - /** - * Determines whether the current page request is a postback. - * Call {@link getIsPostBack} to get the result. - */ - private function determinePostBackMode() - { - $postData=$this->getRequest(); - if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) - $this->_postData=$postData; - } - - /** - * @return boolean whether the current page request is a postback - */ - public function getIsPostBack() - { - return $this->_postData!==null; - } - - /** - * @return boolean whether this is a callback request - */ - public function getIsCallback() - { - return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); - } - - /** - * This method is invoked when control state is to be saved. - * You can override this method to do last step state saving. - * Parent implementation must be invoked. - */ - public function saveState() - { - parent::saveState(); - $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); - } - - /** - * This method is invoked right after the control has loaded its state. - * You can override this method to initialize data from the control state. - * Parent implementation must be invoked. - */ - public function loadState() - { - parent::loadState(); - $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); - } - - /** - * Loads page state from persistent storage. - */ - protected function loadPageState() - { - Prado::trace("Loading state",'System.Web.UI.TPage'); - $state=$this->getStatePersister()->load(); - $this->loadStateRecursive($state,$this->getEnableViewState()); - } - - /** - * Saves page state from persistent storage. - */ - protected function savePageState() - { - Prado::trace("Saving state",'System.Web.UI.TPage'); - $state=&$this->saveStateRecursive($this->getEnableViewState()); - $this->getStatePersister()->save($state); - } - - /** - * @param string the field name - * @return boolean whether the specified field is a system field in postback data - */ - protected function isSystemPostField($field) - { - return isset(self::$_systemPostFields[$field]); - } - - /** - * Registers a control for loading post data in the next postback. - * This method needs to be invoked if the control to load post data - * may not have a post variable in some cases. For example, a checkbox, - * if not checked, will not have a post value. - * @param TControl control registered for loading post data - */ - public function registerRequiresPostData($control) - { - $id=is_string($control)?$control:$control->getUniqueID(); - $this->_controlsRegisteredForPostData[$id]=true; - $this->registerPostDataLoader($id); - $params=func_get_args(); - foreach($this->getCachingStack() as $item) - $item->registerAction('Page','registerRequiresPostData',array($id)); - } - - /** - * @return TControl the control responsible for the current postback event, null if nonexistent - */ - public function getPostBackEventTarget() - { - if($this->_postBackEventTarget===null && $this->_postData!==null) - { - $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); - if(!empty($eventTarget)) - $this->_postBackEventTarget=$this->findControl($eventTarget); - } - return $this->_postBackEventTarget; - } - - /** - * Registers a control to raise postback event in the current request. - * @param TControl control registered to raise postback event. - */ - public function setPostBackEventTarget(TControl $control) - { - $this->_postBackEventTarget=$control; - } - - /** - * @return string postback event parameter - */ - public function getPostBackEventParameter() - { - if($this->_postBackEventParameter===null && $this->_postData!==null) - { - if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) - $this->_postBackEventParameter=''; - } - return $this->_postBackEventParameter; - } - - /** - * @param string postback event parameter - */ - public function setPostBackEventParameter($value) - { - $this->_postBackEventParameter=$value; - } - - /** - * Processes post data. - * @param TMap post data to be processed - * @param boolean whether this method is invoked before {@link onLoad OnLoad}. - */ - protected function processPostData($postData,$beforeLoad) - { - $this->_isLoadingPostData=true; - if($beforeLoad) - $this->_restPostData=new TMap; - foreach($postData as $key=>$value) - { - if($this->isSystemPostField($key)) - continue; - else if($control=$this->findControl($key)) - { - if($control instanceof IPostBackDataHandler) - { - if($control->loadPostData($key,$postData)) - $this->_controlsPostDataChanged[]=$control; - } - else if($control instanceof IPostBackEventHandler && - empty($this->_postData[self::FIELD_POSTBACK_TARGET])) - { - $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); // not calling setPostBackEventTarget() because the control may be removed later - } - unset($this->_controlsRequiringPostData[$key]); - } - else if($beforeLoad) - $this->_restPostData->add($key,$value); - } - - foreach($this->_controlsRequiringPostData as $key=>$value) - { - if($control=$this->findControl($key)) - { - if($control instanceof IPostBackDataHandler) - { - if($control->loadPostData($key,$this->_postData)) - $this->_controlsPostDataChanged[]=$control; - } - else - throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); - unset($this->_controlsRequiringPostData[$key]); - } - } - $this->_isLoadingPostData=false; - } - - /** - * @return boolean true if loading post data. - */ - public function getIsLoadingPostData() - { - return $this->_isLoadingPostData; - } - - /** - * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback. - */ - protected function raiseChangedEvents() - { - foreach($this->_controlsPostDataChanged as $control) - $control->raisePostDataChangedEvent(); - } - - /** - * Raises PostBack event. - */ - protected function raisePostBackEvent() - { - if(($postBackHandler=$this->getPostBackEventTarget())===null) - $this->validate(); - else if($postBackHandler instanceof IPostBackEventHandler) - $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); - } - - /** - * @return boolean Whether form rendering is in progress - */ - public function getInFormRender() - { - return $this->_inFormRender; - } - - /** - * Ensures the control is rendered within a form. - * @param TControl the control to be rendered - * @throws TConfigurationException if the control is outside of the form - */ - public function ensureRenderInForm($control) - { - if(!$this->getIsCallback() && !$this->_inFormRender) - throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); - } - - /** - * @internal This method is invoked by TForm at the beginning of its rendering - */ - public function beginFormRender($writer) - { - if($this->_formRendered) - throw new TConfigurationException('page_form_duplicated'); - $this->_formRendered=true; - $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); - $this->_inFormRender=true; - } - - /** - * @internal This method is invoked by TForm at the end of its rendering - */ - public function endFormRender($writer) - { - if($this->_focus) - { - if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) - $focus=$this->_focus->getClientID(); - else - $focus=$this->_focus; - $this->getClientScript()->registerFocusControl($focus); - } - else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) - $this->getClientScript()->registerFocusControl($lastFocus); - $this->_inFormRender=false; - } - - /** - * Sets input focus on a control after the page is rendered to users. - * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus - */ - public function setFocus($value) - { - $this->_focus=$value; - } - - /** - * @return boolean whether client supports javascript. Defaults to true. - */ - public function getClientSupportsJavaScript() - { - return $this->_enableJavaScript; - } - - /** - * @param boolean whether client supports javascript. If false, javascript will not be generated for controls. - */ - public function setClientSupportsJavaScript($value) - { - $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); - } - - /** - * @return THead page head, null if not available - */ - public function getHead() - { - return $this->_head; - } - - /** - * @param THead page head - * @throws TInvalidOperationException if a head already exists - */ - public function setHead(THead $value) - { - if($this->_head) - throw new TInvalidOperationException('page_head_duplicated'); - $this->_head=$value; - if($this->_title!==null) - { - $this->_head->setTitle($this->_title); - $this->_title=null; - } - } - - /** - * @return string page title. - */ - public function getTitle() - { - if($this->_head) - return $this->_head->getTitle(); - else - return $this->_title===null ? '' : $this->_title; - } - - /** - * Sets the page title. - * Note, a {@link THead} control needs to place on the page - * in order that this title be rendered. - * @param string page title. This will override the title set in {@link getHead Head}. - */ - public function setTitle($value) - { - if($this->_head) - $this->_head->setTitle($value); - else - $this->_title=$value; - } - - /** - * Returns the state to be stored on the client side. - * This method should only be used by framework and control developers. - * @return string the state to be stored on the client side - */ - public function getClientState() - { - return $this->_clientState; - } - - /** - * Sets the state to be stored on the client side. - * This method should only be used by framework and control developers. - * @param string the state to be stored on the client side - */ - public function setClientState($state) - { - $this->_clientState=$state; - } - - /** - * @return string the state postback from client side - */ - public function getRequestClientState() - { - return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); - } - - /** - * @return string class name of the page state persister. Defaults to TPageStatePersister. - */ - public function getStatePersisterClass() - { - return $this->_statePersisterClass; - } - - /** - * @param string class name of the page state persister. - */ - public function setStatePersisterClass($value) - { - $this->_statePersisterClass=$value; - } - - /** - * @return IPageStatePersister page state persister - */ - public function getStatePersister() - { - if($this->_statePersister===null) - { - $this->_statePersister=Prado::createComponent($this->_statePersisterClass); - if(!($this->_statePersister instanceof IPageStatePersister)) - throw new TInvalidDataTypeException('page_statepersister_invalid'); - $this->_statePersister->setPage($this); - } - return $this->_statePersister; - } - - /** - * @return boolean whether page state should be HMAC validated. Defaults to true. - */ - public function getEnableStateValidation() - { - return $this->_enableStateValidation; - } - - /** - * @param boolean whether page state should be HMAC validated. - */ - public function setEnableStateValidation($value) - { - $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether page state should be encrypted. Defaults to false. - */ - public function getEnableStateEncryption() - { - return $this->_enableStateEncryption; - } - - /** - * @param boolean whether page state should be encrypted. - */ - public function setEnableStateEncryption($value) - { - $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether page state should be compressed. Defaults to true. - * @since 3.1.6 - */ - public function getEnableStateCompression() - { - return $this->_enableStateCompression; - } - - /** - * @param boolean whether page state should be compressed. - * @since 3.1.6 - */ - public function setEnableStateCompression($value) - { - $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); - } - - /** - * @return string the requested page path for this page - */ - public function getPagePath() - { - return $this->_pagePath; - } - - /** - * @param string the requested page path for this page - */ - public function setPagePath($value) - { - $this->_pagePath=$value; - } - - /** - * Registers an action associated with the content being cached. - * The registered action will be replayed if the content stored - * in the cache is served to end-users. - * @param string context of the action method. This is a property-path - * referring to the context object (e.g. Page, Page.ClientScript). - * @param string method name of the context object - * @param array list of parameters to be passed to the action method - */ - public function registerCachingAction($context,$funcName,$funcParams) - { - if($this->_cachingStack) - { - foreach($this->_cachingStack as $cache) - $cache->registerAction($context,$funcName,$funcParams); - } - } - - /** - * @return TStack stack of {@link TOutputCache} objects - */ - public function getCachingStack() - { - if(!$this->_cachingStack) - $this->_cachingStack=new TStack; - return $this->_cachingStack; - } - - /** - * Flushes output - */ - public function flushWriter() - { - if ($this->_writer) - $this->Response->write($this->_writer->flush()); - } -} - -/** - * IPageStatePersister interface. - * - * IPageStatePersister interface is required for all page state persister - * classes. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.1 - */ -interface IPageStatePersister -{ - /** - * @param TPage the page that this persister works for - */ - public function getPage(); - /** - * @param TPage the page that this persister works for - */ - public function setPage(TPage $page); - /** - * Saves state to persistent storage. - * @param mixed state to be stored - */ - public function save($state); - /** - * Loads page state from persistent storage - * @return mixed the restored state - */ - public function load(); -} - - -/** - * TPageStateFormatter class. - * - * TPageStateFormatter is a utility class to transform the page state - * into and from a string that can be properly saved in persistent storage. - * - * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation} - * and {@link TPage::getEnableStateEncryption() EnableStateEncryption}, - * TPageStateFormatter may do HMAC validation and encryption to prevent - * the state data from being tampered or viewed. - * The private keys and hashing/encryption methods are determined by - * {@link TApplication::getSecurityManager() SecurityManager}. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Web.UI - * @since 3.1 - */ -class TPageStateFormatter -{ - /** - * @param TPage - * @param mixed state data - * @return string serialized data - */ - public static function serialize($page,$data) - { - $sm=$page->getApplication()->getSecurityManager(); - if($page->getEnableStateValidation()) - $str=$sm->hashData(Prado::serialize($data)); - else - $str=Prado::serialize($data); - if($page->getEnableStateCompression() && extension_loaded('zlib')) - $str=gzcompress($str); - if($page->getEnableStateEncryption()) - $str=$sm->encrypt($str); - return base64_encode($str); - } - - /** - * @param TPage - * @param string serialized data - * @return mixed unserialized state data, null if data is corrupted - */ - public static function unserialize($page,$data) - { - $str=base64_decode($data); - if($str==='') - return null; - if($str!==false) - { - $sm=$page->getApplication()->getSecurityManager(); - if($page->getEnableStateEncryption()) - $str=$sm->decrypt($str); - if($page->getEnableStateCompression() && extension_loaded('zlib')) - $str=@gzuncompress($str); - if($page->getEnableStateValidation()) - { - if(($str=$sm->validateData($str))!==false) - return Prado::unserialize($str); - } - else - return Prado::unserialize($str); - } - return null; - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +Prado::using('System.Web.UI.WebControls.*'); +Prado::using('System.Web.UI.TControl'); +Prado::using('System.Web.UI.WebControls.TWebControl'); +Prado::using('System.Web.UI.TCompositeControl'); +Prado::using('System.Web.UI.TTemplateControl'); +Prado::using('System.Web.UI.TForm'); +Prado::using('System.Web.UI.TClientScriptManager'); + +/** + * TPage class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TPage extends TTemplateControl +{ + /** + * system post fields + */ + const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; + const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; + const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; + const FIELD_PAGESTATE='PRADO_PAGESTATE'; + const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; + const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; + + /** + * @var array system post fields + */ + private static $_systemPostFields=array( + 'PRADO_POSTBACK_TARGET'=>true, + 'PRADO_POSTBACK_PARAMETER'=>true, + 'PRADO_LASTFOCUS'=>true, + 'PRADO_PAGESTATE'=>true, + 'PRADO_CALLBACK_TARGET'=>true, + 'PRADO_CALLBACK_PARAMETER'=>true + ); + /** + * @var TForm form instance + */ + private $_form; + /** + * @var THead head instance + */ + private $_head; + /** + * @var array list of registered validators + */ + private $_validators=array(); + /** + * @var boolean if validation has been performed + */ + private $_validated=false; + /** + * @var TTheme page theme + */ + private $_theme; + /** + * @var string page title set when Head is not in page yet + */ + private $_title; + /** + * @var TTheme page stylesheet theme + */ + private $_styleSheet; + /** + * @var TClientScriptManager client script manager + */ + private $_clientScript; + /** + * @var TMap data post back by user + */ + protected $_postData; + /** + * @var TMap postback data that is not handled during first invocation of LoadPostData. + */ + protected $_restPostData; + /** + * @var array list of controls whose data have been changed due to the postback + */ + protected $_controlsPostDataChanged=array(); + /** + * @var array list of controls that need to load post data in the current request + */ + protected $_controlsRequiringPostData=array(); + /** + * @var array list of controls that need to load post data in the next postback + */ + protected $_controlsRegisteredForPostData=array(); + /** + * @var TControl control that needs to raise postback event + */ + private $_postBackEventTarget; + /** + * @var string postback event parameter + */ + private $_postBackEventParameter; + /** + * @var boolean whether the form has been rendered + */ + protected $_formRendered=false; + /** + * @var boolean whether the current rendering is within a form + */ + protected $_inFormRender=false; + /** + * @var TControl|string the control or the ID of the element on the page to be focused when the page is sent back to user + */ + private $_focus; + /** + * @var string page path to this page + */ + private $_pagePath=''; + /** + * @var boolean whether page state should be HMAC validated + */ + private $_enableStateValidation=true; + /** + * @var boolean whether page state should be encrypted + */ + private $_enableStateEncryption=false; + /** + * @var boolean whether page state should be compressed + * @since 3.1.6 + */ + private $_enableStateCompression=true; + /** + * @var string page state persister class name + */ + private $_statePersisterClass='System.Web.UI.TPageStatePersister'; + /** + * @var mixed page state persister + */ + private $_statePersister; + /** + * @var TStack stack used to store currently active caching controls + */ + private $_cachingStack; + /** + * @var string state string to be stored on the client side + */ + private $_clientState=''; + /** + * @var array post data loader IDs. + */ + protected $_postDataLoaders=array(); + /** + * @var boolean true if loading post data. + */ + protected $_isLoadingPostData=false; + /** + * @var boolean whether client supports javascript + */ + private $_enableJavaScript=true; + /** + * @var THtmlWriter current html render writer + */ + private $_writer; + + /** + * Constructor. + * Sets the page object to itself. + * Derived classes must call parent implementation. + */ + public function __construct() + { + $this->setPage($this); + } + + /** + * Runs through the page lifecycles. + * @param THtmlTextWriter the HTML writer + */ + public function run($writer) + { + Prado::trace("Running page life cycles",'System.Web.UI.TPage'); + $this->_writer = $writer; + + $this->determinePostBackMode(); + + if($this->getIsPostBack()) + { + if($this->getIsCallback()) + $this->processCallbackRequest($writer); + else + $this->processPostBackRequest($writer); + } + else + $this->processNormalRequest($writer); + + $this->_writer = null; + } + + protected function processNormalRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected function processPostBackRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected static function decodeUTF8($data, $enc) + { + if(is_array($data)) + { + foreach($data as $k=>$v) + $data[$k]=self::decodeUTF8($v, $enc); + return $data; + } elseif(is_string($data)) { + return iconv('UTF-8',$enc.'//IGNORE',$data); + } else { + return $data; + } + } + + /** + * Sets Adapter to TActivePageAdapter and calls apter to process the + * callback request. + */ + protected function processCallbackRequest($writer) + { + Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); + + $this->setAdapter(new TActivePageAdapter($this)); + + // Decode Callback postData from UTF-8 to current Charset + if (($g=$this->getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + foreach ($this->_postData as $k=>$v) + $this->_postData[$k]=self::decodeUTF8($v, $enc); + + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + + + $this->getAdapter()->processCallbackEvent($writer); + +/* + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); +*/ + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + +/* + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); +*/ + $this->getAdapter()->renderCallbackResponse($writer); + + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + /** + * Gets the callback client script handler that allows javascript functions + * to be executed during the callback response. + * @return TCallbackClientScript interface to client-side javascript code. + */ + public function getCallbackClient() + { + if($this->getAdapter() !== null) + return $this->getAdapter()->getCallbackClientHandler(); + else + return new TCallbackClientScript(); + } + + /** + * Set a new callback client handler. + * @param TCallbackClientScript new callback client script handler. + */ + public function setCallbackClient($client) + { + $this->getAdapter()->setCallbackClientHandler($client); + } + + /** + * @return TControl the control responsible for the current callback event, + * null if nonexistent + */ + public function getCallbackEventTarget() + { + return $this->getAdapter()->getCallbackEventTarget(); + } + + /** + * Registers a control to raise callback event in the current request. + * @param TControl control registered to raise callback event. + */ + public function setCallbackEventTarget(TControl $control) + { + $this->getAdapter()->setCallbackEventTarget($control); + } + + /** + * Callback parameter is decoded assuming JSON encoding. + * @return string callback event parameter + */ + public function getCallbackEventParameter() + { + return $this->getAdapter()->getCallbackEventParameter(); + } + + /** + * @param mixed callback event parameter + */ + public function setCallbackEventParameter($value) + { + $this->getAdapter()->setCallbackEventParameter($value); + } + + /** + * Register post data loaders for Callback to collect post data. + * This method should only be called by framework developers. + * @param TControl control that requires post data. + * @see TControl::preRenderRecursive(); + */ + public function registerPostDataLoader($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_postDataLoaders[$id] = true; + } + + /** + * Get a list of IDs of controls that are enabled and require post data. + * @return array list of IDs implementing IPostBackDataHandler + */ + public function getPostDataLoaders() + { + return array_keys($this->_postDataLoaders); + } + + /** + * @return TForm the form on the page + */ + public function getForm() + { + return $this->_form; + } + + /** + * Registers a TForm instance to the page. + * Note, a page can contain at most one TForm instance. + * @param TForm the form on the page + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public function setForm(TForm $form) + { + if($this->_form===null) + $this->_form=$form; + else + throw new TInvalidOperationException('page_form_duplicated'); + } + + /** + * Returns a list of registered validators. + * If validation group is specified, only the validators in that group will be returned. + * @param string validation group + * @return TList registered validators in the requested group. If the group is null, all validators will be returned. + */ + public function getValidators($validationGroup=null) + { + if(!$this->_validators) + $this->_validators=new TList; + if(empty($validationGroup) === true) + return $this->_validators; + else + { + $list=new TList; + foreach($this->_validators as $validator) + if($validator->getValidationGroup()===$validationGroup) + $list->add($validator); + return $list; + } + } + + /** + * Performs input validation. + * This method will invoke the registered validators to perform the actual validation. + * If validation group is specified, only the validators in that group will be invoked. + * @param string validation group. If null, all validators will perform validation. + */ + public function validate($validationGroup=null) + { + Prado::trace("Page validate()",'System.Web.UI.TPage'); + $this->_validated=true; + if($this->_validators && $this->_validators->getCount()) + { + if($validationGroup===null) + { + foreach($this->_validators as $validator) + $validator->validate(); + } + else + { + foreach($this->_validators as $validator) + { + if($validator->getValidationGroup()===$validationGroup) + $validator->validate(); + } + } + } + } + + /** + * Returns whether user input is valid or not. + * This method must be invoked after {@link validate} is called. + * @return boolean whether the user input is valid or not. + * @throws TInvalidOperationException if {@link validate} is not invoked yet. + */ + public function getIsValid() + { + if($this->_validated) + { + if($this->_validators && $this->_validators->getCount()) + { + foreach($this->_validators as $validator) + if(!$validator->getIsValid()) + return false; + } + return true; + } + else + throw new TInvalidOperationException('page_isvalid_unknown'); + } + + /** + * @return TTheme the theme used for the page. Defaults to null. + */ + public function getTheme() + { + if(is_string($this->_theme)) + $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); + return $this->_theme; + } + + /** + * Sets the theme to be used for the page. + * @param string|TTheme the theme name or the theme object to be used for the page. + */ + public function setTheme($value) + { + $this->_theme=empty($value)?null:$value; + } + + + /** + * @return TTheme the stylesheet theme used for the page. Defaults to null. + */ + public function getStyleSheetTheme() + { + if(is_string($this->_styleSheet)) + $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); + return $this->_styleSheet; + } + + /** + * Sets the stylesheet theme to be used for the page. + * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page. + */ + public function setStyleSheetTheme($value) + { + $this->_styleSheet=empty($value)?null:$value; + } + + /** + * Applies a skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied skin with + */ + public function applyControlSkin($control) + { + if(($theme=$this->getTheme())!==null) + $theme->applySkin($control); + } + + /** + * Applies a stylesheet skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied stylesheet skin with + */ + public function applyControlStyleSheet($control) + { + if(($theme=$this->getStyleSheetTheme())!==null) + $theme->applySkin($control); + } + + /** + * @return TClientScriptManager client script manager + */ + public function getClientScript() + { + if(!$this->_clientScript) { + $className = $classPath = $this->getService()->getClientScriptManagerClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + + if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) + throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); + + $this->_clientScript=new $className($this); + } + return $this->_clientScript; + } + + /** + * Raises OnPreInit event. + * This method is invoked right before {@link onInit OnInit} stage. + * You may override this method to provide additional initialization that + * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or + * {@link setStyleSheetTheme StyleSheetTheme}). + * Remember to call the parent implementation to ensure OnPreInit event is raised. + * @param mixed event parameter + */ + public function onPreInit($param) + { + $this->raiseEvent('OnPreInit',$this,$param); + } + + /** + * Raises OnInitComplete event. + * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage. + * You may override this method to provide additional initialization that + * should be done after {@link onInit OnInit}. + * Remember to call the parent implementation to ensure OnInitComplete event is raised. + * @param mixed event parameter + */ + public function onInitComplete($param) + { + $this->raiseEvent('OnInitComplete',$this,$param); + } + + /** + * Raises OnPreLoad event. + * This method is invoked right before {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done before {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnPreLoad event is raised. + * @param mixed event parameter + */ + public function onPreLoad($param) + { + $this->raiseEvent('OnPreLoad',$this,$param); + } + + /** + * Raises OnLoadComplete event. + * This method is invoked right after {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done after {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnLoadComplete event is raised. + * @param mixed event parameter + */ + public function onLoadComplete($param) + { + $this->raiseEvent('OnLoadComplete',$this,$param); + } + + /** + * Raises OnPreRenderComplete event. + * This method is invoked right after {@link onPreRender OnPreRender} stage. + * You may override this method to provide additional preparation for page rendering + * that should be done after {@link onPreRender OnPreRender}. + * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised. + * @param mixed event parameter + */ + public function onPreRenderComplete($param) + { + $this->raiseEvent('OnPreRenderComplete',$this,$param); + $cs=$this->getClientScript(); + $theme=$this->getTheme(); + if($theme instanceof ITheme) + { + foreach($theme->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($theme->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + $styleSheet=$this->getStyleSheetTheme(); + if($styleSheet instanceof ITheme) + { + foreach($styleSheet->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($styleSheet->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + + if($cs->getRequiresHead() && $this->getHead()===null) + throw new TConfigurationException('page_head_required'); + } + + /** + * Determines the media type of the CSS file. + * The media type is determined according to the following file name pattern: + * xxx.media-type.extension + * For example, 'mystyle.print.css' means its media type is 'print'. + * @param string CSS URL + * @return string media type of the CSS file + */ + private function getCssMediaType($url) + { + $segs=explode('.',basename($url)); + if(isset($segs[2])) + return $segs[count($segs)-2]; + else + return ''; + } + + /** + * Raises OnSaveStateComplete event. + * This method is invoked right after {@link onSaveState OnSaveState} stage. + * You may override this method to provide additional logic after page state is saved. + * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised. + * @param mixed event parameter + */ + public function onSaveStateComplete($param) + { + $this->raiseEvent('OnSaveStateComplete',$this,$param); + } + + /** + * Determines whether the current page request is a postback. + * Call {@link getIsPostBack} to get the result. + */ + private function determinePostBackMode() + { + $postData=$this->getRequest(); + if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) + $this->_postData=$postData; + } + + /** + * @return boolean whether the current page request is a postback + */ + public function getIsPostBack() + { + return $this->_postData!==null; + } + + /** + * @return boolean whether this is a callback request + */ + public function getIsCallback() + { + return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); + } + + /** + * This method is invoked when control state is to be saved. + * You can override this method to do last step state saving. + * Parent implementation must be invoked. + */ + public function saveState() + { + parent::saveState(); + $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); + } + + /** + * This method is invoked right after the control has loaded its state. + * You can override this method to initialize data from the control state. + * Parent implementation must be invoked. + */ + public function loadState() + { + parent::loadState(); + $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); + } + + /** + * Loads page state from persistent storage. + */ + protected function loadPageState() + { + Prado::trace("Loading state",'System.Web.UI.TPage'); + $state=$this->getStatePersister()->load(); + $this->loadStateRecursive($state,$this->getEnableViewState()); + } + + /** + * Saves page state from persistent storage. + */ + protected function savePageState() + { + Prado::trace("Saving state",'System.Web.UI.TPage'); + $state=&$this->saveStateRecursive($this->getEnableViewState()); + $this->getStatePersister()->save($state); + } + + /** + * @param string the field name + * @return boolean whether the specified field is a system field in postback data + */ + protected function isSystemPostField($field) + { + return isset(self::$_systemPostFields[$field]); + } + + /** + * Registers a control for loading post data in the next postback. + * This method needs to be invoked if the control to load post data + * may not have a post variable in some cases. For example, a checkbox, + * if not checked, will not have a post value. + * @param TControl control registered for loading post data + */ + public function registerRequiresPostData($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_controlsRegisteredForPostData[$id]=true; + $this->registerPostDataLoader($id); + $params=func_get_args(); + foreach($this->getCachingStack() as $item) + $item->registerAction('Page','registerRequiresPostData',array($id)); + } + + /** + * @return TControl the control responsible for the current postback event, null if nonexistent + */ + public function getPostBackEventTarget() + { + if($this->_postBackEventTarget===null && $this->_postData!==null) + { + $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); + if(!empty($eventTarget)) + $this->_postBackEventTarget=$this->findControl($eventTarget); + } + return $this->_postBackEventTarget; + } + + /** + * Registers a control to raise postback event in the current request. + * @param TControl control registered to raise postback event. + */ + public function setPostBackEventTarget(TControl $control) + { + $this->_postBackEventTarget=$control; + } + + /** + * @return string postback event parameter + */ + public function getPostBackEventParameter() + { + if($this->_postBackEventParameter===null && $this->_postData!==null) + { + if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) + $this->_postBackEventParameter=''; + } + return $this->_postBackEventParameter; + } + + /** + * @param string postback event parameter + */ + public function setPostBackEventParameter($value) + { + $this->_postBackEventParameter=$value; + } + + /** + * Processes post data. + * @param TMap post data to be processed + * @param boolean whether this method is invoked before {@link onLoad OnLoad}. + */ + protected function processPostData($postData,$beforeLoad) + { + $this->_isLoadingPostData=true; + if($beforeLoad) + $this->_restPostData=new TMap; + foreach($postData as $key=>$value) + { + if($this->isSystemPostField($key)) + continue; + else if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$postData)) + $this->_controlsPostDataChanged[]=$control; + } + else if($control instanceof IPostBackEventHandler && + empty($this->_postData[self::FIELD_POSTBACK_TARGET])) + { + $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); // not calling setPostBackEventTarget() because the control may be removed later + } + unset($this->_controlsRequiringPostData[$key]); + } + else if($beforeLoad) + $this->_restPostData->add($key,$value); + } + + foreach($this->_controlsRequiringPostData as $key=>$value) + { + if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$this->_postData)) + $this->_controlsPostDataChanged[]=$control; + } + else + throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); + unset($this->_controlsRequiringPostData[$key]); + } + } + $this->_isLoadingPostData=false; + } + + /** + * @return boolean true if loading post data. + */ + public function getIsLoadingPostData() + { + return $this->_isLoadingPostData; + } + + /** + * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback. + */ + protected function raiseChangedEvents() + { + foreach($this->_controlsPostDataChanged as $control) + $control->raisePostDataChangedEvent(); + } + + /** + * Raises PostBack event. + */ + protected function raisePostBackEvent() + { + if(($postBackHandler=$this->getPostBackEventTarget())===null) + $this->validate(); + else if($postBackHandler instanceof IPostBackEventHandler) + $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); + } + + /** + * @return boolean Whether form rendering is in progress + */ + public function getInFormRender() + { + return $this->_inFormRender; + } + + /** + * Ensures the control is rendered within a form. + * @param TControl the control to be rendered + * @throws TConfigurationException if the control is outside of the form + */ + public function ensureRenderInForm($control) + { + if(!$this->getIsCallback() && !$this->_inFormRender) + throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); + } + + /** + * @internal This method is invoked by TForm at the beginning of its rendering + */ + public function beginFormRender($writer) + { + if($this->_formRendered) + throw new TConfigurationException('page_form_duplicated'); + $this->_formRendered=true; + $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); + $this->_inFormRender=true; + } + + /** + * @internal This method is invoked by TForm at the end of its rendering + */ + public function endFormRender($writer) + { + if($this->_focus) + { + if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) + $focus=$this->_focus->getClientID(); + else + $focus=$this->_focus; + $this->getClientScript()->registerFocusControl($focus); + } + else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) + $this->getClientScript()->registerFocusControl($lastFocus); + $this->_inFormRender=false; + } + + /** + * Sets input focus on a control after the page is rendered to users. + * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus + */ + public function setFocus($value) + { + $this->_focus=$value; + } + + /** + * @return boolean whether client supports javascript. Defaults to true. + */ + public function getClientSupportsJavaScript() + { + return $this->_enableJavaScript; + } + + /** + * @param boolean whether client supports javascript. If false, javascript will not be generated for controls. + */ + public function setClientSupportsJavaScript($value) + { + $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); + } + + /** + * @return THead page head, null if not available + */ + public function getHead() + { + return $this->_head; + } + + /** + * @param THead page head + * @throws TInvalidOperationException if a head already exists + */ + public function setHead(THead $value) + { + if($this->_head) + throw new TInvalidOperationException('page_head_duplicated'); + $this->_head=$value; + if($this->_title!==null) + { + $this->_head->setTitle($this->_title); + $this->_title=null; + } + } + + /** + * @return string page title. + */ + public function getTitle() + { + if($this->_head) + return $this->_head->getTitle(); + else + return $this->_title===null ? '' : $this->_title; + } + + /** + * Sets the page title. + * Note, a {@link THead} control needs to place on the page + * in order that this title be rendered. + * @param string page title. This will override the title set in {@link getHead Head}. + */ + public function setTitle($value) + { + if($this->_head) + $this->_head->setTitle($value); + else + $this->_title=$value; + } + + /** + * Returns the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @return string the state to be stored on the client side + */ + public function getClientState() + { + return $this->_clientState; + } + + /** + * Sets the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @param string the state to be stored on the client side + */ + public function setClientState($state) + { + $this->_clientState=$state; + } + + /** + * @return string the state postback from client side + */ + public function getRequestClientState() + { + return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); + } + + /** + * @return string class name of the page state persister. Defaults to TPageStatePersister. + */ + public function getStatePersisterClass() + { + return $this->_statePersisterClass; + } + + /** + * @param string class name of the page state persister. + */ + public function setStatePersisterClass($value) + { + $this->_statePersisterClass=$value; + } + + /** + * @return IPageStatePersister page state persister + */ + public function getStatePersister() + { + if($this->_statePersister===null) + { + $this->_statePersister=Prado::createComponent($this->_statePersisterClass); + if(!($this->_statePersister instanceof IPageStatePersister)) + throw new TInvalidDataTypeException('page_statepersister_invalid'); + $this->_statePersister->setPage($this); + } + return $this->_statePersister; + } + + /** + * @return boolean whether page state should be HMAC validated. Defaults to true. + */ + public function getEnableStateValidation() + { + return $this->_enableStateValidation; + } + + /** + * @param boolean whether page state should be HMAC validated. + */ + public function setEnableStateValidation($value) + { + $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be encrypted. Defaults to false. + */ + public function getEnableStateEncryption() + { + return $this->_enableStateEncryption; + } + + /** + * @param boolean whether page state should be encrypted. + */ + public function setEnableStateEncryption($value) + { + $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be compressed. Defaults to true. + * @since 3.1.6 + */ + public function getEnableStateCompression() + { + return $this->_enableStateCompression; + } + + /** + * @param boolean whether page state should be compressed. + * @since 3.1.6 + */ + public function setEnableStateCompression($value) + { + $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the requested page path for this page + */ + public function getPagePath() + { + return $this->_pagePath; + } + + /** + * @param string the requested page path for this page + */ + public function setPagePath($value) + { + $this->_pagePath=$value; + } + + /** + * Registers an action associated with the content being cached. + * The registered action will be replayed if the content stored + * in the cache is served to end-users. + * @param string context of the action method. This is a property-path + * referring to the context object (e.g. Page, Page.ClientScript). + * @param string method name of the context object + * @param array list of parameters to be passed to the action method + */ + public function registerCachingAction($context,$funcName,$funcParams) + { + if($this->_cachingStack) + { + foreach($this->_cachingStack as $cache) + $cache->registerAction($context,$funcName,$funcParams); + } + } + + /** + * @return TStack stack of {@link TOutputCache} objects + */ + public function getCachingStack() + { + if(!$this->_cachingStack) + $this->_cachingStack=new TStack; + return $this->_cachingStack; + } + + /** + * Flushes output + */ + public function flushWriter() + { + if ($this->_writer) + $this->Response->write($this->_writer->flush()); + } +} + +/** + * IPageStatePersister interface. + * + * IPageStatePersister interface is required for all page state persister + * classes. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.1 + */ +interface IPageStatePersister +{ + /** + * @param TPage the page that this persister works for + */ + public function getPage(); + /** + * @param TPage the page that this persister works for + */ + public function setPage(TPage $page); + /** + * Saves state to persistent storage. + * @param mixed state to be stored + */ + public function save($state); + /** + * Loads page state from persistent storage + * @return mixed the restored state + */ + public function load(); +} + + +/** + * TPageStateFormatter class. + * + * TPageStateFormatter is a utility class to transform the page state + * into and from a string that can be properly saved in persistent storage. + * + * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation} + * and {@link TPage::getEnableStateEncryption() EnableStateEncryption}, + * TPageStateFormatter may do HMAC validation and encryption to prevent + * the state data from being tampered or viewed. + * The private keys and hashing/encryption methods are determined by + * {@link TApplication::getSecurityManager() SecurityManager}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI + * @since 3.1 + */ +class TPageStateFormatter +{ + /** + * @param TPage + * @param mixed state data + * @return string serialized data + */ + public static function serialize($page,$data) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateValidation()) + $str=$sm->hashData(Prado::serialize($data)); + else + $str=Prado::serialize($data); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=gzcompress($str); + if($page->getEnableStateEncryption()) + $str=$sm->encrypt($str); + return base64_encode($str); + } + + /** + * @param TPage + * @param string serialized data + * @return mixed unserialized state data, null if data is corrupted + */ + public static function unserialize($page,$data) + { + $str=base64_decode($data); + if($str==='') + return null; + if($str!==false) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateEncryption()) + $str=$sm->decrypt($str); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=@gzuncompress($str); + if($page->getEnableStateValidation()) + { + if(($str=$sm->validateData($str))!==false) + return Prado::unserialize($str); + } + else + return Prado::unserialize($str); + } + return null; + } +} diff --git a/framework/Web/UI/TPageStatePersister.php b/framework/Web/UI/TPageStatePersister.php index 18590a77..183cde13 100644 --- a/framework/Web/UI/TPageStatePersister.php +++ b/framework/Web/UI/TPageStatePersister.php @@ -1,71 +1,71 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TPageStatePersister class - * - * TPageStatePersister implements a page state persistent method based on - * form hidden fields. - * - * Since page state can be very big for complex pages, consider using - * alternative persisters, such as {@link TSessionPageStatePersister}, - * which store page state on the server side and thus reduce the network - * traffic for transmitting bulky page state. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TPageStatePersister extends TComponent implements IPageStatePersister -{ - private $_page; - - /** - * @return TPage the page that this persister works for - */ - public function getPage() - { - return $this->_page; - } - - /** - * @param TPage the page that this persister works for - */ - public function setPage(TPage $page) - { - $this->_page=$page; - } - - /** - * Saves state in hidden fields. - * @param mixed state to be stored - */ - public function save($state) - { - $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$state)); - } - - /** - * Loads page state from hidden fields. - * @return mixed the restored state - * @throws THttpException if page state is corrupted - */ - public function load() - { - if(($data=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) - return $data; - else - throw new THttpException(400,'pagestatepersister_pagestate_corrupted'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TPageStatePersister class + * + * TPageStatePersister implements a page state persistent method based on + * form hidden fields. + * + * Since page state can be very big for complex pages, consider using + * alternative persisters, such as {@link TSessionPageStatePersister}, + * which store page state on the server side and thus reduce the network + * traffic for transmitting bulky page state. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TPageStatePersister extends TComponent implements IPageStatePersister +{ + private $_page; + + /** + * @return TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * Saves state in hidden fields. + * @param mixed state to be stored + */ + public function save($state) + { + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$state)); + } + + /** + * Loads page state from hidden fields. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($data=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + return $data; + else + throw new THttpException(400,'pagestatepersister_pagestate_corrupted'); + } +} + diff --git a/framework/Web/UI/TSessionPageStatePersister.php b/framework/Web/UI/TSessionPageStatePersister.php index 4c2537df..a6d26f7a 100644 --- a/framework/Web/UI/TSessionPageStatePersister.php +++ b/framework/Web/UI/TSessionPageStatePersister.php @@ -1,131 +1,131 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * TSessionPageStatePersister class - * - * TSessionPageStatePersister implements a page state persistent method based on - * sessions. Page state are stored in user sessions and therefore, this persister - * requires session to be started and available. - * - * TSessionPageStatePersister keeps limited number of history states in session, - * mainly to preserve the precious server storage. The number is specified - * by {@link setHistorySize HistorySize}, which defaults to 10. - * - * There are a couple of ways to use TSessionPageStatePersister. - * One can override the page's {@link TPage::getStatePersister()} method and - * create a TSessionPageStatePersister instance there. - * Or one can configure the pages to use TSessionPageStatePersister in page configurations - * as follows, - * - * - * - * The above configuration will affect the pages under the directory containing - * this configuration and all its subdirectories. - * To configure individual pages to use TSessionPageStatePersister, use - * - * - * - * - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.1 - */ -class TSessionPageStatePersister extends TComponent implements IPageStatePersister -{ - const STATE_SESSION_KEY='PRADO_SESSION_PAGESTATE'; - const QUEUE_SESSION_KEY='PRADO_SESSION_STATEQUEUE'; - - private $_page; - private $_historySize=10; - - /** - * @param TPage the page that this persister works for - */ - public function getPage() - { - return $this->_page; - } - - /** - * @param TPage the page that this persister works for. - */ - public function setPage(TPage $page) - { - $this->_page=$page; - } - - /** - * @return integer maximum number of page states that should be kept in session. Defaults to 10. - */ - public function getHistorySize() - { - return $this->_historySize; - } - - /** - * @param integer maximum number of page states that should be kept in session - * @throws TInvalidDataValueException if the number is smaller than 1. - */ - public function setHistorySize($value) - { - if(($value=TPropertyValue::ensureInteger($value))>0) - $this->_historySize=$value; - else - throw new TInvalidDataValueException('sessionpagestatepersister_historysize_invalid'); - } - /** - * Saves state in session. - * @param mixed state to be stored - */ - public function save($state) - { - $session=$this->_page->getSession(); - $session->open(); - $data=serialize($state); - $timestamp=(string)microtime(true); - $key=self::STATE_SESSION_KEY.$timestamp; - $session->add($key,$data); - if(($queue=$session->itemAt(self::QUEUE_SESSION_KEY))===null) - $queue=array(); - $queue[]=$key; - if(count($queue)>$this->getHistorySize()) - { - $expiredKey=array_shift($queue); - $session->remove($expiredKey); - } - $session->add(self::QUEUE_SESSION_KEY,$queue); - $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); - } - - /** - * Loads page state from session. - * @return mixed the restored state - * @throws THttpException if page state is corrupted - */ - public function load() - { - if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) - { - $session=$this->_page->getSession(); - $session->open(); - $key=self::STATE_SESSION_KEY.$timestamp; - if(($data=$session->itemAt($key))!==null) - return unserialize($data); - } - throw new THttpException(400,'sessionpagestatepersister_pagestate_corrupted'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * TSessionPageStatePersister class + * + * TSessionPageStatePersister implements a page state persistent method based on + * sessions. Page state are stored in user sessions and therefore, this persister + * requires session to be started and available. + * + * TSessionPageStatePersister keeps limited number of history states in session, + * mainly to preserve the precious server storage. The number is specified + * by {@link setHistorySize HistorySize}, which defaults to 10. + * + * There are a couple of ways to use TSessionPageStatePersister. + * One can override the page's {@link TPage::getStatePersister()} method and + * create a TSessionPageStatePersister instance there. + * Or one can configure the pages to use TSessionPageStatePersister in page configurations + * as follows, + * + * + * + * The above configuration will affect the pages under the directory containing + * this configuration and all its subdirectories. + * To configure individual pages to use TSessionPageStatePersister, use + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.1 + */ +class TSessionPageStatePersister extends TComponent implements IPageStatePersister +{ + const STATE_SESSION_KEY='PRADO_SESSION_PAGESTATE'; + const QUEUE_SESSION_KEY='PRADO_SESSION_STATEQUEUE'; + + private $_page; + private $_historySize=10; + + /** + * @param TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for. + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * @return integer maximum number of page states that should be kept in session. Defaults to 10. + */ + public function getHistorySize() + { + return $this->_historySize; + } + + /** + * @param integer maximum number of page states that should be kept in session + * @throws TInvalidDataValueException if the number is smaller than 1. + */ + public function setHistorySize($value) + { + if(($value=TPropertyValue::ensureInteger($value))>0) + $this->_historySize=$value; + else + throw new TInvalidDataValueException('sessionpagestatepersister_historysize_invalid'); + } + /** + * Saves state in session. + * @param mixed state to be stored + */ + public function save($state) + { + $session=$this->_page->getSession(); + $session->open(); + $data=serialize($state); + $timestamp=(string)microtime(true); + $key=self::STATE_SESSION_KEY.$timestamp; + $session->add($key,$data); + if(($queue=$session->itemAt(self::QUEUE_SESSION_KEY))===null) + $queue=array(); + $queue[]=$key; + if(count($queue)>$this->getHistorySize()) + { + $expiredKey=array_shift($queue); + $session->remove($expiredKey); + } + $session->add(self::QUEUE_SESSION_KEY,$queue); + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); + } + + /** + * Loads page state from session. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + { + $session=$this->_page->getSession(); + $session->open(); + $key=self::STATE_SESSION_KEY.$timestamp; + if(($data=$session->itemAt($key))!==null) + return unserialize($data); + } + throw new THttpException(400,'sessionpagestatepersister_pagestate_corrupted'); + } +} + diff --git a/framework/Web/UI/TTemplateControl.php b/framework/Web/UI/TTemplateControl.php index 400c1059..9b26728d 100644 --- a/framework/Web/UI/TTemplateControl.php +++ b/framework/Web/UI/TTemplateControl.php @@ -1,243 +1,243 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * Includes TCompositeControl class - */ -Prado::using('System.Web.UI.TCompositeControl'); - -/** - * TTemplateControl class. - * TTemplateControl is the base class for all controls that use templates. - * By default, a control template is assumed to be in a file under the same - * directory with the control class file. They have the same file name and - * different extension name. For template file, the extension name is ".tpl". - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TTemplateControl extends TCompositeControl -{ - /** - * template file extension. - */ - const EXT_TEMPLATE='.tpl'; - - /** - * @var ITemplate the parsed template structure shared by the same control class - */ - private static $_template=array(); - /** - * @var ITemplate the parsed template structure specific for this control instance - */ - private $_localTemplate=null; - /** - * @var TTemplateControl the master control if any - */ - private $_master=null; - /** - * @var string master control class name - */ - private $_masterClass=''; - /** - * @var array list of TContent controls - */ - private $_contents=array(); - /** - * @var array list of TContentPlaceHolder controls - */ - private $_placeholders=array(); - - /** - * Returns the template object associated with this control object. - * @return ITemplate|null the parsed template, null if none - */ - public function getTemplate() - { - if($this->_localTemplate===null) - { - $class=get_class($this); - if(!isset(self::$_template[$class])) - self::$_template[$class]=$this->loadTemplate(); - return self::$_template[$class]; - } - else - return $this->_localTemplate; - } - - /** - * Sets the parsed template. - * Note, the template will be applied to the whole control class. - * This method should only be used by framework and control developers. - * @param ITemplate the parsed template - */ - public function setTemplate($value) - { - $this->_localTemplate=$value; - } - - /** - * @return boolean whether this control is a source template control. - * A source template control loads its template from external storage, - * such as file, db, rather than from within another template. - */ - public function getIsSourceTemplateControl() - { - if(($template=$this->getTemplate())!==null) - return $template->getIsSourceTemplate(); - else - return false; - } - - /** - * @return string the directory containing the template. Empty if no template available. - */ - public function getTemplateDirectory() - { - if(($template=$this->getTemplate())!==null) - return $template->getContextPath(); - else - return ''; - } - - /** - * Loads the template associated with this control class. - * @return ITemplate the parsed template structure - */ - protected function loadTemplate() - { - Prado::trace("Loading template ".get_class($this),'System.Web.UI.TTemplateControl'); - $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this)); - return $template; - } - - /** - * Creates child controls. - * This method is overridden to load and instantiate control template. - * This method should only be used by framework and control developers. - */ - public function createChildControls() - { - if($tpl=$this->getTemplate()) - { - foreach($tpl->getDirective() as $name=>$value) - { - if(is_string($value)) - $this->setSubProperty($name,$value); - else - throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name); - } - $tpl->instantiateIn($this); - } - } - - /** - * Registers a content control. - * @param string ID of the content - * @param TContent - */ - public function registerContent($id,TContent $object) - { - if(isset($this->_contents[$id])) - throw new TConfigurationException('templatecontrol_contentid_duplicated',$id); - else - $this->_contents[$id]=$object; - } - - /** - * Registers a content placeholder to this template control. - * This method should only be used by framework and control developers. - * @param string placeholder ID - * @param TContentPlaceHolder placeholder control - */ - public function registerContentPlaceHolder($id,TContentPlaceHolder $object) - { - if(isset($this->_placeholders[$id])) - throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id); - else - $this->_placeholders[$id]=$object; - } - - /** - * @return string master class name (in namespace form) - */ - public function getMasterClass() - { - return $this->_masterClass; - } - - /** - * @param string master control class name (in namespace form) - */ - public function setMasterClass($value) - { - $this->_masterClass=$value; - } - - /** - * @return TTemplateControl|null master control associated with this control, null if none - */ - public function getMaster() - { - return $this->_master; - } - - /** - * Injects all content controls (and their children) to the corresponding content placeholders. - * This method should only be used by framework and control developers. - * @param string ID of the content control - * @param TContent the content to be injected - */ - public function injectContent($id,$content) - { - if(isset($this->_placeholders[$id])) - { - $placeholder=$this->_placeholders[$id]; - $controls=$placeholder->getParent()->getControls(); - $loc=$controls->remove($placeholder); - $controls->insertAt($loc,$content); - } - else - throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id); - } - - /** - * Performs the OnInit step for the control and all its child controls. - * This method overrides the parent implementation - * by ensuring child controls are created first, - * and if master class is set, master will be applied. - * Only framework developers should use this method. - * @param TControl the naming container control - */ - protected function initRecursive($namingContainer=null) - { - $this->ensureChildControls(); - if($this->_masterClass!=='') - { - $master=Prado::createComponent($this->_masterClass); - if(!($master instanceof TTemplateControl)) - throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid'); - $this->_master=$master; - $this->getControls()->clear(); - $this->getControls()->add($master); - $master->ensureChildControls(); - foreach($this->_contents as $id=>$content) - $master->injectContent($id,$content); - } - else if(!empty($this->_contents)) - throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); - parent::initRecursive($namingContainer); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * Includes TCompositeControl class + */ +Prado::using('System.Web.UI.TCompositeControl'); + +/** + * TTemplateControl class. + * TTemplateControl is the base class for all controls that use templates. + * By default, a control template is assumed to be in a file under the same + * directory with the control class file. They have the same file name and + * different extension name. For template file, the extension name is ".tpl". + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TTemplateControl extends TCompositeControl +{ + /** + * template file extension. + */ + const EXT_TEMPLATE='.tpl'; + + /** + * @var ITemplate the parsed template structure shared by the same control class + */ + private static $_template=array(); + /** + * @var ITemplate the parsed template structure specific for this control instance + */ + private $_localTemplate=null; + /** + * @var TTemplateControl the master control if any + */ + private $_master=null; + /** + * @var string master control class name + */ + private $_masterClass=''; + /** + * @var array list of TContent controls + */ + private $_contents=array(); + /** + * @var array list of TContentPlaceHolder controls + */ + private $_placeholders=array(); + + /** + * Returns the template object associated with this control object. + * @return ITemplate|null the parsed template, null if none + */ + public function getTemplate() + { + if($this->_localTemplate===null) + { + $class=get_class($this); + if(!isset(self::$_template[$class])) + self::$_template[$class]=$this->loadTemplate(); + return self::$_template[$class]; + } + else + return $this->_localTemplate; + } + + /** + * Sets the parsed template. + * Note, the template will be applied to the whole control class. + * This method should only be used by framework and control developers. + * @param ITemplate the parsed template + */ + public function setTemplate($value) + { + $this->_localTemplate=$value; + } + + /** + * @return boolean whether this control is a source template control. + * A source template control loads its template from external storage, + * such as file, db, rather than from within another template. + */ + public function getIsSourceTemplateControl() + { + if(($template=$this->getTemplate())!==null) + return $template->getIsSourceTemplate(); + else + return false; + } + + /** + * @return string the directory containing the template. Empty if no template available. + */ + public function getTemplateDirectory() + { + if(($template=$this->getTemplate())!==null) + return $template->getContextPath(); + else + return ''; + } + + /** + * Loads the template associated with this control class. + * @return ITemplate the parsed template structure + */ + protected function loadTemplate() + { + Prado::trace("Loading template ".get_class($this),'System.Web.UI.TTemplateControl'); + $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this)); + return $template; + } + + /** + * Creates child controls. + * This method is overridden to load and instantiate control template. + * This method should only be used by framework and control developers. + */ + public function createChildControls() + { + if($tpl=$this->getTemplate()) + { + foreach($tpl->getDirective() as $name=>$value) + { + if(is_string($value)) + $this->setSubProperty($name,$value); + else + throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name); + } + $tpl->instantiateIn($this); + } + } + + /** + * Registers a content control. + * @param string ID of the content + * @param TContent + */ + public function registerContent($id,TContent $object) + { + if(isset($this->_contents[$id])) + throw new TConfigurationException('templatecontrol_contentid_duplicated',$id); + else + $this->_contents[$id]=$object; + } + + /** + * Registers a content placeholder to this template control. + * This method should only be used by framework and control developers. + * @param string placeholder ID + * @param TContentPlaceHolder placeholder control + */ + public function registerContentPlaceHolder($id,TContentPlaceHolder $object) + { + if(isset($this->_placeholders[$id])) + throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id); + else + $this->_placeholders[$id]=$object; + } + + /** + * @return string master class name (in namespace form) + */ + public function getMasterClass() + { + return $this->_masterClass; + } + + /** + * @param string master control class name (in namespace form) + */ + public function setMasterClass($value) + { + $this->_masterClass=$value; + } + + /** + * @return TTemplateControl|null master control associated with this control, null if none + */ + public function getMaster() + { + return $this->_master; + } + + /** + * Injects all content controls (and their children) to the corresponding content placeholders. + * This method should only be used by framework and control developers. + * @param string ID of the content control + * @param TContent the content to be injected + */ + public function injectContent($id,$content) + { + if(isset($this->_placeholders[$id])) + { + $placeholder=$this->_placeholders[$id]; + $controls=$placeholder->getParent()->getControls(); + $loc=$controls->remove($placeholder); + $controls->insertAt($loc,$content); + } + else + throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id); + } + + /** + * Performs the OnInit step for the control and all its child controls. + * This method overrides the parent implementation + * by ensuring child controls are created first, + * and if master class is set, master will be applied. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->_masterClass!=='') + { + $master=Prado::createComponent($this->_masterClass); + if(!($master instanceof TTemplateControl)) + throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid'); + $this->_master=$master; + $this->getControls()->clear(); + $this->getControls()->add($master); + $master->ensureChildControls(); + foreach($this->_contents as $id=>$content) + $master->injectContent($id,$content); + } + else if(!empty($this->_contents)) + throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); + parent::initRecursive($namingContainer); + } +} + diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index 4d17806b..f884cd65 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -1,1075 +1,1075 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * Includes TOutputCache class file - */ -Prado::using('System.Web.UI.WebControls.TOutputCache'); - -/** - * TTemplateManager class - * - * TTemplateManager manages the loading and parsing of control templates. - * - * There are two ways of loading a template, either by the associated template - * control class name, or the template file name. - * The former is via calling {@link getTemplateByClassName}, which tries to - * locate the corresponding template file under the directory containing - * the class file. The name of the template file is the class name with - * the extension '.tpl'. To load a template from a template file path, - * call {@link getTemplateByFileName}. - * - * By default, TTemplateManager is registered with {@link TPageService} as the - * template manager module that can be accessed via {@link TPageService::getTemplateManager()}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TTemplateManager extends TModule -{ - /** - * Template file extension - */ - const TEMPLATE_FILE_EXT='.tpl'; - /** - * Prefix of the cache variable name for storing parsed templates - */ - const TEMPLATE_CACHE_PREFIX='prado:template:'; - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * It starts output buffer if it is enabled. - * @param TXmlElement module configuration - */ - public function init($config) - { - $this->getService()->setTemplateManager($this); - } - - /** - * Loads the template corresponding to the specified class name. - * @return ITemplate template for the class name, null if template doesn't exist. - */ - public function getTemplateByClassName($className) - { - $class=new ReflectionClass($className); - $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT; - return $this->getTemplateByFileName($tplFile); - } - - /** - * Loads the template from the specified file. - * @return ITemplate template parsed from the specified file, null if the file doesn't exist. - */ - public function getTemplateByFileName($fileName) - { - if(($fileName=$this->getLocalizedTemplate($fileName))!==null) - { - Prado::trace("Loading template $fileName",'System.Web.UI.TTemplateManager'); - if(($cache=$this->getApplication()->getCache())===null) - return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); - else - { - $array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName); - if(is_array($array)) - { - list($template,$timestamps)=$array; - if($this->getApplication()->getMode()===TApplicationMode::Performance) - return $template; - $cacheValid=true; - foreach($timestamps as $tplFile=>$timestamp) - { - if(!is_file($tplFile) || filemtime($tplFile)>$timestamp) - { - $cacheValid=false; - break; - } - } - if($cacheValid) - return $template; - } - $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); - $includedFiles=$template->getIncludedFiles(); - $timestamps=array(); - $timestamps[$fileName]=filemtime($fileName); - foreach($includedFiles as $includedFile) - $timestamps[$includedFile]=filemtime($includedFile); - $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps)); - return $template; - } - } - else - return null; - } - - /** - * Finds a localized template file. - * @param string template file. - * @return string|null a localized template file if found, null otherwise. - */ - protected function getLocalizedTemplate($filename) - { - if(($app=$this->getApplication()->getGlobalization(false))===null) - return is_file($filename)?$filename:null; - foreach($app->getLocalizedResource($filename) as $file) - { - if(($file=realpath($file))!==false && is_file($file)) - return $file; - } - return null; - } -} - -/** - * TTemplate implements PRADO template parsing logic. - * A TTemplate object represents a parsed PRADO control template. - * It can instantiate the template as child controls of a specified control. - * The template format is like HTML, with the following special tags introduced, - * - component tags: a component tag represents the configuration of a component. - * The tag name is in the format of com:ComponentType, where ComponentType is the component - * class name. Component tags must be well-formed. Attributes of the component tag - * are treated as either property initial values, event handler attachment, or regular - * tag attributes. - * - property tags: property tags are used to set large block of attribute values. - * The property tag name is in the format of where AttributeName - * can be a property name, an event name or a regular tag attribute name. - * - group subproperty tags: subproperties of a common property can be configured using - * - * - directive: directive specifies the property values for the template owner. - * It is in the format of <%@ property name-value pairs %>; - * - expressions: They are in the format of <%= PHP expression %> and <%% PHP statements %> - * - comments: There are two kinds of comments, regular HTML comments and special template comments. - * The former is in the format of , which will be treated as text strings. - * The latter is in the format of , which will be stripped out. - * - * Tags other than the above are not required to be well-formed. - * - * A TTemplate object represents a parsed PRADO template. To instantiate the template - * for a particular control, call {@link instantiateIn($control)}, which - * will create and intialize all components specified in the template and - * set their parent as $control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class TTemplate extends TApplicationComponent implements ITemplate -{ - /** - * '' - template comments - * '' - HTML comments - * '<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>' - component tags - * '<\/?prop:([\w\.]+)\s*>' - property tags - * '<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>' - directives - * '<%[%#~\/\\$=\\[](.*?)%>' - expressions - * ')*)\s*\/>' - group subproperty tags - */ - const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\s*\/>/msS'; - - /** - * Different configurations of component property/event/attribute - */ - const CONFIG_DATABIND=0; - const CONFIG_EXPRESSION=1; - const CONFIG_ASSET=2; - const CONFIG_PARAMETER=3; - const CONFIG_LOCALIZATION=4; - const CONFIG_TEMPLATE=5; - - /** - * @var array list of component tags and strings - */ - private $_tpl=array(); - /** - * @var array list of directive settings - */ - private $_directive=array(); - /** - * @var string context path - */ - private $_contextPath; - /** - * @var string template file path (if available) - */ - private $_tplFile=null; - /** - * @var integer the line number that parsing starts from (internal use) - */ - private $_startingLine=0; - /** - * @var string template content to be parsed - */ - private $_content; - /** - * @var boolean whether this template is a source template - */ - private $_sourceTemplate=true; - /** - * @var string hash code of the template - */ - private $_hashCode=''; - private $_tplControl=null; - private $_includedFiles=array(); - private $_includeAtLine=array(); - private $_includeLines=array(); - - - /** - * Constructor. - * The template will be parsed after construction. - * @param string the template string - * @param string the template context directory - * @param string the template file, null if no file - * @param integer the line number that parsing starts from (internal use) - * @param boolean whether this template is a source template, i.e., this template is loaded from - * some external storage rather than from within another template. - */ - public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true) - { - $this->_sourceTemplate=$sourceTemplate; - $this->_contextPath=$contextPath; - $this->_tplFile=$tplFile; - $this->_startingLine=$startingLine; - $this->_content=$template; - $this->_hashCode=md5($template); - $this->parse($template); - $this->_content=null; // reset to save memory - } - - /** - * @return string template file path if available, null otherwise. - */ - public function getTemplateFile() - { - return $this->_tplFile; - } - - /** - * @return boolean whether this template is a source template, i.e., this template is loaded from - * some external storage rather than from within another template. - */ - public function getIsSourceTemplate() - { - return $this->_sourceTemplate; - } - - /** - * @return string context directory path - */ - public function getContextPath() - { - return $this->_contextPath; - } - - /** - * @return array name-value pairs declared in the directive - */ - public function getDirective() - { - return $this->_directive; - } - - /** - * @return string hash code that can be used to identify the template - */ - public function getHashCode() - { - return $this->_hashCode; - } - - /** - * @return array the parsed template - */ - public function &getItems() - { - return $this->_tpl; - } - - /** - * Instantiates the template. - * Content in the template will be instantiated as components and text strings - * and passed to the specified parent control. - * @param TControl the control who owns the template - * @param TControl the control who will become the root parent of the controls on the template. If null, it uses the template control. - */ - public function instantiateIn($tplControl,$parentControl=null) - { - $this->_tplControl=$tplControl; - if($parentControl===null) - $parentControl=$tplControl; - if(($page=$tplControl->getPage())===null) - $page=$this->getService()->getRequestedPage(); - $controls=array(); - $directChildren=array(); - foreach($this->_tpl as $key=>$object) - { - if($object[0]===-1) - $parent=$parentControl; - else if(isset($controls[$object[0]])) - $parent=$controls[$object[0]]; - else - continue; - if(isset($object[2])) // component - { - $component=Prado::createComponent($object[1]); - $properties=&$object[2]; - if($component instanceof TControl) - { - if($component instanceof TOutputCache) - $component->setCacheKeyPrefix($this->_hashCode.$key); - $component->setTemplateControl($tplControl); - if(isset($properties['id'])) - { - if(is_array($properties['id'])) - $properties['id']=$component->evaluateExpression($properties['id'][1]); - $tplControl->registerObject($properties['id'],$component); - } - if(isset($properties['skinid'])) - { - if(is_array($properties['skinid'])) - $component->setSkinID($component->evaluateExpression($properties['skinid'][1])); - else - $component->setSkinID($properties['skinid']); - unset($properties['skinid']); - } - - $component->trackViewState(false); - - $component->applyStyleSheetSkin($page); - foreach($properties as $name=>$value) - $this->configureControl($component,$name,$value); - - $component->trackViewState(true); - - if($parent===$parentControl) - $directChildren[]=$component; - else - $component->createdOnTemplate($parent); - if($component->getAllowChildControls()) - $controls[$key]=$component; - } - else if($component instanceof TComponent) - { - $controls[$key]=$component; - if(isset($properties['id'])) - { - if(is_array($properties['id'])) - $properties['id']=$component->evaluateExpression($properties['id'][1]); - $tplControl->registerObject($properties['id'],$component); - if(!$component->hasProperty('id')) - unset($properties['id']); - } - foreach($properties as $name=>$value) - $this->configureComponent($component,$name,$value); - if($parent===$parentControl) - $directChildren[]=$component; - else - $component->createdOnTemplate($parent); - } - } - else - { - if($object[1] instanceof TCompositeLiteral) - { - // need to clone a new object because the one in template is reused - $o=clone $object[1]; - $o->setContainer($tplControl); - if($parent===$parentControl) - $directChildren[]=$o; - else - $parent->addParsedObject($o); - } - else - { - if($parent===$parentControl) - $directChildren[]=$object[1]; - else - $parent->addParsedObject($object[1]); - } - } - } - // delay setting parent till now because the parent may cause - // the child to do lifecycle catchup which may cause problem - // if the child needs its own child controls. - foreach($directChildren as $control) - { - if($control instanceof TComponent) - $control->createdOnTemplate($parentControl); - else - $parentControl->addParsedObject($control); - } - } - - /** - * Configures a property/event of a control. - * @param TControl control to be configured - * @param string property name - * @param mixed property initial value - */ - protected function configureControl($control,$name,$value) - { - if(strncasecmp($name,'on',2)===0) // is an event - $this->configureEvent($control,$name,$value,$control); - else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute - $this->configureProperty($control,$name,$value); - else // is a subproperty - $this->configureSubProperty($control,$name,$value); - } - - /** - * Configures a property of a non-control component. - * @param TComponent component to be configured - * @param string property name - * @param mixed property initial value - */ - protected function configureComponent($component,$name,$value) - { - if(strpos($name,'.')===false) // is a simple property or custom attribute - $this->configureProperty($component,$name,$value); - else // is a subproperty - $this->configureSubProperty($component,$name,$value); - } - - /** - * Configures an event for a control. - * @param TControl control to be configured - * @param string event name - * @param string event handler - * @param TControl context control - */ - protected function configureEvent($control,$name,$value,$contextControl) - { - if(strpos($value,'.')===false) - $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value)); - else - $control->attachEventHandler($name,array($contextControl,$value)); - } - - /** - * Configures a simple property for a component. - * @param TComponent component to be configured - * @param string property name - * @param mixed property initial value - */ - protected function configureProperty($component,$name,$value) - { - if(is_array($value)) - { - switch($value[0]) - { - case self::CONFIG_DATABIND: - $component->bindProperty($name,$value[1]); - break; - case self::CONFIG_EXPRESSION: - if($component instanceof TControl) - $component->autoBindProperty($name,$value[1]); - else - { - $setter='set'.$name; - $component->$setter($this->_tplControl->evaluateExpression($value[1])); - } - break; - case self::CONFIG_TEMPLATE: - $setter='set'.$name; - $component->$setter($value[1]); - break; - case self::CONFIG_ASSET: // asset URL - $setter='set'.$name; - $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); - $component->$setter($url); - break; - case self::CONFIG_PARAMETER: // application parameter - $setter='set'.$name; - $component->$setter($this->getApplication()->getParameters()->itemAt($value[1])); - break; - case self::CONFIG_LOCALIZATION: - $setter='set'.$name; - $component->$setter(Prado::localize($value[1])); - break; - default: // an error if reaching here - throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); - break; - } - } - else - { - if (substr($name,0,2)=='js') - if ($value and !($value instanceof TJavaScriptLiteral)) - $value = new TJavaScriptLiteral($value); - $setter='set'.$name; - $component->$setter($value); - } - } - - /** - * Configures a subproperty for a component. - * @param TComponent component to be configured - * @param string subproperty name - * @param mixed subproperty initial value - */ - protected function configureSubProperty($component,$name,$value) - { - if(is_array($value)) - { - switch($value[0]) - { - case self::CONFIG_DATABIND: // databinding - $component->bindProperty($name,$value[1]); - break; - case self::CONFIG_EXPRESSION: // expression - if($component instanceof TControl) - $component->autoBindProperty($name,$value[1]); - else - $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1])); - break; - case self::CONFIG_TEMPLATE: - $component->setSubProperty($name,$value[1]); - break; - case self::CONFIG_ASSET: // asset URL - $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); - $component->setSubProperty($name,$url); - break; - case self::CONFIG_PARAMETER: // application parameter - $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1])); - break; - case self::CONFIG_LOCALIZATION: - $component->setSubProperty($name,Prado::localize($value[1])); - break; - default: // an error if reaching here - throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); - break; - } - } - else - $component->setSubProperty($name,$value); - } - - /** - * Parses a template string. - * - * This template parser recognizes five types of data: - * regular string, well-formed component tags, well-formed property tags, directives, and expressions. - * - * The parsing result is returned as an array. Each array element can be of three types: - * - a string, 0: container index; 1: string content; - * - a component tag, 0: container index; 1: component type; 2: attributes (name=>value pairs) - * If a directive is found in the template, it will be parsed and can be - * retrieved via {@link getDirective}, which returns an array consisting of - * name-value pairs in the directive. - * - * Note, attribute names are treated as case-insensitive and will be turned into lower cases. - * Component and directive types are case-sensitive. - * Container index is the index to the array element that stores the container object. - * If an object has no container, its container index is -1. - * - * @param string the template string - * @throws TConfigurationException if a parsing error is encountered - */ - protected function parse($input) - { - $input=$this->preprocess($input); - $tpl=&$this->_tpl; - $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); - $expectPropEnd=false; - $textStart=0; - $stack=array(); - $container=-1; - $matchEnd=0; - $c=0; - $this->_directive=null; - try - { - for($i=0;$i<$n;++$i) - { - $match=&$matches[$i]; - $str=$match[0][0]; - $matchStart=$match[0][1]; - $matchEnd=$matchStart+strlen($str)-1; - if(strpos($str,'$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - $type=$match[1][0]; - $attributes=$this->parseAttributes($match[2][0],$match[2][1]); - $this->validateAttributes($type,$attributes); - $tpl[$c++]=array($container,$type,$attributes); - if($str[strlen($str)-2]!=='/') // open tag - { - $stack[] = $type; - $container=$c-1; - } - } - else if(strpos($str,'$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - $type=$match[1][0]; - - if(empty($stack)) - throw new TConfigurationException('template_closingtag_unexpected',""); - - $name=array_pop($stack); - if($name!==$type) - { - $tag=$name[0]==='@' ? '' : ""; - throw new TConfigurationException('template_closingtag_expected',$tag); - } - $container=$tpl[$container][0]; - } - else if(strpos($str,'<%@')===0) // directive - { - if($expectPropEnd) - continue; - if($matchStart>$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - if(isset($tpl[0]) || $this->_directive!==null) - throw new TConfigurationException('template_directive_nonunique'); - $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]); - } - else if(strpos($str,'<%')===0) // expression - { - if($expectPropEnd) - continue; - if($matchStart>$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - $literal=trim($match[5][0]); - if($str[2]==='=') // expression - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal)); - else if($str[2]==='%') // statements - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal)); - else if($str[2]==='#') - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal)); - else if($str[2]==='$') - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')")); - else if($str[2]==='~') - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')")); - else if($str[2]==='/') - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"dirname(\$this->getApplication()->getRequest()->getApplicationUrl()).'/$literal'")); - else if($str[2]==='[') - { - $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\")); - $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')")); - } - } - else if(strpos($str,'')===strlen($str)-2) //subproperties - { - if($expectPropEnd) - continue; - if($matchStart>$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - $prop=strtolower($match[6][0]); - $attrs=$this->parseAttributes($match[7][0],$match[7][1]); - $attributes=array(); - foreach($attrs as $name=>$value) - $attributes[$prop.'.'.$name]=$value; - $type=$tpl[$container][1]; - $this->validateAttributes($type,$attributes); - foreach($attributes as $name=>$value) - { - if(isset($tpl[$container][2][$name])) - throw new TConfigurationException('template_property_duplicated',$name); - $tpl[$container][2][$name]=$value; - } - } - else // regular property - { - $prop=strtolower($match[3][0]); - $stack[] = '@'.$prop; - if(!$expectPropEnd) - { - if($matchStart>$textStart) - $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); - $textStart=$matchEnd+1; - $expectPropEnd=true; - } - } - } - else if(strpos($str,'"); - $name=array_pop($stack); - if($name!=='@'.$prop) - { - $tag=$name[0]==='@' ? '' : ""; - throw new TConfigurationException('template_closingtag_expected',$tag); - } - if(($last=count($stack))<1 || $stack[$last-1][0]!=='@') - { - if($matchStart>$textStart) - { - $value=substr($input,$textStart,$matchStart-$textStart); - if(substr($prop,-8,8)==='template') - $value=$this->parseTemplateProperty($value,$textStart); - else - $value=$this->parseAttribute($value); - if($container>=0) - { - $type=$tpl[$container][1]; - $this->validateAttributes($type,array($prop=>$value)); - if(isset($tpl[$container][2][$prop])) - throw new TConfigurationException('template_property_duplicated',$prop); - $tpl[$container][2][$prop]=$value; - } - else // a property for the template control - $this->_directive[$prop]=$value; - $textStart=$matchEnd+1; - } - $expectPropEnd=false; - } - } - else if(strpos($str,', which will be treated as text strings. + * The latter is in the format of , which will be stripped out. + * + * Tags other than the above are not required to be well-formed. + * + * A TTemplate object represents a parsed PRADO template. To instantiate the template + * for a particular control, call {@link instantiateIn($control)}, which + * will create and intialize all components specified in the template and + * set their parent as $control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class TTemplate extends TApplicationComponent implements ITemplate +{ + /** + * '' - template comments + * '' - HTML comments + * '<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>' - component tags + * '<\/?prop:([\w\.]+)\s*>' - property tags + * '<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>' - directives + * '<%[%#~\/\\$=\\[](.*?)%>' - expressions + * ')*)\s*\/>' - group subproperty tags + */ + const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\s*\/>/msS'; + + /** + * Different configurations of component property/event/attribute + */ + const CONFIG_DATABIND=0; + const CONFIG_EXPRESSION=1; + const CONFIG_ASSET=2; + const CONFIG_PARAMETER=3; + const CONFIG_LOCALIZATION=4; + const CONFIG_TEMPLATE=5; + + /** + * @var array list of component tags and strings + */ + private $_tpl=array(); + /** + * @var array list of directive settings + */ + private $_directive=array(); + /** + * @var string context path + */ + private $_contextPath; + /** + * @var string template file path (if available) + */ + private $_tplFile=null; + /** + * @var integer the line number that parsing starts from (internal use) + */ + private $_startingLine=0; + /** + * @var string template content to be parsed + */ + private $_content; + /** + * @var boolean whether this template is a source template + */ + private $_sourceTemplate=true; + /** + * @var string hash code of the template + */ + private $_hashCode=''; + private $_tplControl=null; + private $_includedFiles=array(); + private $_includeAtLine=array(); + private $_includeLines=array(); + + + /** + * Constructor. + * The template will be parsed after construction. + * @param string the template string + * @param string the template context directory + * @param string the template file, null if no file + * @param integer the line number that parsing starts from (internal use) + * @param boolean whether this template is a source template, i.e., this template is loaded from + * some external storage rather than from within another template. + */ + public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true) + { + $this->_sourceTemplate=$sourceTemplate; + $this->_contextPath=$contextPath; + $this->_tplFile=$tplFile; + $this->_startingLine=$startingLine; + $this->_content=$template; + $this->_hashCode=md5($template); + $this->parse($template); + $this->_content=null; // reset to save memory + } + + /** + * @return string template file path if available, null otherwise. + */ + public function getTemplateFile() + { + return $this->_tplFile; + } + + /** + * @return boolean whether this template is a source template, i.e., this template is loaded from + * some external storage rather than from within another template. + */ + public function getIsSourceTemplate() + { + return $this->_sourceTemplate; + } + + /** + * @return string context directory path + */ + public function getContextPath() + { + return $this->_contextPath; + } + + /** + * @return array name-value pairs declared in the directive + */ + public function getDirective() + { + return $this->_directive; + } + + /** + * @return string hash code that can be used to identify the template + */ + public function getHashCode() + { + return $this->_hashCode; + } + + /** + * @return array the parsed template + */ + public function &getItems() + { + return $this->_tpl; + } + + /** + * Instantiates the template. + * Content in the template will be instantiated as components and text strings + * and passed to the specified parent control. + * @param TControl the control who owns the template + * @param TControl the control who will become the root parent of the controls on the template. If null, it uses the template control. + */ + public function instantiateIn($tplControl,$parentControl=null) + { + $this->_tplControl=$tplControl; + if($parentControl===null) + $parentControl=$tplControl; + if(($page=$tplControl->getPage())===null) + $page=$this->getService()->getRequestedPage(); + $controls=array(); + $directChildren=array(); + foreach($this->_tpl as $key=>$object) + { + if($object[0]===-1) + $parent=$parentControl; + else if(isset($controls[$object[0]])) + $parent=$controls[$object[0]]; + else + continue; + if(isset($object[2])) // component + { + $component=Prado::createComponent($object[1]); + $properties=&$object[2]; + if($component instanceof TControl) + { + if($component instanceof TOutputCache) + $component->setCacheKeyPrefix($this->_hashCode.$key); + $component->setTemplateControl($tplControl); + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + } + if(isset($properties['skinid'])) + { + if(is_array($properties['skinid'])) + $component->setSkinID($component->evaluateExpression($properties['skinid'][1])); + else + $component->setSkinID($properties['skinid']); + unset($properties['skinid']); + } + + $component->trackViewState(false); + + $component->applyStyleSheetSkin($page); + foreach($properties as $name=>$value) + $this->configureControl($component,$name,$value); + + $component->trackViewState(true); + + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + if($component->getAllowChildControls()) + $controls[$key]=$component; + } + else if($component instanceof TComponent) + { + $controls[$key]=$component; + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + if(!$component->hasProperty('id')) + unset($properties['id']); + } + foreach($properties as $name=>$value) + $this->configureComponent($component,$name,$value); + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + } + } + else + { + if($object[1] instanceof TCompositeLiteral) + { + // need to clone a new object because the one in template is reused + $o=clone $object[1]; + $o->setContainer($tplControl); + if($parent===$parentControl) + $directChildren[]=$o; + else + $parent->addParsedObject($o); + } + else + { + if($parent===$parentControl) + $directChildren[]=$object[1]; + else + $parent->addParsedObject($object[1]); + } + } + } + // delay setting parent till now because the parent may cause + // the child to do lifecycle catchup which may cause problem + // if the child needs its own child controls. + foreach($directChildren as $control) + { + if($control instanceof TComponent) + $control->createdOnTemplate($parentControl); + else + $parentControl->addParsedObject($control); + } + } + + /** + * Configures a property/event of a control. + * @param TControl control to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureControl($control,$name,$value) + { + if(strncasecmp($name,'on',2)===0) // is an event + $this->configureEvent($control,$name,$value,$control); + else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute + $this->configureProperty($control,$name,$value); + else // is a subproperty + $this->configureSubProperty($control,$name,$value); + } + + /** + * Configures a property of a non-control component. + * @param TComponent component to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureComponent($component,$name,$value) + { + if(strpos($name,'.')===false) // is a simple property or custom attribute + $this->configureProperty($component,$name,$value); + else // is a subproperty + $this->configureSubProperty($component,$name,$value); + } + + /** + * Configures an event for a control. + * @param TControl control to be configured + * @param string event name + * @param string event handler + * @param TControl context control + */ + protected function configureEvent($control,$name,$value,$contextControl) + { + if(strpos($value,'.')===false) + $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value)); + else + $control->attachEventHandler($name,array($contextControl,$value)); + } + + /** + * Configures a simple property for a component. + * @param TComponent component to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: + $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: + if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + { + $setter='set'.$name; + $component->$setter($this->_tplControl->evaluateExpression($value[1])); + } + break; + case self::CONFIG_TEMPLATE: + $setter='set'.$name; + $component->$setter($value[1]); + break; + case self::CONFIG_ASSET: // asset URL + $setter='set'.$name; + $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->$setter($url); + break; + case self::CONFIG_PARAMETER: // application parameter + $setter='set'.$name; + $component->$setter($this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $setter='set'.$name; + $component->$setter(Prado::localize($value[1])); + break; + default: // an error if reaching here + throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + { + if (substr($name,0,2)=='js') + if ($value and !($value instanceof TJavaScriptLiteral)) + $value = new TJavaScriptLiteral($value); + $setter='set'.$name; + $component->$setter($value); + } + } + + /** + * Configures a subproperty for a component. + * @param TComponent component to be configured + * @param string subproperty name + * @param mixed subproperty initial value + */ + protected function configureSubProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: // databinding + $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: // expression + if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1])); + break; + case self::CONFIG_TEMPLATE: + $component->setSubProperty($name,$value[1]); + break; + case self::CONFIG_ASSET: // asset URL + $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->setSubProperty($name,$url); + break; + case self::CONFIG_PARAMETER: // application parameter + $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $component->setSubProperty($name,Prado::localize($value[1])); + break; + default: // an error if reaching here + throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + $component->setSubProperty($name,$value); + } + + /** + * Parses a template string. + * + * This template parser recognizes five types of data: + * regular string, well-formed component tags, well-formed property tags, directives, and expressions. + * + * The parsing result is returned as an array. Each array element can be of three types: + * - a string, 0: container index; 1: string content; + * - a component tag, 0: container index; 1: component type; 2: attributes (name=>value pairs) + * If a directive is found in the template, it will be parsed and can be + * retrieved via {@link getDirective}, which returns an array consisting of + * name-value pairs in the directive. + * + * Note, attribute names are treated as case-insensitive and will be turned into lower cases. + * Component and directive types are case-sensitive. + * Container index is the index to the array element that stores the container object. + * If an object has no container, its container index is -1. + * + * @param string the template string + * @throws TConfigurationException if a parsing error is encountered + */ + protected function parse($input) + { + $input=$this->preprocess($input); + $tpl=&$this->_tpl; + $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + $expectPropEnd=false; + $textStart=0; + $stack=array(); + $container=-1; + $matchEnd=0; + $c=0; + $this->_directive=null; + try + { + for($i=0;$i<$n;++$i) + { + $match=&$matches[$i]; + $str=$match[0][0]; + $matchStart=$match[0][1]; + $matchEnd=$matchStart+strlen($str)-1; + if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + $attributes=$this->parseAttributes($match[2][0],$match[2][1]); + $this->validateAttributes($type,$attributes); + $tpl[$c++]=array($container,$type,$attributes); + if($str[strlen($str)-2]!=='/') // open tag + { + $stack[] = $type; + $container=$c-1; + } + } + else if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + + if(empty($stack)) + throw new TConfigurationException('template_closingtag_unexpected',""); + + $name=array_pop($stack); + if($name!==$type) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + $container=$tpl[$container][0]; + } + else if(strpos($str,'<%@')===0) // directive + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + if(isset($tpl[0]) || $this->_directive!==null) + throw new TConfigurationException('template_directive_nonunique'); + $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]); + } + else if(strpos($str,'<%')===0) // expression + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $literal=trim($match[5][0]); + if($str[2]==='=') // expression + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal)); + else if($str[2]==='%') // statements + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal)); + else if($str[2]==='#') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal)); + else if($str[2]==='$') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')")); + else if($str[2]==='~') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')")); + else if($str[2]==='/') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"dirname(\$this->getApplication()->getRequest()->getApplicationUrl()).'/$literal'")); + else if($str[2]==='[') + { + $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\")); + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')")); + } + } + else if(strpos($str,'')===strlen($str)-2) //subproperties + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $prop=strtolower($match[6][0]); + $attrs=$this->parseAttributes($match[7][0],$match[7][1]); + $attributes=array(); + foreach($attrs as $name=>$value) + $attributes[$prop.'.'.$name]=$value; + $type=$tpl[$container][1]; + $this->validateAttributes($type,$attributes); + foreach($attributes as $name=>$value) + { + if(isset($tpl[$container][2][$name])) + throw new TConfigurationException('template_property_duplicated',$name); + $tpl[$container][2][$name]=$value; + } + } + else // regular property + { + $prop=strtolower($match[3][0]); + $stack[] = '@'.$prop; + if(!$expectPropEnd) + { + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $expectPropEnd=true; + } + } + } + else if(strpos($str,'"); + $name=array_pop($stack); + if($name!=='@'.$prop) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + if(($last=count($stack))<1 || $stack[$last-1][0]!=='@') + { + if($matchStart>$textStart) + { + $value=substr($input,$textStart,$matchStart-$textStart); + if(substr($prop,-8,8)==='template') + $value=$this->parseTemplateProperty($value,$textStart); + else + $value=$this->parseAttribute($value); + if($container>=0) + { + $type=$tpl[$container][1]; + $this->validateAttributes($type,array($prop=>$value)); + if(isset($tpl[$container][2][$prop])) + throw new TConfigurationException('template_property_duplicated',$prop); + $tpl[$container][2][$prop]=$value; + } + else // a property for the template control + $this->_directive[$prop]=$value; + $textStart=$matchEnd+1; + } + $expectPropEnd=false; + } + } + else if(strpos($str,' - * - * Use the {@link setClientValidationFunction ClientValidationFunction} property - * to specify the name of the client-side validation script function associated - * with the TCustomValidator. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TCustomValidator extends TBaseValidator -{ - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TCustomValidator'; - } - - /** - * @return string the name of the custom client-side script function used for validation. - */ - public function getClientValidationFunction() - { - return $this->getViewState('ClientValidationFunction',''); - } - - /** - * Sets the name of the custom client-side script function used for validation. - * @param string the script function name - */ - public function setClientValidationFunction($value) - { - $this->setViewState('ClientValidationFunction',$value,''); - } - - /** - * This method overrides the parent's implementation. - * The validation succeeds if {@link onServerValidate} returns true. - * @return boolean whether the validation succeeds - */ - public function evaluateIsValid() - { - $value = ''; - if($this->getValidationTarget()!==null) - $value=$this->getValidationValue($this->getValidationTarget()); - return $this->onServerValidate($value); - } - - /** - * This method is invoked when the server side validation happens. - * It will raise the OnServerValidate event. - * The method also allows derived classes to handle the event without attaching a delegate. - * Note The derived classes should call parent implementation - * to ensure the OnServerValidate event is raised. - * @param string the value to be validated - * @return boolean whether the value is valid - */ - public function onServerValidate($value) - { - $param=new TServerValidateEventParameter($value,true); - $this->raiseEvent('OnServerValidate',$this,$param); - return $param->getIsValid(); - } - - /** - * @return TControl control to be validated. Null if no control is found. - */ - public function getValidationTarget() - { - if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null) - return $control; - else if(($id=$this->getControlToValidate())!=='') - throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this)); - else - return null; - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options=parent::getClientScriptOptions(); - if(($clientJs=$this->getClientValidationFunction())!=='') - $options['ClientValidationFunction']=$clientJs; - return $options; - } - - /** - * Only register the client-side validator if - * {@link setClientValidationFunction ClientValidationFunction} is set. - */ - protected function registerClientScriptValidator() - { - if($this->getClientValidationFunction()!=='') - parent::registerClientScriptValidator(); - } -} - -/** - * TServerValidateEventParameter class - * - * TServerValidateEventParameter encapsulates the parameter data for - * OnServerValidate event of TCustomValidator components. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TServerValidateEventParameter extends TEventParameter -{ - /** - * the value to be validated - * @var string - */ - private $_value=''; - /** - * whether the value is valid - * @var boolean - */ - private $_isValid=true; - - /** - * Constructor. - * @param string property value to be validated - * @param boolean whether the value is valid - */ - public function __construct($value,$isValid) - { - $this->_value=$value; - $this->setIsValid($isValid); - } - - /** - * @return string value to be validated - */ - public function getValue() - { - return $this->_value; - } - - /** - * @return boolean whether the value is valid - */ - public function getIsValid() - { - return $this->_isValid; - } - - /** - * @param boolean whether the value is valid - */ - public function setIsValid($value) - { - $this->_isValid=TPropertyValue::ensureBoolean($value); - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TCustomValidator class + * + * TCustomValidator performs user-defined validation (either + * server-side or client-side or both) on an input component. + * + * To create a server-side validation function, provide a handler for + * the {@link onServerValidate OnServerValidate} event that performs the validation. + * The data string of the input control to validate can be accessed + * by {@link TServerValidateEventParameter::getValue Value} of the event parameter. + * The result of the validation should be stored in the + * {@link TServerValidateEventParameter::getIsValid IsValid} property of the event + * parameter. + * + * To create a client-side validation function, add the client-side + * validation javascript function to the page template. + * The function should have the following signature: + * + * + * + * Use the {@link setClientValidationFunction ClientValidationFunction} property + * to specify the name of the client-side validation script function associated + * with the TCustomValidator. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCustomValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TCustomValidator'; + } + + /** + * @return string the name of the custom client-side script function used for validation. + */ + public function getClientValidationFunction() + { + return $this->getViewState('ClientValidationFunction',''); + } + + /** + * Sets the name of the custom client-side script function used for validation. + * @param string the script function name + */ + public function setClientValidationFunction($value) + { + $this->setViewState('ClientValidationFunction',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if {@link onServerValidate} returns true. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + $value = ''; + if($this->getValidationTarget()!==null) + $value=$this->getValidationValue($this->getValidationTarget()); + return $this->onServerValidate($value); + } + + /** + * This method is invoked when the server side validation happens. + * It will raise the OnServerValidate event. + * The method also allows derived classes to handle the event without attaching a delegate. + * Note The derived classes should call parent implementation + * to ensure the OnServerValidate event is raised. + * @param string the value to be validated + * @return boolean whether the value is valid + */ + public function onServerValidate($value) + { + $param=new TServerValidateEventParameter($value,true); + $this->raiseEvent('OnServerValidate',$this,$param); + return $param->getIsValid(); + } + + /** + * @return TControl control to be validated. Null if no control is found. + */ + public function getValidationTarget() + { + if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null) + return $control; + else if(($id=$this->getControlToValidate())!=='') + throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this)); + else + return null; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options=parent::getClientScriptOptions(); + if(($clientJs=$this->getClientValidationFunction())!=='') + $options['ClientValidationFunction']=$clientJs; + return $options; + } + + /** + * Only register the client-side validator if + * {@link setClientValidationFunction ClientValidationFunction} is set. + */ + protected function registerClientScriptValidator() + { + if($this->getClientValidationFunction()!=='') + parent::registerClientScriptValidator(); + } +} + +/** + * TServerValidateEventParameter class + * + * TServerValidateEventParameter encapsulates the parameter data for + * OnServerValidate event of TCustomValidator components. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TServerValidateEventParameter extends TEventParameter +{ + /** + * the value to be validated + * @var string + */ + private $_value=''; + /** + * whether the value is valid + * @var boolean + */ + private $_isValid=true; + + /** + * Constructor. + * @param string property value to be validated + * @param boolean whether the value is valid + */ + public function __construct($value,$isValid) + { + $this->_value=$value; + $this->setIsValid($isValid); + } + + /** + * @return string value to be validated + */ + public function getValue() + { + return $this->_value; + } + + /** + * @return boolean whether the value is valid + */ + public function getIsValid() + { + return $this->_isValid; + } + + /** + * @param boolean whether the value is valid + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } +} diff --git a/framework/Web/UI/WebControls/TDataBoundControl.php b/framework/Web/UI/WebControls/TDataBoundControl.php index 294dd416..076179fb 100644 --- a/framework/Web/UI/WebControls/TDataBoundControl.php +++ b/framework/Web/UI/WebControls/TDataBoundControl.php @@ -1,587 +1,587 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TDataSourceControl'); -Prado::using('System.Web.UI.WebControls.TDataSourceView'); -Prado::using('System.Collections.TPagedDataSource'); - -/** - * TDataBoundControl class. - * - * TDataBoundControl is the based class for controls that need to populate - * data from data sources. It provides basic properties and methods that allow - * the derived controls to associate with data sources and retrieve data from them. - * - * TBC.... - * - * TDataBoundControl is equipped with paging capabilities. By setting - * {@link setAllowPaging AllowPaging} to true, the input data will be paged - * and only one page of data is actually populated into the data-bound control. - * This saves a lot of memory when dealing with larget datasets. - * - * To specify the number of data items displayed on each page, set - * the {@link setPageSize PageSize} property, and to specify which - * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. - * - * When the size of the original data is too big to be loaded all in the memory, - * one can enable custom paging. In custom paging, the total number of data items - * is specified manually via {@link setVirtualItemCount VirtualItemCount}, - * and the data source only needs to contain the current page of data. To enable - * custom paging, set {@link setAllowCustomPaging AllowCustomPaging} to true. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TDataBoundControl extends TWebControl -{ - private $_initialized=false; - private $_dataSource=null; - private $_requiresBindToNull=false; - private $_requiresDataBinding=false; - private $_prerendered=false; - private $_currentView=null; - private $_currentDataSource=null; - private $_currentViewValid=false; - private $_currentDataSourceValid=false; - private $_currentViewIsFromDataSourceID=false; - private $_parameters=null; - private $_isDataBound=false; - - /** - * @return Traversable data source object, defaults to null. - */ - public function getDataSource() - { - return $this->_dataSource; - } - - /** - * Sets the data source object associated with the databound control. - * The data source must implement Traversable interface. - * If an array is given, it will be converted to xxx. - * If a string is given, it will be converted to xxx. - * @param Traversable|array|string data source object - */ - public function setDataSource($value) - { - $this->_dataSource=$this->validateDataSource($value); - $this->onDataSourceChanged(); - } - - /** - * @return string ID path to the data source control. Defaults to empty. - */ - public function getDataSourceID() - { - return $this->getViewState('DataSourceID',''); - } - - /** - * @param string ID path to the data source control. The data source - * control must be locatable via {@link TControl::findControl} call. - */ - public function setDataSourceID($value) - { - $dsid=$this->getViewState('DataSourceID',''); - if($dsid!=='' && $value==='') - $this->_requiresBindToNull=true; - $this->setViewState('DataSourceID',$value,''); - $this->onDataSourceChanged(); - } - - /** - * @return boolean if the databound control uses the data source specified - * by {@link setDataSourceID}, or it uses the data source object specified - * by {@link setDataSource}. - */ - protected function getUsingDataSourceID() - { - return $this->getDataSourceID()!==''; - } - - /** - * Sets {@link setRequiresDataBinding RequiresDataBinding} as true if the control is initialized. - * This method is invoked when either {@link setDataSource} or {@link setDataSourceID} is changed. - */ - public function onDataSourceChanged() - { - $this->_currentViewValid=false; - $this->_currentDataSourceValid=false; - if($this->getInitialized()) - $this->setRequiresDataBinding(true); - } - - /** - * @return boolean whether the databound control has been initialized. - * By default, the control is initialized after its viewstate has been restored. - */ - protected function getInitialized() - { - return $this->_initialized; - } - - /** - * Sets a value indicating whether the databound control is initialized. - * If initialized, any modification to {@link setDataSource DataSource} or - * {@link setDataSourceID DataSourceID} will set {@link setRequiresDataBinding RequiresDataBinding} - * as true. - * @param boolean a value indicating whether the databound control is initialized. - */ - protected function setInitialized($value) - { - $this->_initialized=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether databind has been invoked in the previous page request - */ - protected function getIsDataBound() - { - return $this->_isDataBound; - } - - /** - * @param boolean if databind has been invoked in this page request - */ - protected function setIsDataBound($value) - { - $this->_isDataBound=$value; - } - - /** - * @return boolean whether a databind call is required (by the data bound control) - */ - protected function getRequiresDataBinding() - { - return $this->_requiresDataBinding; - } - - /** - * @return boolean whether paging is enabled. Defaults to false. - */ - public function getAllowPaging() - { - return $this->getViewState('AllowPaging',false); - } - - /** - * @param boolean whether paging is enabled - */ - public function setAllowPaging($value) - { - $this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether the custom paging is enabled. Defaults to false. - */ - public function getAllowCustomPaging() - { - return $this->getViewState('AllowCustomPaging',false); - } - - /** - * Sets a value indicating whether the custom paging should be enabled. - * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} - * property is used to determine the paging, and the data items in the - * {@link setDataSource DataSource} are considered to be in the current page. - * @param boolean whether the custom paging is enabled - */ - public function setAllowCustomPaging($value) - { - $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return integer the zero-based index of the current page. Defaults to 0. - */ - public function getCurrentPageIndex() - { - return $this->getViewState('CurrentPageIndex',0); - } - - /** - * @param integer the zero-based index of the current page - * @throws TInvalidDataValueException if the value is less than 0 - */ - public function setCurrentPageIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=0; - $this->setViewState('CurrentPageIndex',$value,0); - } - - /** - * @return integer the number of data items on each page. Defaults to 10. - */ - public function getPageSize() - { - return $this->getViewState('PageSize',10); - } - - /** - * @param integer the number of data items on each page. - * @throws TInvalidDataValueException if the value is less than 1 - */ - public function setPageSize($value) - { - if(($value=TPropertyValue::ensureInteger($value))<1) - throw new TInvalidDataValueException('databoundcontrol_pagesize_invalid',get_class($this)); - $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); - } - - /** - * @return integer number of pages of data items available - */ - public function getPageCount() - { - return $this->getViewState('PageCount',1); - } - - /** - * @return integer virtual number of data items in the data source. Defaults to 0. - * @see setAllowCustomPaging - */ - public function getVirtualItemCount() - { - return $this->getViewState('VirtualItemCount',0); - } - - /** - * @param integer virtual number of data items in the data source. - * @throws TInvalidDataValueException if the value is less than 0 - * @see setAllowCustomPaging - */ - public function setVirtualItemCount($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - throw new TInvalidDataValueException('databoundcontrol_virtualitemcount_invalid',get_class($this)); - $this->setViewState('VirtualItemCount',$value,0); - } - - /** - * Sets a value indicating whether a databind call is required by the data bound control. - * If true and the control has been prerendered while it uses the data source - * specified by {@link setDataSourceID}, a databind call will be called by this method. - * @param boolean whether a databind call is required. - */ - protected function setRequiresDataBinding($value) - { - $value=TPropertyValue::ensureBoolean($value); - if($value && $this->_prerendered) - { - $this->_requiresDataBinding=true; - $this->ensureDataBound(); - } - else - $this->_requiresDataBinding=$value; - } - - /** - * Ensures any pending {@link dataBind} is called. - * This method calls {@link dataBind} if the data source is specified - * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding} - * is true. - */ - protected function ensureDataBound() - { - if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull)) - { - $this->dataBind(); - $this->_requiresBindToNull=false; - } - } - - /** - * @return TPagedDataSource creates a paged data source - */ - protected function createPagedDataSource() - { - $ds=new TPagedDataSource; - $ds->setCurrentPageIndex($this->getCurrentPageIndex()); - $ds->setPageSize($this->getPageSize()); - $ds->setAllowPaging($this->getAllowPaging()); - $ds->setAllowCustomPaging($this->getAllowCustomPaging()); - $ds->setVirtualItemCount($this->getVirtualItemCount()); - return $ds; - } - - /** - * Performs databinding. - * This method overrides the parent implementation by calling - * {@link performSelect} which fetches data from data source and does - * the actual binding work. - */ - public function dataBind() - { - $this->setRequiresDataBinding(false); - $this->dataBindProperties(); - $this->onDataBinding(null); - - if(($view=$this->getDataSourceView())!==null) - $data=$view->select($this->getSelectParameters()); - else - $data=null; - - if($data instanceof Traversable) - { - if($this->getAllowPaging()) - { - $ds=$this->createPagedDataSource(); - $ds->setDataSource($data); - $this->setViewState('PageCount',$ds->getPageCount()); - if($ds->getCurrentPageIndex()>=$ds->getPageCount()) - { - $ds->setCurrentPageIndex($ds->getPageCount()-1); - $this->setCurrentPageIndex($ds->getCurrentPageIndex()); - } - $this->performDataBinding($ds); - } - else - { - $this->clearViewState('PageCount'); - $this->performDataBinding($data); - } - } - $this->setIsDataBound(true); - $this->onDataBound(null); - } - - public function dataSourceViewChanged($sender,$param) - { - if(!$this->_ignoreDataSourceViewChanged) - $this->setRequiresDataBinding(true); - } - - protected function getDataSourceView() - { - if(!$this->_currentViewValid) - { - if($this->_currentView && $this->_currentViewIsFromDataSourceID) - $this->_currentView->detachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged')); - if(($dataSource=$this->determineDataSource())!==null) - { - if(($view=$dataSource->getView($this->getDataMember()))===null) - throw new TInvalidDataValueException('databoundcontrol_datamember_invalid',$this->getDataMember()); - if($this->_currentViewIsFromDataSourceID=$this->getUsingDataSourceID()) - $view->attachEventHandler('OnDataSourceViewChanged',array($this,'dataSourceViewChanged')); - $this->_currentView=$view; - } - else - $this->_currentView=null; - $this->_currentViewValid=true; - } - return $this->_currentView; - } - - protected function determineDataSource() - { - if(!$this->_currentDataSourceValid) - { - if(($dsid=$this->getDataSourceID())!=='') - { - if(($dataSource=$this->getNamingContainer()->findControl($dsid))===null) - throw new TInvalidDataValueException('databoundcontrol_datasourceid_inexistent',$dsid); - else if(!($dataSource instanceof IDataSource)) - throw new TInvalidDataValueException('databoundcontrol_datasourceid_invalid',$dsid); - else - $this->_currentDataSource=$dataSource; - } - else if(($dataSource=$this->getDataSource())!==null) - $this->_currentDataSource=new TReadOnlyDataSource($dataSource,$this->getDataMember()); - else - $this->_currentDataSource=null; - $this->_currentDataSourceValid=true; - } - return $this->_currentDataSource; - } - - abstract protected function performDataBinding($data); - - /** - * Raises OnDataBound event. - * This method should be invoked after a databind is performed. - * It is mainly used by framework and component developers. - */ - public function onDataBound($param) - { - $this->raiseEvent('OnDataBound',$this,$param); - } - - /** - * Sets page's OnPreLoad event handler as {@link pagePreLoad}. - * If viewstate is disabled and the current request is a postback, - * {@link setRequiresDataBinding RequiresDataBinding} will be set true. - * This method overrides the parent implementation. - * @param TEventParameter event parameter - */ - public function onInit($param) - { - parent::onInit($param); - $page=$this->getPage(); - $page->attachEventHandler('OnPreLoad',array($this,'pagePreLoad')); - } - - /** - * Sets {@link getInitialized} as true. - * This method is invoked when page raises PreLoad event. - * @param mixed event sender - * @param TEventParameter event parameter - */ - public function pagePreLoad($sender,$param) - { - $this->_initialized=true; - $isPostBack=$this->getPage()->getIsPostBack(); - if(!$isPostBack || ($isPostBack && (!$this->getEnableViewState(true) || !$this->getIsDataBound()))) - $this->setRequiresDataBinding(true); - } - - /** - * Ensures any pending databind is performed. - * This method overrides the parent implementation. - * @param TEventParameter event parameter - */ - public function onPreRender($param) - { - $this->_prerendered=true; - $this->ensureDataBound(); - parent::onPreRender($param); - } - - /** - * Validates if the parameter is a valid data source. - * If it is a string or an array, it will be converted as a TList object. - * @param Traversable|array|string data source to be validated - * @return Traversable the data that is traversable - * @throws TInvalidDataTypeException if the data is neither null nor Traversable - */ - protected function validateDataSource($value) - { - if(is_string($value)) - { - $list=new TList; - foreach(TPropertyValue::ensureArray($value) as $key=>$value) - { - if(is_array($value)) - $list->add($value); - else - $list->add(array($value,is_string($key)?$key:$value)); - } - return $list; - } - else if(is_array($value)) - return new TMap($value); - else if($value instanceof TDbDataReader) { - // read array from TDbDataReader since it's forward-only stream and can only be traversed once - return $value->readAll(); - } - else if(($value instanceof Traversable) || $value===null) - return $value; - else - throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid',get_class($this)); - } - - public function getDataMember() - { - return $this->getViewState('DataMember',''); - } - - public function setDataMember($value) - { - $this->setViewState('DataMember',$value,''); - } - - public function getSelectParameters() - { - if(!$this->_parameters) - $this->_parameters=new TDataSourceSelectParameters; - return $this->_parameters; - } -} - - -/** - * TListItemType class. - * TListItemType defines the enumerable type for the possible types - * that databound list items could take. - * - * The following enumerable values are defined: - * - Header: header item - * - Footer: footer item - * - Item: content item (neither header nor footer) - * - Separator: separator between items - * - AlternatingItem: alternating content item - * - EditItem: content item in edit mode - * - SelectedItem: selected content item - * - Pager: pager - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TListItemType extends TEnumerable -{ - const Header='Header'; - const Footer='Footer'; - const Item='Item'; - const Separator='Separator'; - const AlternatingItem='AlternatingItem'; - const EditItem='EditItem'; - const SelectedItem='SelectedItem'; - const Pager='Pager'; -} - - -/** - * IItemDataRenderer interface. - * - * IItemDataRenderer defines the interface that an item renderer - * needs to implement. Besides the {@link getData Data} property, a list item - * renderer also needs to provide {@link getItemIndex ItemIndex} and - * {@link getItemType ItemType} property. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.0 - */ -interface IItemDataRenderer extends IDataRenderer -{ - /** - * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. - * If the item is not in the collection (e.g. it is a header item), it returns -1. - * @return integer zero-based index of the item. - */ - public function getItemIndex(); - - /** - * Sets the zero-based index for the item. - * If the item is not in the item collection (e.g. it is a header item), -1 should be used. - * @param integer zero-based index of the item. - */ - public function setItemIndex($value); - - /** - * @return TListItemType the item type. - */ - public function getItemType(); - - /** - * @param TListItemType the item type. - */ - public function setItemType($value); -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataSourceControl'); +Prado::using('System.Web.UI.WebControls.TDataSourceView'); +Prado::using('System.Collections.TPagedDataSource'); + +/** + * TDataBoundControl class. + * + * TDataBoundControl is the based class for controls that need to populate + * data from data sources. It provides basic properties and methods that allow + * the derived controls to associate with data sources and retrieve data from them. + * + * TBC.... + * + * TDataBoundControl is equipped with paging capabilities. By setting + * {@link setAllowPaging AllowPaging} to true, the input data will be paged + * and only one page of data is actually populated into the data-bound control. + * This saves a lot of memory when dealing with larget datasets. + * + * To specify the number of data items displayed on each page, set + * the {@link setPageSize PageSize} property, and to specify which + * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. + * + * When the size of the original data is too big to be loaded all in the memory, + * one can enable custom paging. In custom paging, the total number of data items + * is specified manually via {@link setVirtualItemCount VirtualItemCount}, + * and the data source only needs to contain the current page of data. To enable + * custom paging, set {@link setAllowCustomPaging AllowCustomPaging} to true. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataBoundControl extends TWebControl +{ + private $_initialized=false; + private $_dataSource=null; + private $_requiresBindToNull=false; + private $_requiresDataBinding=false; + private $_prerendered=false; + private $_currentView=null; + private $_currentDataSource=null; + private $_currentViewValid=false; + private $_currentDataSourceValid=false; + private $_currentViewIsFromDataSourceID=false; + private $_parameters=null; + private $_isDataBound=false; + + /** + * @return Traversable data source object, defaults to null. + */ + public function getDataSource() + { + return $this->_dataSource; + } + + /** + * Sets the data source object associated with the databound control. + * The data source must implement Traversable interface. + * If an array is given, it will be converted to xxx. + * If a string is given, it will be converted to xxx. + * @param Traversable|array|string data source object + */ + public function setDataSource($value) + { + $this->_dataSource=$this->validateDataSource($value); + $this->onDataSourceChanged(); + } + + /** + * @return string ID path to the data source control. Defaults to empty. + */ + public function getDataSourceID() + { + return $this->getViewState('DataSourceID',''); + } + + /** + * @param string ID path to the data source control. The data source + * control must be locatable via {@link TControl::findControl} call. + */ + public function setDataSourceID($value) + { + $dsid=$this->getViewState('DataSourceID',''); + if($dsid!=='' && $value==='') + $this->_requiresBindToNull=true; + $this->setViewState('DataSourceID',$value,''); + $this->onDataSourceChanged(); + } + + /** + * @return boolean if the databound control uses the data source specified + * by {@link setDataSourceID}, or it uses the data source object specified + * by {@link setDataSource}. + */ + protected function getUsingDataSourceID() + { + return $this->getDataSourceID()!==''; + } + + /** + * Sets {@link setRequiresDataBinding RequiresDataBinding} as true if the control is initialized. + * This method is invoked when either {@link setDataSource} or {@link setDataSourceID} is changed. + */ + public function onDataSourceChanged() + { + $this->_currentViewValid=false; + $this->_currentDataSourceValid=false; + if($this->getInitialized()) + $this->setRequiresDataBinding(true); + } + + /** + * @return boolean whether the databound control has been initialized. + * By default, the control is initialized after its viewstate has been restored. + */ + protected function getInitialized() + { + return $this->_initialized; + } + + /** + * Sets a value indicating whether the databound control is initialized. + * If initialized, any modification to {@link setDataSource DataSource} or + * {@link setDataSourceID DataSourceID} will set {@link setRequiresDataBinding RequiresDataBinding} + * as true. + * @param boolean a value indicating whether the databound control is initialized. + */ + protected function setInitialized($value) + { + $this->_initialized=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether databind has been invoked in the previous page request + */ + protected function getIsDataBound() + { + return $this->_isDataBound; + } + + /** + * @param boolean if databind has been invoked in this page request + */ + protected function setIsDataBound($value) + { + $this->_isDataBound=$value; + } + + /** + * @return boolean whether a databind call is required (by the data bound control) + */ + protected function getRequiresDataBinding() + { + return $this->_requiresDataBinding; + } + + /** + * @return boolean whether paging is enabled. Defaults to false. + */ + public function getAllowPaging() + { + return $this->getViewState('AllowPaging',false); + } + + /** + * @param boolean whether paging is enabled + */ + public function setAllowPaging($value) + { + $this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the custom paging is enabled. Defaults to false. + */ + public function getAllowCustomPaging() + { + return $this->getViewState('AllowCustomPaging',false); + } + + /** + * Sets a value indicating whether the custom paging should be enabled. + * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} + * property is used to determine the paging, and the data items in the + * {@link setDataSource DataSource} are considered to be in the current page. + * @param boolean whether the custom paging is enabled + */ + public function setAllowCustomPaging($value) + { + $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer the zero-based index of the current page. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->getViewState('CurrentPageIndex',0); + } + + /** + * @param integer the zero-based index of the current page + * @throws TInvalidDataValueException if the value is less than 0 + */ + public function setCurrentPageIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->setViewState('CurrentPageIndex',$value,0); + } + + /** + * @return integer the number of data items on each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->getViewState('PageSize',10); + } + + /** + * @param integer the number of data items on each page. + * @throws TInvalidDataValueException if the value is less than 1 + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('databoundcontrol_pagesize_invalid',get_class($this)); + $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); + } + + /** + * @return integer number of pages of data items available + */ + public function getPageCount() + { + return $this->getViewState('PageCount',1); + } + + /** + * @return integer virtual number of data items in the data source. Defaults to 0. + * @see setAllowCustomPaging + */ + public function getVirtualItemCount() + { + return $this->getViewState('VirtualItemCount',0); + } + + /** + * @param integer virtual number of data items in the data source. + * @throws TInvalidDataValueException if the value is less than 0 + * @see setAllowCustomPaging + */ + public function setVirtualItemCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('databoundcontrol_virtualitemcount_invalid',get_class($this)); + $this->setViewState('VirtualItemCount',$value,0); + } + + /** + * Sets a value indicating whether a databind call is required by the data bound control. + * If true and the control has been prerendered while it uses the data source + * specified by {@link setDataSourceID}, a databind call will be called by this method. + * @param boolean whether a databind call is required. + */ + protected function setRequiresDataBinding($value) + { + $value=TPropertyValue::ensureBoolean($value); + if($value && $this->_prerendered) + { + $this->_requiresDataBinding=true; + $this->ensureDataBound(); + } + else + $this->_requiresDataBinding=$value; + } + + /** + * Ensures any pending {@link dataBind} is called. + * This method calls {@link dataBind} if the data source is specified + * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding} + * is true. + */ + protected function ensureDataBound() + { + if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull)) + { + $this->dataBind(); + $this->_requiresBindToNull=false; + } + } + + /** + * @return TPagedDataSource creates a paged data source + */ + protected function createPagedDataSource() + { + $ds=new TPagedDataSource; + $ds->setCurrentPageIndex($this->getCurrentPageIndex()); + $ds->setPageSize($this->getPageSize()); + $ds->setAllowPaging($this->getAllowPaging()); + $ds->setAllowCustomPaging($this->getAllowCustomPaging()); + $ds->setVirtualItemCount($this->getVirtualItemCount()); + return $ds; + } + + /** + * Performs databinding. + * This method overrides the parent implementation by calling + * {@link performSelect} which fetches data from data source and does + * the actual binding work. + */ + public function dataBind() + { + $this->setRequiresDataBinding(false); + $this->dataBindProperties(); + $this->onDataBinding(null); + + if(($view=$this->getDataSourceView())!==null) + $data=$view->select($this->getSelectParameters()); + else + $data=null; + + if($data instanceof Traversable) + { + if($this->getAllowPaging()) + { + $ds=$this->createPagedDataSource(); + $ds->setDataSource($data); + $this->setViewState('PageCount',$ds->getPageCount()); + if($ds->getCurrentPageIndex()>=$ds->getPageCount()) + { + $ds->setCurrentPageIndex($ds->getPageCount()-1); + $this->setCurrentPageIndex($ds->getCurrentPageIndex()); + } + $this->performDataBinding($ds); + } + else + { + $this->clearViewState('PageCount'); + $this->performDataBinding($data); + } + } + $this->setIsDataBound(true); + $this->onDataBound(null); + } + + public function dataSourceViewChanged($sender,$param) + { + if(!$this->_ignoreDataSourceViewChanged) + $this->setRequiresDataBinding(true); + } + + protected function getDataSourceView() + { + if(!$this->_currentViewValid) + { + if($this->_currentView && $this->_currentViewIsFromDataSourceID) + $this->_currentView->detachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged')); + if(($dataSource=$this->determineDataSource())!==null) + { + if(($view=$dataSource->getView($this->getDataMember()))===null) + throw new TInvalidDataValueException('databoundcontrol_datamember_invalid',$this->getDataMember()); + if($this->_currentViewIsFromDataSourceID=$this->getUsingDataSourceID()) + $view->attachEventHandler('OnDataSourceViewChanged',array($this,'dataSourceViewChanged')); + $this->_currentView=$view; + } + else + $this->_currentView=null; + $this->_currentViewValid=true; + } + return $this->_currentView; + } + + protected function determineDataSource() + { + if(!$this->_currentDataSourceValid) + { + if(($dsid=$this->getDataSourceID())!=='') + { + if(($dataSource=$this->getNamingContainer()->findControl($dsid))===null) + throw new TInvalidDataValueException('databoundcontrol_datasourceid_inexistent',$dsid); + else if(!($dataSource instanceof IDataSource)) + throw new TInvalidDataValueException('databoundcontrol_datasourceid_invalid',$dsid); + else + $this->_currentDataSource=$dataSource; + } + else if(($dataSource=$this->getDataSource())!==null) + $this->_currentDataSource=new TReadOnlyDataSource($dataSource,$this->getDataMember()); + else + $this->_currentDataSource=null; + $this->_currentDataSourceValid=true; + } + return $this->_currentDataSource; + } + + abstract protected function performDataBinding($data); + + /** + * Raises OnDataBound event. + * This method should be invoked after a databind is performed. + * It is mainly used by framework and component developers. + */ + public function onDataBound($param) + { + $this->raiseEvent('OnDataBound',$this,$param); + } + + /** + * Sets page's OnPreLoad event handler as {@link pagePreLoad}. + * If viewstate is disabled and the current request is a postback, + * {@link setRequiresDataBinding RequiresDataBinding} will be set true. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $page=$this->getPage(); + $page->attachEventHandler('OnPreLoad',array($this,'pagePreLoad')); + } + + /** + * Sets {@link getInitialized} as true. + * This method is invoked when page raises PreLoad event. + * @param mixed event sender + * @param TEventParameter event parameter + */ + public function pagePreLoad($sender,$param) + { + $this->_initialized=true; + $isPostBack=$this->getPage()->getIsPostBack(); + if(!$isPostBack || ($isPostBack && (!$this->getEnableViewState(true) || !$this->getIsDataBound()))) + $this->setRequiresDataBinding(true); + } + + /** + * Ensures any pending databind is performed. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onPreRender($param) + { + $this->_prerendered=true; + $this->ensureDataBound(); + parent::onPreRender($param); + } + + /** + * Validates if the parameter is a valid data source. + * If it is a string or an array, it will be converted as a TList object. + * @param Traversable|array|string data source to be validated + * @return Traversable the data that is traversable + * @throws TInvalidDataTypeException if the data is neither null nor Traversable + */ + protected function validateDataSource($value) + { + if(is_string($value)) + { + $list=new TList; + foreach(TPropertyValue::ensureArray($value) as $key=>$value) + { + if(is_array($value)) + $list->add($value); + else + $list->add(array($value,is_string($key)?$key:$value)); + } + return $list; + } + else if(is_array($value)) + return new TMap($value); + else if($value instanceof TDbDataReader) { + // read array from TDbDataReader since it's forward-only stream and can only be traversed once + return $value->readAll(); + } + else if(($value instanceof Traversable) || $value===null) + return $value; + else + throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid',get_class($this)); + } + + public function getDataMember() + { + return $this->getViewState('DataMember',''); + } + + public function setDataMember($value) + { + $this->setViewState('DataMember',$value,''); + } + + public function getSelectParameters() + { + if(!$this->_parameters) + $this->_parameters=new TDataSourceSelectParameters; + return $this->_parameters; + } +} + + +/** + * TListItemType class. + * TListItemType defines the enumerable type for the possible types + * that databound list items could take. + * + * The following enumerable values are defined: + * - Header: header item + * - Footer: footer item + * - Item: content item (neither header nor footer) + * - Separator: separator between items + * - AlternatingItem: alternating content item + * - EditItem: content item in edit mode + * - SelectedItem: selected content item + * - Pager: pager + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TListItemType extends TEnumerable +{ + const Header='Header'; + const Footer='Footer'; + const Item='Item'; + const Separator='Separator'; + const AlternatingItem='AlternatingItem'; + const EditItem='EditItem'; + const SelectedItem='SelectedItem'; + const Pager='Pager'; +} + + +/** + * IItemDataRenderer interface. + * + * IItemDataRenderer defines the interface that an item renderer + * needs to implement. Besides the {@link getData Data} property, a list item + * renderer also needs to provide {@link getItemIndex ItemIndex} and + * {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +interface IItemDataRenderer extends IDataRenderer +{ + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex(); + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value); + + /** + * @return TListItemType the item type. + */ + public function getItemType(); + + /** + * @param TListItemType the item type. + */ + public function setItemType($value); +} + +?> diff --git a/framework/Web/UI/WebControls/TDataGrid.php b/framework/Web/UI/WebControls/TDataGrid.php index 2af66b33..2e2c5252 100644 --- a/framework/Web/UI/WebControls/TDataGrid.php +++ b/framework/Web/UI/WebControls/TDataGrid.php @@ -1,2258 +1,2258 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TBaseList, TPagedDataSource, TDummyDataSource and TTable classes - */ -Prado::using('System.Web.UI.WebControls.TBaseDataList'); -Prado::using('System.Collections.TPagedDataSource'); -Prado::using('System.Collections.TDummyDataSource'); -Prado::using('System.Web.UI.WebControls.TTable'); -Prado::using('System.Web.UI.WebControls.TPanel'); -Prado::using('System.Web.UI.WebControls.TDataGridPagerStyle'); - -/** - * TDataGrid class - * - * TDataGrid represents a data bound and updatable grid control. - * - * To populate data into the datagrid, sets its {@link setDataSource DataSource} - * to a tabular data source and call {@link dataBind()}. - * Each row of data will be represented by an item in the {@link getItems Items} - * collection of the datagrid. - * - * An item can be at one of three states: browsing, selected and edit. - * The state determines how the item will be displayed. For example, if an item - * is in edit state, it may be displayed as a table row with input text boxes - * if the columns are of type {@link TBoundColumn}; and if in browsing state, - * they are displayed as static text. - * - * To change the state of an item, set {@link setEditItemIndex EditItemIndex} - * or {@link setSelectedItemIndex SelectedItemIndex} property. - * - * Each datagrid item has a {@link TDataGridItem::getItemType type} - * which tells the position and state of the item in the datalist. An item in the header - * of the repeater is of type Header. A body item may be of either - * Item, AlternatingItem, SelectedItem or EditItem, depending whether the item - * index is odd or even, whether it is being selected or edited. - * - * A datagrid is specified with a list of columns. Each column specifies how the corresponding - * table column will be displayed. For example, the header/footer text of that column, - * the cells in that column, and so on. The following column types are currently - * provided by the framework, - * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. - * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons - * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. - * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state - * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. - * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. - * - {@link TTemplateColumn}, displaying content based on templates. - * - * There are three ways to specify columns for a datagrid. - *
      - *
    • Automatically generated based on data source. - * By setting {@link setAutoGenerateColumns AutoGenerateColumns} to true, - * a list of columns will be automatically generated based on the schema of the data source. - * Each column corresponds to a column of the data.
    • - *
    • Specified in template. For example, - * - * - * - * - * - * - *
    • - *
    • Manually created in code. Columns can be manipulated via - * the {@link setColumns Columns} property of the datagrid. For example, - * - * $column=new TBoundColumn; - * $datagrid->Columns[]=$column; - * - *
    • - *
    - * Note, automatically generated columns cannot be accessed via - * the {@link getColumns Columns} property. - * - * TDataGrid supports sorting. If the {@link setAllowSorting AllowSorting} - * is set to true, a column with nonempty {@link setSortExpression SortExpression} - * will have its header text displayed as a clickable link button. - * Clicking on the link button will raise {@link onSortCommand OnSortCommand} - * event. You can respond to this event, sort the data source according - * to the event parameter, and then invoke {@link databind()} on the datagrid - * to show to end users the sorted data. - * - * TDataGrid supports paging. If the {@link setAllowPaging AllowPaging} - * is set to true, a pager will be displayed on top and/or bottom of the table. - * How the pager will be displayed is determined by the {@link getPagerStyle PagerStyle} - * property. Clicking on a pager button will raise an {@link onPageIndexChanged OnPageIndexChanged} - * event. You can respond to this event, specify the page to be displayed by - * setting {@link setCurrentPageIndex CurrentPageIndex} property, - * and then invoke {@link databind()} on the datagrid to show to end users - * a new page of data. - * - * TDataGrid supports two kinds of paging. The first one is based on the number of data items in - * datasource. The number of pages {@link getPageCount PageCount} is calculated based - * the item number and the {@link setPageSize PageSize} property. - * The datagrid will manage which section of the data source to be displayed - * based on the {@link setCurrentPageIndex CurrentPageIndex} property. - * The second approach calculates the page number based on the - * {@link setVirtualItemCount VirtualItemCount} property and - * the {@link setPageSize PageSize} property. The datagrid will always - * display from the beginning of the datasource up to the number of - * {@link setPageSize PageSize} data items. This approach is especially - * useful when the datasource may contain too many data items to be managed by - * the datagrid efficiently. - * - * When the datagrid contains a button control that raises an {@link onCommand OnCommand} - * event, the event will be bubbled up to the datagrid control. - * If the event's command name is recognizable by the datagrid control, - * a corresponding item event will be raised. The following item events will be - * raised upon a specific command: - * - OnEditCommand, if CommandName=edit - * - OnCancelCommand, if CommandName=cancel - * - OnSelectCommand, if CommandName=select - * - OnDeleteCommand, if CommandName=delete - * - OnUpdateCommand, if CommandName=update - * - onPageIndexChanged, if CommandName=page - * - OnSortCommand, if CommandName=sort - * Note, an {@link onItemCommand OnItemCommand} event is raised in addition to - * the above specific command events. - * - * TDataGrid also raises an {@link onItemCreated OnItemCreated} event for - * every newly created datagrid item. You can respond to this event to customize - * the content or style of the newly created item. - * - * Note, the data bound to the datagrid are reset to null after databinding. - * There are several ways to access the data associated with a datagrid row: - * - Access the data in {@link onItemDataBound OnItemDataBound} event - * - Use {@link getDataKeys DataKeys} to obtain the data key associated with - * the specified datagrid row and use the key to fetch the corresponding data - * from some persistent storage such as DB. - * - Save the data in viewstate and get it back during postbacks. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGrid extends TBaseDataList implements INamingContainer -{ - /** - * datagrid item types - * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. - */ - const IT_HEADER='Header'; - const IT_FOOTER='Footer'; - const IT_ITEM='Item'; - const IT_SEPARATOR='Separator'; - const IT_ALTERNATINGITEM='AlternatingItem'; - const IT_EDITITEM='EditItem'; - const IT_SELECTEDITEM='SelectedItem'; - const IT_PAGER='Pager'; - - /** - * Command name that TDataGrid understands. - */ - const CMD_SELECT='Select'; - const CMD_EDIT='Edit'; - const CMD_UPDATE='Update'; - const CMD_DELETE='Delete'; - const CMD_CANCEL='Cancel'; - const CMD_SORT='Sort'; - const CMD_PAGE='Page'; - const CMD_PAGE_NEXT='Next'; - const CMD_PAGE_PREV='Previous'; - const CMD_PAGE_FIRST='First'; - const CMD_PAGE_LAST='Last'; - - /** - * @var TDataGridColumnCollection manually created column collection - */ - private $_columns=null; - /** - * @var TDataGridColumnCollection automatically created column collection - */ - private $_autoColumns=null; - /** - * @var TList all columns including both manually and automatically created columns - */ - private $_allColumns=null; - /** - * @var TDataGridItemCollection datagrid item collection - */ - private $_items=null; - /** - * @var TDataGridItem header item - */ - private $_header=null; - /** - * @var TDataGridItem footer item - */ - private $_footer=null; - /** - * @var TPagedDataSource paged data source object - */ - private $_pagedDataSource=null; - private $_topPager=null; - private $_bottomPager=null; - /** - * @var ITemplate template used when empty data is bounded - */ - private $_emptyTemplate=null; - /** - * @var boolean whether empty template is effective - */ - private $_useEmptyTemplate=false; - - /** - * @return string tag name (table) of the datagrid - */ - protected function getTagName() - { - return 'table'; - } - - /** - * @return string Name of the class used in AutoGenerateColumns mode - */ - protected function getAutoGenerateColumnName() - { - return 'TBoundColumn'; - } - - /** - * Adds objects parsed in template to datagrid. - * Datagrid columns are added into {@link getColumns Columns} collection. - * @param mixed object parsed in template - */ - public function addParsedObject($object) - { - if($object instanceof TDataGridColumn) - $this->getColumns()->add($object); - else - parent::addParsedObject($object); // this is needed by EmptyTemplate - } - - /** - * @return TDataGridColumnCollection manually specified datagrid columns - */ - public function getColumns() - { - if(!$this->_columns) - $this->_columns=new TDataGridColumnCollection($this); - return $this->_columns; - } - - /** - * @return TDataGridColumnCollection automatically generated datagrid columns - */ - public function getAutoColumns() - { - if(!$this->_autoColumns) - $this->_autoColumns=new TDataGridColumnCollection($this); - return $this->_autoColumns; - } - - /** - * @return TDataGridItemCollection datagrid item collection - */ - public function getItems() - { - if(!$this->_items) - $this->_items=new TDataGridItemCollection; - return $this->_items; - } - - /** - * @return integer number of items - */ - public function getItemCount() - { - return $this->_items?$this->_items->getCount():0; - } - - /** - * Creates a style object for the control. - * This method creates a {@link TTableStyle} to be used by datagrid. - * @return TTableStyle control style to be used - */ - protected function createStyle() - { - return new TTableStyle; - } - - /** - * @return string the URL of the background image for the datagrid - */ - public function getBackImageUrl() - { - return $this->getStyle()->getBackImageUrl(); - } - - /** - * @param string the URL of the background image for the datagrid - */ - public function setBackImageUrl($value) - { - $this->getStyle()->setBackImageUrl($value); - } - - /** - * @return TTableItemStyle the style for every item - */ - public function getItemStyle() - { - if(($style=$this->getViewState('ItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('ItemStyle',$style,null); - } - return $style; - } - - /** - * @return TTableItemStyle the style for each alternating item - */ - public function getAlternatingItemStyle() - { - if(($style=$this->getViewState('AlternatingItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('AlternatingItemStyle',$style,null); - } - return $style; - } - - /** - * @return TTableItemStyle the style for selected item - */ - public function getSelectedItemStyle() - { - if(($style=$this->getViewState('SelectedItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('SelectedItemStyle',$style,null); - } - return $style; - } - - /** - * @return TTableItemStyle the style for edit item - */ - public function getEditItemStyle() - { - if(($style=$this->getViewState('EditItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('EditItemStyle',$style,null); - } - return $style; - } - - /** - * @return TTableItemStyle the style for header - */ - public function getHeaderStyle() - { - if(($style=$this->getViewState('HeaderStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('HeaderStyle',$style,null); - } - return $style; - } - - /** - * @return TTableItemStyle the style for footer - */ - public function getFooterStyle() - { - if(($style=$this->getViewState('FooterStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('FooterStyle',$style,null); - } - return $style; - } - - /** - * @return TDataGridPagerStyle the style for pager - */ - public function getPagerStyle() - { - if(($style=$this->getViewState('PagerStyle',null))===null) - { - $style=new TDataGridPagerStyle; - $this->setViewState('PagerStyle',$style,null); - } - return $style; - } - - /** - * @return TStyle the style for thead element, if any - * @since 3.1.1 - */ - public function getTableHeadStyle() - { - if(($style=$this->getViewState('TableHeadStyle',null))===null) - { - $style=new TStyle; - $this->setViewState('TableHeadStyle',$style,null); - } - return $style; - } - - /** - * @return TStyle the style for tbody element, if any - * @since 3.1.1 - */ - public function getTableBodyStyle() - { - if(($style=$this->getViewState('TableBodyStyle',null))===null) - { - $style=new TStyle; - $this->setViewState('TableBodyStyle',$style,null); - } - return $style; - } - - /** - * @return TStyle the style for tfoot element, if any - * @since 3.1.1 - */ - public function getTableFootStyle() - { - if(($style=$this->getViewState('TableFootStyle',null))===null) - { - $style=new TStyle; - $this->setViewState('TableFootStyle',$style,null); - } - return $style; - } - - /** - * @return string caption for the datagrid - */ - public function getCaption() - { - return $this->getViewState('Caption',''); - } - - /** - * @param string caption for the datagrid - */ - public function setCaption($value) - { - $this->setViewState('Caption',$value,''); - } - - /** - * @return TTableCaptionAlign datagrid caption alignment. Defaults to TTableCaptionAlign::NotSet. - */ - public function getCaptionAlign() - { - return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); - } - - /** - * @param TTableCaptionAlign datagrid caption alignment. Valid values include - */ - public function setCaptionAlign($value) - { - $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); - } - - /** - * @return TDataGridItem the header item - */ - public function getHeader() - { - return $this->_header; - } - - /** - * @return TDataGridItem the footer item - */ - public function getFooter() - { - return $this->_footer; - } - - /** - * @return TDataGridPager the pager displayed at the top of datagrid. It could be null if paging is disabled. - */ - public function getTopPager() - { - return $this->_topPager; - } - - /** - * @return TDataGridPager the pager displayed at the bottom of datagrid. It could be null if paging is disabled. - */ - public function getBottomPager() - { - return $this->_bottomPager; - } - - /** - * @return TDataGridItem the selected item, null if no item is selected. - */ - public function getSelectedItem() - { - $index=$this->getSelectedItemIndex(); - $items=$this->getItems(); - if($index>=0 && $index<$items->getCount()) - return $items->itemAt($index); - else - return null; - } - - /** - * @return integer the zero-based index of the selected item in {@link getItems Items}. - * A value -1 means no item selected. - */ - public function getSelectedItemIndex() - { - return $this->getViewState('SelectedItemIndex',-1); - } - - /** - * Selects an item by its index in {@link getItems Items}. - * Previously selected item will be un-selected. - * If the item to be selected is already in edit mode, it will remain in edit mode. - * If the index is less than 0, any existing selection will be cleared up. - * @param integer the selected item index - */ - public function setSelectedItemIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - if(($current=$this->getSelectedItemIndex())!==$value) - { - $this->setViewState('SelectedItemIndex',$value,-1); - $items=$this->getItems(); - $itemCount=$items->getCount(); - if($current>=0 && $current<$itemCount) - { - $item=$items->itemAt($current); - if($item->getItemType()!==TListItemType::EditItem) - $item->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); - } - if($value>=0 && $value<$itemCount) - { - $item=$items->itemAt($value); - if($item->getItemType()!==TListItemType::EditItem) - $item->setItemType(TListItemType::SelectedItem); - } - } - } - - /** - * @return TDataGridItem the edit item - */ - public function getEditItem() - { - $index=$this->getEditItemIndex(); - $items=$this->getItems(); - if($index>=0 && $index<$items->getCount()) - return $items->itemAt($index); - else - return null; - } - - /** - * @return integer the zero-based index of the edit item in {@link getItems Items}. - * A value -1 means no item is in edit mode. - */ - public function getEditItemIndex() - { - return $this->getViewState('EditItemIndex',-1); - } - - /** - * Edits an item by its index in {@link getItems Items}. - * Previously editting item will change to normal item state. - * If the index is less than 0, any existing edit item will be cleared up. - * @param integer the edit item index - */ - public function setEditItemIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - if(($current=$this->getEditItemIndex())!==$value) - { - $this->setViewState('EditItemIndex',$value,-1); - $items=$this->getItems(); - $itemCount=$items->getCount(); - if($current>=0 && $current<$itemCount) - $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); - if($value>=0 && $value<$itemCount) - $items->itemAt($value)->setItemType(TListItemType::EditItem); - } - } - - /** - * @return boolean whether sorting is enabled. Defaults to false. - */ - public function getAllowSorting() - { - return $this->getViewState('AllowSorting',false); - } - - /** - * @param boolean whether sorting is enabled - */ - public function setAllowSorting($value) - { - $this->setViewState('AllowSorting',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether datagrid columns should be automatically generated. Defaults to true. - */ - public function getAutoGenerateColumns() - { - return $this->getViewState('AutoGenerateColumns',true); - } - - /** - * @param boolean whether datagrid columns should be automatically generated - */ - public function setAutoGenerateColumns($value) - { - $this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the header should be displayed. Defaults to true. - */ - public function getShowHeader() - { - return $this->getViewState('ShowHeader',true); - } - - /** - * @param boolean whether the header should be displayed - */ - public function setShowHeader($value) - { - $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the footer should be displayed. Defaults to false. - */ - public function getShowFooter() - { - return $this->getViewState('ShowFooter',false); - } - - /** - * @param boolean whether the footer should be displayed - */ - public function setShowFooter($value) - { - $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return ITemplate the template applied when no data is bound to the datagrid - */ - public function getEmptyTemplate() - { - return $this->_emptyTemplate; - } - - /** - * @param ITemplate the template applied when no data is bound to the datagrid - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setEmptyTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_emptyTemplate=$value; - else - throw new TInvalidDataTypeException('datagrid_template_required','EmptyTemplate'); - } - - /** - * This method overrides parent's implementation to handle - * {@link onItemCommand OnItemCommand} event which is bubbled from - * {@link TDataGridItem} child controls. - * If the event parameter is {@link TDataGridCommandEventParameter} and - * the command name is a recognized one, which includes 'select', 'edit', - * 'delete', 'update', and 'cancel' (case-insensitive), then a - * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). - * This method should only be used by control developers. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TDataGridCommandEventParameter) - { - $this->onItemCommand($param); - $command=$param->getCommandName(); - if(strcasecmp($command,self::CMD_SELECT)===0) - { - $this->setSelectedItemIndex($param->getItem()->getItemIndex()); - $this->onSelectedIndexChanged($param); - return true; - } - else if(strcasecmp($command,self::CMD_EDIT)===0) - { - $this->onEditCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_DELETE)===0) - { - $this->onDeleteCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_UPDATE)===0) - { - $this->onUpdateCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_CANCEL)===0) - { - $this->onCancelCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_SORT)===0) - { - $this->onSortCommand(new TDataGridSortCommandEventParameter($sender,$param)); - return true; - } - else if(strcasecmp($command,self::CMD_PAGE)===0) - { - $p=$param->getCommandParameter(); - if(strcasecmp($p,self::CMD_PAGE_NEXT)===0) - $pageIndex=$this->getCurrentPageIndex()+1; - else if(strcasecmp($p,self::CMD_PAGE_PREV)===0) - $pageIndex=$this->getCurrentPageIndex()-1; - else if(strcasecmp($p,self::CMD_PAGE_FIRST)===0) - $pageIndex=0; - else if(strcasecmp($p,self::CMD_PAGE_LAST)===0) - $pageIndex=$this->getPageCount()-1; - else - $pageIndex=TPropertyValue::ensureInteger($p)-1; - $this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender,$pageIndex)); - return true; - } - } - return false; - } - - /** - * Raises OnCancelCommand event. - * This method is invoked when a button control raises OnCommand event - * with cancel command name. - * @param TDataGridCommandEventParameter event parameter - */ - public function onCancelCommand($param) - { - $this->raiseEvent('OnCancelCommand',$this,$param); - } - - /** - * Raises OnDeleteCommand event. - * This method is invoked when a button control raises OnCommand event - * with delete command name. - * @param TDataGridCommandEventParameter event parameter - */ - public function onDeleteCommand($param) - { - $this->raiseEvent('OnDeleteCommand',$this,$param); - } - - /** - * Raises OnEditCommand event. - * This method is invoked when a button control raises OnCommand event - * with edit command name. - * @param TDataGridCommandEventParameter event parameter - */ - public function onEditCommand($param) - { - $this->raiseEvent('OnEditCommand',$this,$param); - } - - /** - * Raises OnItemCommand event. - * This method is invoked when a button control raises OnCommand event. - * @param TDataGridCommandEventParameter event parameter - */ - public function onItemCommand($param) - { - $this->raiseEvent('OnItemCommand',$this,$param); - } - - /** - * Raises OnSortCommand event. - * This method is invoked when a button control raises OnCommand event - * with sort command name. - * @param TDataGridSortCommandEventParameter event parameter - */ - public function onSortCommand($param) - { - $this->raiseEvent('OnSortCommand',$this,$param); - } - - /** - * Raises OnUpdateCommand event. - * This method is invoked when a button control raises OnCommand event - * with update command name. - * @param TDataGridCommandEventParameter event parameter - */ - public function onUpdateCommand($param) - { - $this->raiseEvent('OnUpdateCommand',$this,$param); - } - - /** - * Raises OnItemCreated event. - * This method is invoked right after a datagrid item is created and before - * added to page hierarchy. - * @param TDataGridItemEventParameter event parameter - */ - public function onItemCreated($param) - { - $this->raiseEvent('OnItemCreated',$this,$param); - } - - /** - * Raises OnPagerCreated event. - * This method is invoked right after a datagrid pager is created and before - * added to page hierarchy. - * @param TDataGridPagerEventParameter event parameter - */ - public function onPagerCreated($param) - { - $this->raiseEvent('OnPagerCreated',$this,$param); - } - - /** - * Raises OnItemDataBound event. - * This method is invoked for each datagrid item after it performs - * databinding. - * @param TDataGridItemEventParameter event parameter - */ - public function onItemDataBound($param) - { - $this->raiseEvent('OnItemDataBound',$this,$param); - } - - /** - * Raises OnPageIndexChanged event. - * This method is invoked when current page is changed. - * @param TDataGridPageChangedEventParameter event parameter - */ - public function onPageIndexChanged($param) - { - $this->raiseEvent('OnPageIndexChanged',$this,$param); - } - - /** - * Saves item count in viewstate. - * This method is invoked right before control state is to be saved. - */ - public function saveState() - { - parent::saveState(); - if(!$this->getEnableViewState(true)) - return; - if($this->_items) - $this->setViewState('ItemCount',$this->_items->getCount(),0); - else - $this->clearViewState('ItemCount'); - if($this->_autoColumns) - { - $state=array(); - foreach($this->_autoColumns as $column) - $state[]=$column->saveState(); - $this->setViewState('AutoColumns',$state,array()); - } - else - $this->clearViewState('AutoColumns'); - if($this->_columns) - { - $state=array(); - foreach($this->_columns as $column) - $state[]=$column->saveState(); - $this->setViewState('Columns',$state,array()); - } - else - $this->clearViewState('Columns'); - } - - /** - * Loads item count information from viewstate. - * This method is invoked right after control state is loaded. - */ - public function loadState() - { - parent::loadState(); - if(!$this->getEnableViewState(true)) - return; - if(!$this->getIsDataBound()) - { - $state=$this->getViewState('AutoColumns',array()); - if(!empty($state)) - { - $this->_autoColumns=new TDataGridColumnCollection($this); - foreach($state as $st) - { - $column=new $this->AutoGenerateColumnName; - $column->loadState($st); - $this->_autoColumns->add($column); - } - } - else - $this->_autoColumns=null; - $state=$this->getViewState('Columns',array()); - if($this->_columns && $this->_columns->getCount()===count($state)) - { - $i=0; - foreach($this->_columns as $column) - { - $column->loadState($state[$i]); - $i++; - } - } - $this->restoreGridFromViewState(); - } - } - - /** - * Clears up all items in the datagrid. - */ - public function reset() - { - $this->getControls()->clear(); - $this->getItems()->clear(); - $this->_header=null; - $this->_footer=null; - $this->_topPager=null; - $this->_bottomPager=null; - $this->_useEmptyTemplate=false; - } - - /** - * Restores datagrid content from viewstate. - */ - protected function restoreGridFromViewState() - { - $this->reset(); - - $allowPaging=$this->getAllowPaging(); - - $itemCount=$this->getViewState('ItemCount',0); - $dsIndex=$this->getViewState('DataSourceIndex',0); - - $columns=new TList($this->getColumns()); - $columns->mergeWith($this->_autoColumns); - $this->_allColumns=$columns; - - $items=$this->getItems(); - - if($columns->getCount()) - { - foreach($columns as $column) - $column->initialize(); - $selectedIndex=$this->getSelectedItemIndex(); - $editIndex=$this->getEditItemIndex(); - for($index=0;$index<$itemCount;++$index) - { - if($index===0) - { - if($allowPaging) - $this->_topPager=$this->createPager(); - $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,false,null,$columns); - } - if($index===$editIndex) - $itemType=TListItemType::EditItem; - else if($index===$selectedIndex) - $itemType=TListItemType::SelectedItem; - else if($index % 2) - $itemType=TListItemType::AlternatingItem; - else - $itemType=TListItemType::Item; - $items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns)); - $dsIndex++; - } - if($index>0) - { - $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,false,null,$columns); - if($allowPaging) - $this->_bottomPager=$this->createPager(); - } - } - if(!$dsIndex && $this->_emptyTemplate!==null) - { - $this->_useEmptyTemplate=true; - $this->_emptyTemplate->instantiateIn($this); - } - } - - /** - * Performs databinding to populate datagrid items from data source. - * This method is invoked by {@link dataBind()}. - * You may override this function to provide your own way of data population. - * @param Traversable the bound data - */ - protected function performDataBinding($data) - { - $this->reset(); - $keys=$this->getDataKeys(); - $keys->clear(); - $keyField=$this->getDataKeyField(); - - // get all columns - if($this->getAutoGenerateColumns()) - { - $columns=new TList($this->getColumns()); - $autoColumns=$this->createAutoColumns($data); - $columns->mergeWith($autoColumns); - } - else - $columns=$this->getColumns(); - $this->_allColumns=$columns; - - $items=$this->getItems(); - - $index=0; - $allowPaging=$this->getAllowPaging() && ($data instanceof TPagedDataSource); - $dsIndex=$allowPaging?$data->getFirstIndexInPage():0; - $this->setViewState('DataSourceIndex',$dsIndex,0); - if($columns->getCount()) - { - foreach($columns as $column) - $column->initialize(); - - $selectedIndex=$this->getSelectedItemIndex(); - $editIndex=$this->getEditItemIndex(); - foreach($data as $key=>$row) - { - if($keyField!=='') - $keys->add($this->getDataFieldValue($row,$keyField)); - else - $keys->add($key); - if($index===0) - { - if($allowPaging) - $this->_topPager=$this->createPager(); - $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,true,null,$columns); - } - if($index===$editIndex) - $itemType=TListItemType::EditItem; - else if($index===$selectedIndex) - $itemType=TListItemType::SelectedItem; - else if($index % 2) - $itemType=TListItemType::AlternatingItem; - else - $itemType=TListItemType::Item; - $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$row,$columns)); - $index++; - $dsIndex++; - } - if($index>0) - { - $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,true,null,$columns); - if($allowPaging) - $this->_bottomPager=$this->createPager(); - } - } - $this->setViewState('ItemCount',$index,0); - if(!$dsIndex && $this->_emptyTemplate!==null) - { - $this->_useEmptyTemplate=true; - $this->_emptyTemplate->instantiateIn($this); - $this->dataBindChildren(); - } - } - - /** - * Merges consecutive cells who have the same text. - * @since 3.1.1 - */ - private function groupCells() - { - if(($columns=$this->_allColumns)===null) - return; - $items=$this->getItems(); - foreach($columns as $id=>$column) - { - if(!$column->getEnableCellGrouping()) - continue; - $prevCell=null; - $prevCellText=null; - foreach($items as $item) - { - $itemType=$item->getItemType(); - $cell=$item->getCells()->itemAt($id); - if(!$cell->getVisible()) - continue; - if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) - { - if(($cellText=$this->getCellText($cell))==='') - { - $prevCell=null; - $prevCellText=null; - continue; - } - if($prevCell===null || $prevCellText!==$cellText) - { - $prevCell=$cell; - $prevCellText=$cellText; - } - else - { - if(($rowSpan=$prevCell->getRowSpan())===0) - $rowSpan=1; - $prevCell->setRowSpan($rowSpan+1); - $cell->setVisible(false); - } - } - } - } - } - - private function getCellText($cell) - { - if(($data=$cell->getText())==='' && $cell->getHasControls()) - { - $controls=$cell->getControls(); - foreach($controls as $control) - { - if($control instanceof IDataRenderer) - return $control->getData(); - } - } - return $data; - } - - /** - * Creates a datagrid item instance based on the item type and index. - * @param integer zero-based item index - * @param TListItemType item type - * @return TDataGridItem created data list item - */ - protected function createItem($itemIndex,$dataSourceIndex,$itemType) - { - return new TDataGridItem($itemIndex,$dataSourceIndex,$itemType); - } - - private function createItemInternal($itemIndex,$dataSourceIndex,$itemType,$dataBind,$dataItem,$columns) - { - $item=$this->createItem($itemIndex,$dataSourceIndex,$itemType); - $this->initializeItem($item,$columns); - $param=new TDataGridItemEventParameter($item); - if($dataBind) - { - $item->setDataItem($dataItem); - $this->onItemCreated($param); - $this->getControls()->add($item); - $item->dataBind(); - $this->onItemDataBound($param); - } - else - { - $this->onItemCreated($param); - $this->getControls()->add($item); - } - return $item; - } - - /** - * Initializes a datagrid item and cells inside it - * @param TDataGrid datagrid item to be initialized - * @param TDataGridColumnCollection datagrid columns to be used to initialize the cells in the item - */ - protected function initializeItem($item,$columns) - { - $cells=$item->getCells(); - $itemType=$item->getItemType(); - $index=0; - foreach($columns as $column) - { - if($itemType===TListItemType::Header) - $cell=new TTableHeaderCell; - else - $cell=new TTableCell; - if(($id=$column->getID())!=='') - $item->registerObject($id,$cell); - $cells->add($cell); - $column->initializeCell($cell,$index,$itemType); - $index++; - } - } - - protected function createPager() - { - $pager=new TDataGridPager($this); - $this->buildPager($pager); - $this->onPagerCreated(new TDataGridPagerEventParameter($pager)); - $this->getControls()->add($pager); - return $pager; - } - - /** - * Builds the pager content based on pager style. - * @param TDataGridPager the container for the pager - */ - protected function buildPager($pager) - { - switch($this->getPagerStyle()->getMode()) - { - case TDataGridPagerMode::NextPrev: - $this->buildNextPrevPager($pager); - break; - case TDataGridPagerMode::Numeric: - $this->buildNumericPager($pager); - break; - } - } - - /** - * Creates a pager button. - * Depending on the button type, a TLinkButton or a TButton may be created. - * If it is enabled (clickable), its command name and parameter will also be set. - * Derived classes may override this method to create additional types of buttons, such as TImageButton. - * @param mixed the container pager instance of TActiveDatagridPager - * @param string button type, either LinkButton or PushButton - * @param boolean whether the button should be enabled - * @param string caption of the button - * @param string CommandName corresponding to the OnCommand event of the button - * @param string CommandParameter corresponding to the OnCommand event of the button - * @return mixed the button instance - */ - protected function createPagerButton($pager,$buttonType,$enabled,$text,$commandName,$commandParameter) - { - if($buttonType===TDataGridPagerButtonType::LinkButton) - { - if($enabled) - $button=new TLinkButton; - else - { - $button=new TLabel; - $button->setText($text); - return $button; - } - } - else - { - $button=new TButton; - if(!$enabled) - $button->setEnabled(false); - } - $button->setText($text); - $button->setCommandName($commandName); - $button->setCommandParameter($commandParameter); - $button->setCausesValidation(false); - return $button; - } - - /** - * Builds a next-prev pager - * @param TDataGridPager the container for the pager - */ - protected function buildNextPrevPager($pager) - { - $style=$this->getPagerStyle(); - $buttonType=$style->getButtonType(); - $controls=$pager->getControls(); - $currentPageIndex=$this->getCurrentPageIndex(); - if($currentPageIndex===0) - { - if(($text=$style->getFirstPageText())!=='') - { - $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); - $controls->add($label); - $controls->add("\n"); - } - - $label=$this->createPagerButton($pager,$buttonType,false,$style->getPrevPageText(),'',''); - $controls->add($label); - } - else - { - if(($text=$style->getFirstPageText())!=='') - { - $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); - $controls->add($button); - $controls->add("\n"); - } - - $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,self::CMD_PAGE_PREV); - $controls->add($button); - } - $controls->add("\n"); - if($currentPageIndex===$this->getPageCount()-1) - { - $label=$this->createPagerButton($pager,$buttonType,false,$style->getNextPageText(),'',''); - $controls->add($label); - if(($text=$style->getLastPageText())!=='') - { - $controls->add("\n"); - $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); - $controls->add($label); - } - } - else - { - $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,self::CMD_PAGE_NEXT); - $controls->add($button); - if(($text=$style->getLastPageText())!=='') - { - $controls->add("\n"); - $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); - $controls->add($button); - } - } - } - - /** - * Builds a numeric pager - * @param TDataGridPager the container for the pager - */ - protected function buildNumericPager($pager) - { - $style=$this->getPagerStyle(); - $buttonType=$style->getButtonType(); - $controls=$pager->getControls(); - $pageCount=$this->getPageCount(); - $pageIndex=$this->getCurrentPageIndex()+1; - $maxButtonCount=$style->getPageButtonCount(); - $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; - $startPageIndex=1; - $endPageIndex=$buttonCount; - if($pageIndex>$endPageIndex) - { - $startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1; - if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount) - $endPageIndex=$pageCount; - if($endPageIndex-$startPageIndex+1<$maxButtonCount) - { - if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1) - $startPageIndex=1; - } - } - - if($startPageIndex>1) - { - if(($text=$style->getFirstPageText())!=='') - { - $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); - $controls->add($button); - $controls->add("\n"); - } - $prevPageIndex=$startPageIndex-1; - $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex"); - $controls->add($button); - $controls->add("\n"); - } - - for($i=$startPageIndex;$i<=$endPageIndex;++$i) - { - if($i===$pageIndex) - { - $label=$this->createPagerButton($pager,$buttonType,false,"$i",'',''); - $controls->add($label); - } - else - { - $button=$this->createPagerButton($pager,$buttonType,true,"$i",self::CMD_PAGE,"$i"); - $controls->add($button); - } - if($i<$endPageIndex) - $controls->add("\n"); - } - - if($pageCount>$endPageIndex) - { - $controls->add("\n"); - $nextPageIndex=$endPageIndex+1; - $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,"$nextPageIndex"); - $controls->add($button); - if(($text=$style->getLastPageText())!=='') - { - $controls->add("\n"); - $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); - $controls->add($button); - } - } - } - - /** - * Automatically generates datagrid columns based on datasource schema - * @param Traversable data source bound to the datagrid - * @return TDataGridColumnCollection - */ - protected function createAutoColumns($dataSource) - { - if(!$dataSource) - return null; - $autoColumns=$this->getAutoColumns(); - $autoColumns->clear(); - foreach($dataSource as $row) - { - foreach($row as $key=>$value) - { - $column=new $this->AutoGenerateColumnName; - if(is_string($key)) - { - $column->setHeaderText($key); - $column->setDataField($key); - $column->setSortExpression($key); - $autoColumns->add($column); - } - else - { - $column->setHeaderText(TListItemType::Item); - $column->setDataField($key); - $column->setSortExpression(TListItemType::Item); - $autoColumns->add($column); - } - } - break; - } - return $autoColumns; - } - - /** - * Applies styles to items, header, footer and separators. - * Item styles are applied in a hierarchical way. Style in higher hierarchy - * will inherit from styles in lower hierarchy. - * Starting from the lowest hierarchy, the item styles include - * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, - * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. - * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, - * {@link getEditItemStyle EditItemStyle} will also have red background color - * unless it is set to a different value explicitly. - */ - protected function applyItemStyles() - { - $itemStyle=$this->getViewState('ItemStyle',null); - - $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); - if($itemStyle!==null) - { - if($alternatingItemStyle===null) - $alternatingItemStyle=$itemStyle; - else - $alternatingItemStyle->mergeWith($itemStyle); - } - - $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); - - $editItemStyle=$this->getViewState('EditItemStyle',null); - if($selectedItemStyle!==null) - { - if($editItemStyle===null) - $editItemStyle=$selectedItemStyle; - else - $editItemStyle->mergeWith($selectedItemStyle); - } - - $headerStyle=$this->getViewState('HeaderStyle',null); - $footerStyle=$this->getViewState('FooterStyle',null); - $pagerStyle=$this->getViewState('PagerStyle',null); - $separatorStyle=$this->getViewState('SeparatorStyle',null); - - foreach($this->getControls() as $index=>$item) - { - if(!($item instanceof TDataGridItem) && !($item instanceof TDataGridPager)) - continue; - $itemType=$item->getItemType(); - switch($itemType) - { - case TListItemType::Header: - if($headerStyle) - $item->getStyle()->mergeWith($headerStyle); - if(!$this->getShowHeader()) - $item->setVisible(false); - break; - case TListItemType::Footer: - if($footerStyle) - $item->getStyle()->mergeWith($footerStyle); - if(!$this->getShowFooter()) - $item->setVisible(false); - break; - case TListItemType::Separator: - if($separatorStyle) - $item->getStyle()->mergeWith($separatorStyle); - break; - case TListItemType::Item: - if($itemStyle) - $item->getStyle()->mergeWith($itemStyle); - break; - case TListItemType::AlternatingItem: - if($alternatingItemStyle) - $item->getStyle()->mergeWith($alternatingItemStyle); - break; - case TListItemType::SelectedItem: - if($selectedItemStyle) - $item->getStyle()->mergeWith($selectedItemStyle); - if($index % 2==1) - { - if($itemStyle) - $item->getStyle()->mergeWith($itemStyle); - } - else - { - if($alternatingItemStyle) - $item->getStyle()->mergeWith($alternatingItemStyle); - } - break; - case TListItemType::EditItem: - if($editItemStyle) - $item->getStyle()->mergeWith($editItemStyle); - if($index % 2==1) - { - if($itemStyle) - $item->getStyle()->mergeWith($itemStyle); - } - else - { - if($alternatingItemStyle) - $item->getStyle()->mergeWith($alternatingItemStyle); - } - break; - case TListItemType::Pager: - if($pagerStyle) - { - $item->getStyle()->mergeWith($pagerStyle); - if($index===0) - { - if($pagerStyle->getPosition()===TDataGridPagerPosition::Bottom || !$pagerStyle->getVisible()) - $item->setVisible(false); - } - else - { - if($pagerStyle->getPosition()===TDataGridPagerPosition::Top || !$pagerStyle->getVisible()) - $item->setVisible(false); - } - } - break; - default: - break; - } - if($this->_columns && $itemType!==TListItemType::Pager) - { - $n=$this->_columns->getCount(); - $cells=$item->getCells(); - for($i=0;$i<$n;++$i) - { - $cell=$cells->itemAt($i); - $column=$this->_columns->itemAt($i); - if(!$column->getVisible()) - $cell->setVisible(false); - else - { - if($itemType===TListItemType::Header) - $style=$column->getHeaderStyle(false); - else if($itemType===TListItemType::Footer) - $style=$column->getFooterStyle(false); - else - $style=$column->getItemStyle(false); - if($style!==null) - $cell->getStyle()->mergeWith($style); - } - } - } - } - } - - /** - * Renders the openning tag for the datagrid control which will render table caption if present. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderBeginTag($writer) - { - parent::renderBeginTag($writer); - if(($caption=$this->getCaption())!=='') - { - if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) - $writer->addAttribute('align',strtolower($align)); - $writer->renderBeginTag('caption'); - $writer->write($caption); - $writer->renderEndTag(); - } - } - - /** - * Renders the datagrid. - * @param THtmlWriter writer for the rendering purpose - */ - public function render($writer) - { - if($this->getHasControls()) - { - $this->groupCells(); - if($this->_useEmptyTemplate) - { - $control=new TWebControl; - $control->setID($this->getClientID()); - $control->copyBaseAttributes($this); - if($this->getHasStyle()) - $control->getStyle()->copyFrom($this->getStyle()); - $control->renderBeginTag($writer); - $this->renderContents($writer); - $control->renderEndTag($writer); - } - else if($this->getViewState('ItemCount',0)>0) - { - $this->applyItemStyles(); - if($this->_topPager) - { - $this->_topPager->renderControl($writer); - $writer->writeLine(); - } - $this->renderTable($writer); - if($this->_bottomPager) - { - $writer->writeLine(); - $this->_bottomPager->renderControl($writer); - } - } - } - } - - /** - * Renders the tabular data. - * @param THtmlWriter writer - */ - protected function renderTable($writer) - { - $this->renderBeginTag($writer); - if($this->_header && $this->_header->getVisible()) - { - $writer->writeLine(); - if($style=$this->getViewState('TableHeadStyle',null)) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('thead'); - $this->_header->render($writer); - $writer->renderEndTag(); - } - $writer->writeLine(); - if($style=$this->getViewState('TableBodyStyle',null)) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('tbody'); - foreach($this->getItems() as $item) - $item->renderControl($writer); - $writer->renderEndTag(); - - if($this->_footer && $this->_footer->getVisible()) - { - $writer->writeLine(); - if($style=$this->getViewState('TableFootStyle',null)) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('tfoot'); - $this->_footer->render($writer); - $writer->renderEndTag(); - } - - $writer->writeLine(); - $this->renderEndTag($writer); - } -} - -/** - * TDataGridItemEventParameter class - * - * TDataGridItemEventParameter encapsulates the parameter data for - * {@link TDataGrid::onItemCreated OnItemCreated} event of {@link TDataGrid} controls. - * The {@link getItem Item} property indicates the datagrid item related with the event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridItemEventParameter extends TEventParameter -{ - /** - * The TDataGridItem control responsible for the event. - * @var TDataGridItem - */ - private $_item=null; - - /** - * Constructor. - * @param TDataGridItem datagrid item related with the corresponding event - */ - public function __construct(TDataGridItem $item) - { - $this->_item=$item; - } - - /** - * @return TDataGridItem datagrid item related with the corresponding event - */ - public function getItem() - { - return $this->_item; - } -} - -/** - * TDataGridPagerEventParameter class - * - * TDataGridPagerEventParameter encapsulates the parameter data for - * {@link TDataGrid::onPagerCreated OnPagerCreated} event of {@link TDataGrid} controls. - * The {@link getPager Pager} property indicates the datagrid pager related with the event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridPagerEventParameter extends TEventParameter -{ - /** - * The TDataGridPager control responsible for the event. - * @var TDataGridPager - */ - protected $_pager=null; - - /** - * Constructor. - * @param TDataGridPager datagrid pager related with the corresponding event - */ - public function __construct(TDataGridPager $pager) - { - $this->_pager=$pager; - } - - /** - * @return TDataGridPager datagrid pager related with the corresponding event - */ - public function getPager() - { - return $this->_pager; - } -} - -/** - * TDataGridCommandEventParameter class - * - * TDataGridCommandEventParameter encapsulates the parameter data for - * {@link TDataGrid::onItemCommand ItemCommand} event of {@link TDataGrid} controls. - * - * The {@link getItem Item} property indicates the datagrid item related with the event. - * The {@link getCommandSource CommandSource} refers to the control that originally - * raises the Command event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridCommandEventParameter extends TCommandEventParameter -{ - /** - * @var TDataGridItem the TDataGridItem control responsible for the event. - */ - private $_item=null; - /** - * @var TControl the control originally raises the Command event. - */ - private $_source=null; - - /** - * Constructor. - * @param TDataGridItem datagrid item responsible for the event - * @param TControl original event sender - * @param TCommandEventParameter original event parameter - */ - public function __construct($item,$source,TCommandEventParameter $param) - { - $this->_item=$item; - $this->_source=$source; - parent::__construct($param->getCommandName(),$param->getCommandParameter()); - } - - /** - * @return TDataGridItem the TDataGridItem control responsible for the event. - */ - public function getItem() - { - return $this->_item; - } - - /** - * @return TControl the control originally raises the Command event. - */ - public function getCommandSource() - { - return $this->_source; - } -} - -/** - * TDataGridSortCommandEventParameter class - * - * TDataGridSortCommandEventParameter encapsulates the parameter data for - * {@link TDataGrid::onSortCommand SortCommand} event of {@link TDataGrid} controls. - * - * The {@link getCommandSource CommandSource} property refers to the control - * that originally raises the OnCommand event, while {@link getSortExpression SortExpression} - * gives the sort expression carried with the sort command. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridSortCommandEventParameter extends TEventParameter -{ - /** - * @var string sort expression - */ - private $_sortExpression=''; - /** - * @var TControl original event sender - */ - private $_source=null; - - /** - * Constructor. - * @param TControl the control originally raises the OnCommand event. - * @param TDataGridCommandEventParameter command event parameter - */ - public function __construct($source,TDataGridCommandEventParameter $param) - { - $this->_source=$source; - $this->_sortExpression=$param->getCommandParameter(); - } - - /** - * @return TControl the control originally raises the OnCommand event. - */ - public function getCommandSource() - { - return $this->_source; - } - - /** - * @return string sort expression - */ - public function getSortExpression() - { - return $this->_sortExpression; - } -} - -/** - * TDataGridPageChangedEventParameter class - * - * TDataGridPageChangedEventParameter encapsulates the parameter data for - * {@link TDataGrid::onPageIndexChanged PageIndexChanged} event of {@link TDataGrid} controls. - * - * The {@link getCommandSource CommandSource} property refers to the control - * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex} - * returns the new page index carried with the page command. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridPageChangedEventParameter extends TEventParameter -{ - /** - * @var integer new page index - */ - private $_newIndex; - /** - * @var TControl original event sender - */ - private $_source=null; - - /** - * Constructor. - * @param TControl the control originally raises the OnCommand event. - * @param integer new page index - */ - public function __construct($source,$newPageIndex) - { - $this->_source=$source; - $this->_newIndex=$newPageIndex; - } - - /** - * @return TControl the control originally raises the OnCommand event. - */ - public function getCommandSource() - { - return $this->_source; - } - - /** - * @return integer new page index - */ - public function getNewPageIndex() - { - return $this->_newIndex; - } -} - -/** - * TDataGridItem class - * - * A TDataGridItem control represents an item in the {@link TDataGrid} control, - * such as heading section, footer section, or a data item. - * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> - * and {@link getDataItem DataItem} properties, respectively. The type of the item - * is given by {@link getItemType ItemType} property. Property {@link getDataSourceIndex DataSourceIndex} - * gives the index of the item from the bound data source. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridItem extends TTableRow implements INamingContainer -{ - /** - * @var integer index of the data item in the Items collection of datagrid - */ - private $_itemIndex=''; - /** - * @var integer index of the item from the bound data source - */ - private $_dataSourceIndex=0; - /** - * type of the TDataGridItem - * @var string - */ - private $_itemType=''; - /** - * value of the data item - * @var mixed - */ - private $_data=null; - - /** - * Constructor. - * @param integer zero-based index of the item in the item collection of datagrid - * @param TListItemType item type - */ - public function __construct($itemIndex,$dataSourceIndex,$itemType) - { - $this->_itemIndex=$itemIndex; - $this->_dataSourceIndex=$dataSourceIndex; - $this->setItemType($itemType); - if($itemType===TListItemType::Header) - $this->setTableSection(TTableRowSection::Header); - else if($itemType===TListItemType::Footer) - $this->setTableSection(TTableRowSection::Footer); - } - - /** - * @return TListItemType item type. - */ - public function getItemType() - { - return $this->_itemType; - } - - /** - * @param TListItemType item type - */ - public function setItemType($value) - { - $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); - } - - /** - * @return integer zero-based index of the item in the item collection of datagrid - */ - public function getItemIndex() - { - return $this->_itemIndex; - } - - /** - * @return integer the index of the datagrid item from the bound data source - */ - public function getDataSourceIndex() - { - return $this->_dataSourceIndex; - } - - /** - * @return mixed data associated with the item - * @since 3.1.0 - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed data to be associated with the item - * @since 3.1.0 - */ - public function setData($value) - { - $this->_data=$value; - } - - /** - * This property is deprecated since v3.1.0. - * @return mixed data associated with the item - * @deprecated deprecated since v3.1.0. Use {@link getData} instead. - */ - public function getDataItem() - { - return $this->getData(); - } - - /** - * This property is deprecated since v3.1.0. - * @param mixed data to be associated with the item - * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. - */ - public function setDataItem($value) - { - return $this->setData($value); - } - - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } -} - - -/** - * TDataGridPager class. - * - * TDataGridPager represents a datagrid pager. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridPager extends TPanel implements INamingContainer -{ - private $_dataGrid; - - /** - * Constructor. - * @param TDataGrid datagrid object - */ - public function __construct($dataGrid) - { - $this->_dataGrid=$dataGrid; - } - - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } - - /** - * @return TDataGrid the datagrid owning this pager - */ - public function getDataGrid() - { - return $this->_dataGrid; - } - - /** - * @return string item type. - */ - public function getItemType() - { - return TListItemType::Pager; - } -} - - -/** - * TDataGridItemCollection class. - * - * TDataGridItemCollection represents a collection of data grid items. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridItemCollection extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by inserting only TDataGridItem. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridItem. - */ - public function insertAt($index,$item) - { - if($item instanceof TDataGridItem) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('datagriditemcollection_datagriditem_required'); - } -} - -/** - * TDataGridColumnCollection class. - * - * TDataGridColumnCollection represents a collection of data grid columns. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridColumnCollection extends TList -{ - /** - * the control that owns this collection. - * @var TControl - */ - private $_o; - - /** - * Constructor. - * @param TDataGrid the control that owns this collection. - */ - public function __construct(TDataGrid $owner) - { - $this->_o=$owner; - } - - /** - * @return TDataGrid the control that owns this collection. - */ - protected function getOwner() - { - return $this->_o; - } - - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by inserting only TDataGridColumn. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridColumn. - */ - public function insertAt($index,$item) - { - if($item instanceof TDataGridColumn) - { - $item->setOwner($this->_o); - parent::insertAt($index,$item); - } - else - throw new TInvalidDataTypeException('datagridcolumncollection_datagridcolumn_required'); - } -} - -/** - * TDataGridPagerMode class. - * TDataGridPagerMode defines the enumerable type for the possible modes that a datagrid pager can take. - * - * The following enumerable values are defined: - * - NextPrev: pager buttons are displayed as next and previous pages - * - Numeric: pager buttons are displayed as numeric page numbers - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDataGridPagerMode extends TEnumerable -{ - const NextPrev='NextPrev'; - const Numeric='Numeric'; -} - - -/** - * TDataGridPagerButtonType class. - * TDataGridPagerButtonType defines the enumerable type for the possible types of datagrid pager buttons. - * - * The following enumerable values are defined: - * - LinkButton: link buttons - * - PushButton: form submit buttons - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDataGridPagerButtonType extends TEnumerable -{ - const LinkButton='LinkButton'; - const PushButton='PushButton'; -} - - -/** - * TDataGridPagerPosition class. - * TDataGridPagerPosition defines the enumerable type for the possible positions that a datagrid pager can be located at. - * - * The following enumerable values are defined: - * - Bottom: pager appears only at the bottom of the data grid. - * - Top: pager appears only at the top of the data grid. - * - TopAndBottom: pager appears on both top and bottom of the data grid. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDataGridPagerPosition extends TEnumerable -{ - const Bottom='Bottom'; - const Top='Top'; - const TopAndBottom='TopAndBottom'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TBaseList, TPagedDataSource, TDummyDataSource and TTable classes + */ +Prado::using('System.Web.UI.WebControls.TBaseDataList'); +Prado::using('System.Collections.TPagedDataSource'); +Prado::using('System.Collections.TDummyDataSource'); +Prado::using('System.Web.UI.WebControls.TTable'); +Prado::using('System.Web.UI.WebControls.TPanel'); +Prado::using('System.Web.UI.WebControls.TDataGridPagerStyle'); + +/** + * TDataGrid class + * + * TDataGrid represents a data bound and updatable grid control. + * + * To populate data into the datagrid, sets its {@link setDataSource DataSource} + * to a tabular data source and call {@link dataBind()}. + * Each row of data will be represented by an item in the {@link getItems Items} + * collection of the datagrid. + * + * An item can be at one of three states: browsing, selected and edit. + * The state determines how the item will be displayed. For example, if an item + * is in edit state, it may be displayed as a table row with input text boxes + * if the columns are of type {@link TBoundColumn}; and if in browsing state, + * they are displayed as static text. + * + * To change the state of an item, set {@link setEditItemIndex EditItemIndex} + * or {@link setSelectedItemIndex SelectedItemIndex} property. + * + * Each datagrid item has a {@link TDataGridItem::getItemType type} + * which tells the position and state of the item in the datalist. An item in the header + * of the repeater is of type Header. A body item may be of either + * Item, AlternatingItem, SelectedItem or EditItem, depending whether the item + * index is odd or even, whether it is being selected or edited. + * + * A datagrid is specified with a list of columns. Each column specifies how the corresponding + * table column will be displayed. For example, the header/footer text of that column, + * the cells in that column, and so on. The following column types are currently + * provided by the framework, + * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. + * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons + * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. + * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state + * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. + * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. + * - {@link TTemplateColumn}, displaying content based on templates. + * + * There are three ways to specify columns for a datagrid. + *
      + *
    • Automatically generated based on data source. + * By setting {@link setAutoGenerateColumns AutoGenerateColumns} to true, + * a list of columns will be automatically generated based on the schema of the data source. + * Each column corresponds to a column of the data.
    • + *
    • Specified in template. For example, + * + * + * + * + * + * + *
    • + *
    • Manually created in code. Columns can be manipulated via + * the {@link setColumns Columns} property of the datagrid. For example, + * + * $column=new TBoundColumn; + * $datagrid->Columns[]=$column; + * + *
    • + *
    + * Note, automatically generated columns cannot be accessed via + * the {@link getColumns Columns} property. + * + * TDataGrid supports sorting. If the {@link setAllowSorting AllowSorting} + * is set to true, a column with nonempty {@link setSortExpression SortExpression} + * will have its header text displayed as a clickable link button. + * Clicking on the link button will raise {@link onSortCommand OnSortCommand} + * event. You can respond to this event, sort the data source according + * to the event parameter, and then invoke {@link databind()} on the datagrid + * to show to end users the sorted data. + * + * TDataGrid supports paging. If the {@link setAllowPaging AllowPaging} + * is set to true, a pager will be displayed on top and/or bottom of the table. + * How the pager will be displayed is determined by the {@link getPagerStyle PagerStyle} + * property. Clicking on a pager button will raise an {@link onPageIndexChanged OnPageIndexChanged} + * event. You can respond to this event, specify the page to be displayed by + * setting {@link setCurrentPageIndex CurrentPageIndex} property, + * and then invoke {@link databind()} on the datagrid to show to end users + * a new page of data. + * + * TDataGrid supports two kinds of paging. The first one is based on the number of data items in + * datasource. The number of pages {@link getPageCount PageCount} is calculated based + * the item number and the {@link setPageSize PageSize} property. + * The datagrid will manage which section of the data source to be displayed + * based on the {@link setCurrentPageIndex CurrentPageIndex} property. + * The second approach calculates the page number based on the + * {@link setVirtualItemCount VirtualItemCount} property and + * the {@link setPageSize PageSize} property. The datagrid will always + * display from the beginning of the datasource up to the number of + * {@link setPageSize PageSize} data items. This approach is especially + * useful when the datasource may contain too many data items to be managed by + * the datagrid efficiently. + * + * When the datagrid contains a button control that raises an {@link onCommand OnCommand} + * event, the event will be bubbled up to the datagrid control. + * If the event's command name is recognizable by the datagrid control, + * a corresponding item event will be raised. The following item events will be + * raised upon a specific command: + * - OnEditCommand, if CommandName=edit + * - OnCancelCommand, if CommandName=cancel + * - OnSelectCommand, if CommandName=select + * - OnDeleteCommand, if CommandName=delete + * - OnUpdateCommand, if CommandName=update + * - onPageIndexChanged, if CommandName=page + * - OnSortCommand, if CommandName=sort + * Note, an {@link onItemCommand OnItemCommand} event is raised in addition to + * the above specific command events. + * + * TDataGrid also raises an {@link onItemCreated OnItemCreated} event for + * every newly created datagrid item. You can respond to this event to customize + * the content or style of the newly created item. + * + * Note, the data bound to the datagrid are reset to null after databinding. + * There are several ways to access the data associated with a datagrid row: + * - Access the data in {@link onItemDataBound OnItemDataBound} event + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified datagrid row and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the data in viewstate and get it back during postbacks. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGrid extends TBaseDataList implements INamingContainer +{ + /** + * datagrid item types + * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. + */ + const IT_HEADER='Header'; + const IT_FOOTER='Footer'; + const IT_ITEM='Item'; + const IT_SEPARATOR='Separator'; + const IT_ALTERNATINGITEM='AlternatingItem'; + const IT_EDITITEM='EditItem'; + const IT_SELECTEDITEM='SelectedItem'; + const IT_PAGER='Pager'; + + /** + * Command name that TDataGrid understands. + */ + const CMD_SELECT='Select'; + const CMD_EDIT='Edit'; + const CMD_UPDATE='Update'; + const CMD_DELETE='Delete'; + const CMD_CANCEL='Cancel'; + const CMD_SORT='Sort'; + const CMD_PAGE='Page'; + const CMD_PAGE_NEXT='Next'; + const CMD_PAGE_PREV='Previous'; + const CMD_PAGE_FIRST='First'; + const CMD_PAGE_LAST='Last'; + + /** + * @var TDataGridColumnCollection manually created column collection + */ + private $_columns=null; + /** + * @var TDataGridColumnCollection automatically created column collection + */ + private $_autoColumns=null; + /** + * @var TList all columns including both manually and automatically created columns + */ + private $_allColumns=null; + /** + * @var TDataGridItemCollection datagrid item collection + */ + private $_items=null; + /** + * @var TDataGridItem header item + */ + private $_header=null; + /** + * @var TDataGridItem footer item + */ + private $_footer=null; + /** + * @var TPagedDataSource paged data source object + */ + private $_pagedDataSource=null; + private $_topPager=null; + private $_bottomPager=null; + /** + * @var ITemplate template used when empty data is bounded + */ + private $_emptyTemplate=null; + /** + * @var boolean whether empty template is effective + */ + private $_useEmptyTemplate=false; + + /** + * @return string tag name (table) of the datagrid + */ + protected function getTagName() + { + return 'table'; + } + + /** + * @return string Name of the class used in AutoGenerateColumns mode + */ + protected function getAutoGenerateColumnName() + { + return 'TBoundColumn'; + } + + /** + * Adds objects parsed in template to datagrid. + * Datagrid columns are added into {@link getColumns Columns} collection. + * @param mixed object parsed in template + */ + public function addParsedObject($object) + { + if($object instanceof TDataGridColumn) + $this->getColumns()->add($object); + else + parent::addParsedObject($object); // this is needed by EmptyTemplate + } + + /** + * @return TDataGridColumnCollection manually specified datagrid columns + */ + public function getColumns() + { + if(!$this->_columns) + $this->_columns=new TDataGridColumnCollection($this); + return $this->_columns; + } + + /** + * @return TDataGridColumnCollection automatically generated datagrid columns + */ + public function getAutoColumns() + { + if(!$this->_autoColumns) + $this->_autoColumns=new TDataGridColumnCollection($this); + return $this->_autoColumns; + } + + /** + * @return TDataGridItemCollection datagrid item collection + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TDataGridItemCollection; + return $this->_items; + } + + /** + * @return integer number of items + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableStyle} to be used by datagrid. + * @return TTableStyle control style to be used + */ + protected function createStyle() + { + return new TTableStyle; + } + + /** + * @return string the URL of the background image for the datagrid + */ + public function getBackImageUrl() + { + return $this->getStyle()->getBackImageUrl(); + } + + /** + * @param string the URL of the background image for the datagrid + */ + public function setBackImageUrl($value) + { + $this->getStyle()->setBackImageUrl($value); + } + + /** + * @return TTableItemStyle the style for every item + */ + public function getItemStyle() + { + if(($style=$this->getViewState('ItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for each alternating item + */ + public function getAlternatingItemStyle() + { + if(($style=$this->getViewState('AlternatingItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('AlternatingItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for selected item + */ + public function getSelectedItemStyle() + { + if(($style=$this->getViewState('SelectedItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SelectedItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for edit item + */ + public function getEditItemStyle() + { + if(($style=$this->getViewState('EditItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('EditItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle() + { + if(($style=$this->getViewState('FooterStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @return TDataGridPagerStyle the style for pager + */ + public function getPagerStyle() + { + if(($style=$this->getViewState('PagerStyle',null))===null) + { + $style=new TDataGridPagerStyle; + $this->setViewState('PagerStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for thead element, if any + * @since 3.1.1 + */ + public function getTableHeadStyle() + { + if(($style=$this->getViewState('TableHeadStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableHeadStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for tbody element, if any + * @since 3.1.1 + */ + public function getTableBodyStyle() + { + if(($style=$this->getViewState('TableBodyStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableBodyStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for tfoot element, if any + * @since 3.1.1 + */ + public function getTableFootStyle() + { + if(($style=$this->getViewState('TableFootStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableFootStyle',$style,null); + } + return $style; + } + + /** + * @return string caption for the datagrid + */ + public function getCaption() + { + return $this->getViewState('Caption',''); + } + + /** + * @param string caption for the datagrid + */ + public function setCaption($value) + { + $this->setViewState('Caption',$value,''); + } + + /** + * @return TTableCaptionAlign datagrid caption alignment. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); + } + + /** + * @param TTableCaptionAlign datagrid caption alignment. Valid values include + */ + public function setCaptionAlign($value) + { + $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); + } + + /** + * @return TDataGridItem the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TDataGridItem the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return TDataGridPager the pager displayed at the top of datagrid. It could be null if paging is disabled. + */ + public function getTopPager() + { + return $this->_topPager; + } + + /** + * @return TDataGridPager the pager displayed at the bottom of datagrid. It could be null if paging is disabled. + */ + public function getBottomPager() + { + return $this->_bottomPager; + } + + /** + * @return TDataGridItem the selected item, null if no item is selected. + */ + public function getSelectedItem() + { + $index=$this->getSelectedItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the selected item in {@link getItems Items}. + * A value -1 means no item selected. + */ + public function getSelectedItemIndex() + { + return $this->getViewState('SelectedItemIndex',-1); + } + + /** + * Selects an item by its index in {@link getItems Items}. + * Previously selected item will be un-selected. + * If the item to be selected is already in edit mode, it will remain in edit mode. + * If the index is less than 0, any existing selection will be cleared up. + * @param integer the selected item index + */ + public function setSelectedItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getSelectedItemIndex())!==$value) + { + $this->setViewState('SelectedItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + { + $item=$items->itemAt($current); + if($item->getItemType()!==TListItemType::EditItem) + $item->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); + } + if($value>=0 && $value<$itemCount) + { + $item=$items->itemAt($value); + if($item->getItemType()!==TListItemType::EditItem) + $item->setItemType(TListItemType::SelectedItem); + } + } + } + + /** + * @return TDataGridItem the edit item + */ + public function getEditItem() + { + $index=$this->getEditItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the edit item in {@link getItems Items}. + * A value -1 means no item is in edit mode. + */ + public function getEditItemIndex() + { + return $this->getViewState('EditItemIndex',-1); + } + + /** + * Edits an item by its index in {@link getItems Items}. + * Previously editting item will change to normal item state. + * If the index is less than 0, any existing edit item will be cleared up. + * @param integer the edit item index + */ + public function setEditItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getEditItemIndex())!==$value) + { + $this->setViewState('EditItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); + if($value>=0 && $value<$itemCount) + $items->itemAt($value)->setItemType(TListItemType::EditItem); + } + } + + /** + * @return boolean whether sorting is enabled. Defaults to false. + */ + public function getAllowSorting() + { + return $this->getViewState('AllowSorting',false); + } + + /** + * @param boolean whether sorting is enabled + */ + public function setAllowSorting($value) + { + $this->setViewState('AllowSorting',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether datagrid columns should be automatically generated. Defaults to true. + */ + public function getAutoGenerateColumns() + { + return $this->getViewState('AutoGenerateColumns',true); + } + + /** + * @param boolean whether datagrid columns should be automatically generated + */ + public function setAutoGenerateColumns($value) + { + $this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the header should be displayed. Defaults to true. + */ + public function getShowHeader() + { + return $this->getViewState('ShowHeader',true); + } + + /** + * @param boolean whether the header should be displayed + */ + public function setShowHeader($value) + { + $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the footer should be displayed. Defaults to false. + */ + public function getShowFooter() + { + return $this->getViewState('ShowFooter',false); + } + + /** + * @param boolean whether the footer should be displayed + */ + public function setShowFooter($value) + { + $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return ITemplate the template applied when no data is bound to the datagrid + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the datagrid + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('datagrid_template_required','EmptyTemplate'); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * {@link TDataGridItem} child controls. + * If the event parameter is {@link TDataGridCommandEventParameter} and + * the command name is a recognized one, which includes 'select', 'edit', + * 'delete', 'update', and 'cancel' (case-insensitive), then a + * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TDataGridCommandEventParameter) + { + $this->onItemCommand($param); + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_SELECT)===0) + { + $this->setSelectedItemIndex($param->getItem()->getItemIndex()); + $this->onSelectedIndexChanged($param); + return true; + } + else if(strcasecmp($command,self::CMD_EDIT)===0) + { + $this->onEditCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_DELETE)===0) + { + $this->onDeleteCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_UPDATE)===0) + { + $this->onUpdateCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_SORT)===0) + { + $this->onSortCommand(new TDataGridSortCommandEventParameter($sender,$param)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE)===0) + { + $p=$param->getCommandParameter(); + if(strcasecmp($p,self::CMD_PAGE_NEXT)===0) + $pageIndex=$this->getCurrentPageIndex()+1; + else if(strcasecmp($p,self::CMD_PAGE_PREV)===0) + $pageIndex=$this->getCurrentPageIndex()-1; + else if(strcasecmp($p,self::CMD_PAGE_FIRST)===0) + $pageIndex=0; + else if(strcasecmp($p,self::CMD_PAGE_LAST)===0) + $pageIndex=$this->getPageCount()-1; + else + $pageIndex=TPropertyValue::ensureInteger($p)-1; + $this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender,$pageIndex)); + return true; + } + } + return false; + } + + /** + * Raises OnCancelCommand event. + * This method is invoked when a button control raises OnCommand event + * with cancel command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onCancelCommand($param) + { + $this->raiseEvent('OnCancelCommand',$this,$param); + } + + /** + * Raises OnDeleteCommand event. + * This method is invoked when a button control raises OnCommand event + * with delete command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onDeleteCommand($param) + { + $this->raiseEvent('OnDeleteCommand',$this,$param); + } + + /** + * Raises OnEditCommand event. + * This method is invoked when a button control raises OnCommand event + * with edit command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onEditCommand($param) + { + $this->raiseEvent('OnEditCommand',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked when a button control raises OnCommand event. + * @param TDataGridCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Raises OnSortCommand event. + * This method is invoked when a button control raises OnCommand event + * with sort command name. + * @param TDataGridSortCommandEventParameter event parameter + */ + public function onSortCommand($param) + { + $this->raiseEvent('OnSortCommand',$this,$param); + } + + /** + * Raises OnUpdateCommand event. + * This method is invoked when a button control raises OnCommand event + * with update command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onUpdateCommand($param) + { + $this->raiseEvent('OnUpdateCommand',$this,$param); + } + + /** + * Raises OnItemCreated event. + * This method is invoked right after a datagrid item is created and before + * added to page hierarchy. + * @param TDataGridItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnPagerCreated event. + * This method is invoked right after a datagrid pager is created and before + * added to page hierarchy. + * @param TDataGridPagerEventParameter event parameter + */ + public function onPagerCreated($param) + { + $this->raiseEvent('OnPagerCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked for each datagrid item after it performs + * databinding. + * @param TDataGridItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnPageIndexChanged event. + * This method is invoked when current page is changed. + * @param TDataGridPageChangedEventParameter event parameter + */ + public function onPageIndexChanged($param) + { + $this->raiseEvent('OnPageIndexChanged',$this,$param); + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if(!$this->getEnableViewState(true)) + return; + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + if($this->_autoColumns) + { + $state=array(); + foreach($this->_autoColumns as $column) + $state[]=$column->saveState(); + $this->setViewState('AutoColumns',$state,array()); + } + else + $this->clearViewState('AutoColumns'); + if($this->_columns) + { + $state=array(); + foreach($this->_columns as $column) + $state[]=$column->saveState(); + $this->setViewState('Columns',$state,array()); + } + else + $this->clearViewState('Columns'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getEnableViewState(true)) + return; + if(!$this->getIsDataBound()) + { + $state=$this->getViewState('AutoColumns',array()); + if(!empty($state)) + { + $this->_autoColumns=new TDataGridColumnCollection($this); + foreach($state as $st) + { + $column=new $this->AutoGenerateColumnName; + $column->loadState($st); + $this->_autoColumns->add($column); + } + } + else + $this->_autoColumns=null; + $state=$this->getViewState('Columns',array()); + if($this->_columns && $this->_columns->getCount()===count($state)) + { + $i=0; + foreach($this->_columns as $column) + { + $column->loadState($state[$i]); + $i++; + } + } + $this->restoreGridFromViewState(); + } + } + + /** + * Clears up all items in the datagrid. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + $this->_topPager=null; + $this->_bottomPager=null; + $this->_useEmptyTemplate=false; + } + + /** + * Restores datagrid content from viewstate. + */ + protected function restoreGridFromViewState() + { + $this->reset(); + + $allowPaging=$this->getAllowPaging(); + + $itemCount=$this->getViewState('ItemCount',0); + $dsIndex=$this->getViewState('DataSourceIndex',0); + + $columns=new TList($this->getColumns()); + $columns->mergeWith($this->_autoColumns); + $this->_allColumns=$columns; + + $items=$this->getItems(); + + if($columns->getCount()) + { + foreach($columns as $column) + $column->initialize(); + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + for($index=0;$index<$itemCount;++$index) + { + if($index===0) + { + if($allowPaging) + $this->_topPager=$this->createPager(); + $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,false,null,$columns); + } + if($index===$editIndex) + $itemType=TListItemType::EditItem; + else if($index===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else if($index % 2) + $itemType=TListItemType::AlternatingItem; + else + $itemType=TListItemType::Item; + $items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns)); + $dsIndex++; + } + if($index>0) + { + $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,false,null,$columns); + if($allowPaging) + $this->_bottomPager=$this->createPager(); + } + } + if(!$dsIndex && $this->_emptyTemplate!==null) + { + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); + } + } + + /** + * Performs databinding to populate datagrid items from data source. + * This method is invoked by {@link dataBind()}. + * You may override this function to provide your own way of data population. + * @param Traversable the bound data + */ + protected function performDataBinding($data) + { + $this->reset(); + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + + // get all columns + if($this->getAutoGenerateColumns()) + { + $columns=new TList($this->getColumns()); + $autoColumns=$this->createAutoColumns($data); + $columns->mergeWith($autoColumns); + } + else + $columns=$this->getColumns(); + $this->_allColumns=$columns; + + $items=$this->getItems(); + + $index=0; + $allowPaging=$this->getAllowPaging() && ($data instanceof TPagedDataSource); + $dsIndex=$allowPaging?$data->getFirstIndexInPage():0; + $this->setViewState('DataSourceIndex',$dsIndex,0); + if($columns->getCount()) + { + foreach($columns as $column) + $column->initialize(); + + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + foreach($data as $key=>$row) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($row,$keyField)); + else + $keys->add($key); + if($index===0) + { + if($allowPaging) + $this->_topPager=$this->createPager(); + $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,true,null,$columns); + } + if($index===$editIndex) + $itemType=TListItemType::EditItem; + else if($index===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else if($index % 2) + $itemType=TListItemType::AlternatingItem; + else + $itemType=TListItemType::Item; + $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$row,$columns)); + $index++; + $dsIndex++; + } + if($index>0) + { + $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,true,null,$columns); + if($allowPaging) + $this->_bottomPager=$this->createPager(); + } + } + $this->setViewState('ItemCount',$index,0); + if(!$dsIndex && $this->_emptyTemplate!==null) + { + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); + $this->dataBindChildren(); + } + } + + /** + * Merges consecutive cells who have the same text. + * @since 3.1.1 + */ + private function groupCells() + { + if(($columns=$this->_allColumns)===null) + return; + $items=$this->getItems(); + foreach($columns as $id=>$column) + { + if(!$column->getEnableCellGrouping()) + continue; + $prevCell=null; + $prevCellText=null; + foreach($items as $item) + { + $itemType=$item->getItemType(); + $cell=$item->getCells()->itemAt($id); + if(!$cell->getVisible()) + continue; + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) + { + if(($cellText=$this->getCellText($cell))==='') + { + $prevCell=null; + $prevCellText=null; + continue; + } + if($prevCell===null || $prevCellText!==$cellText) + { + $prevCell=$cell; + $prevCellText=$cellText; + } + else + { + if(($rowSpan=$prevCell->getRowSpan())===0) + $rowSpan=1; + $prevCell->setRowSpan($rowSpan+1); + $cell->setVisible(false); + } + } + } + } + } + + private function getCellText($cell) + { + if(($data=$cell->getText())==='' && $cell->getHasControls()) + { + $controls=$cell->getControls(); + foreach($controls as $control) + { + if($control instanceof IDataRenderer) + return $control->getData(); + } + } + return $data; + } + + /** + * Creates a datagrid item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TDataGridItem created data list item + */ + protected function createItem($itemIndex,$dataSourceIndex,$itemType) + { + return new TDataGridItem($itemIndex,$dataSourceIndex,$itemType); + } + + private function createItemInternal($itemIndex,$dataSourceIndex,$itemType,$dataBind,$dataItem,$columns) + { + $item=$this->createItem($itemIndex,$dataSourceIndex,$itemType); + $this->initializeItem($item,$columns); + $param=new TDataGridItemEventParameter($item); + if($dataBind) + { + $item->setDataItem($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + } + else + { + $this->onItemCreated($param); + $this->getControls()->add($item); + } + return $item; + } + + /** + * Initializes a datagrid item and cells inside it + * @param TDataGrid datagrid item to be initialized + * @param TDataGridColumnCollection datagrid columns to be used to initialize the cells in the item + */ + protected function initializeItem($item,$columns) + { + $cells=$item->getCells(); + $itemType=$item->getItemType(); + $index=0; + foreach($columns as $column) + { + if($itemType===TListItemType::Header) + $cell=new TTableHeaderCell; + else + $cell=new TTableCell; + if(($id=$column->getID())!=='') + $item->registerObject($id,$cell); + $cells->add($cell); + $column->initializeCell($cell,$index,$itemType); + $index++; + } + } + + protected function createPager() + { + $pager=new TDataGridPager($this); + $this->buildPager($pager); + $this->onPagerCreated(new TDataGridPagerEventParameter($pager)); + $this->getControls()->add($pager); + return $pager; + } + + /** + * Builds the pager content based on pager style. + * @param TDataGridPager the container for the pager + */ + protected function buildPager($pager) + { + switch($this->getPagerStyle()->getMode()) + { + case TDataGridPagerMode::NextPrev: + $this->buildNextPrevPager($pager); + break; + case TDataGridPagerMode::Numeric: + $this->buildNumericPager($pager); + break; + } + } + + /** + * Creates a pager button. + * Depending on the button type, a TLinkButton or a TButton may be created. + * If it is enabled (clickable), its command name and parameter will also be set. + * Derived classes may override this method to create additional types of buttons, such as TImageButton. + * @param mixed the container pager instance of TActiveDatagridPager + * @param string button type, either LinkButton or PushButton + * @param boolean whether the button should be enabled + * @param string caption of the button + * @param string CommandName corresponding to the OnCommand event of the button + * @param string CommandParameter corresponding to the OnCommand event of the button + * @return mixed the button instance + */ + protected function createPagerButton($pager,$buttonType,$enabled,$text,$commandName,$commandParameter) + { + if($buttonType===TDataGridPagerButtonType::LinkButton) + { + if($enabled) + $button=new TLinkButton; + else + { + $button=new TLabel; + $button->setText($text); + return $button; + } + } + else + { + $button=new TButton; + if(!$enabled) + $button->setEnabled(false); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCommandParameter($commandParameter); + $button->setCausesValidation(false); + return $button; + } + + /** + * Builds a next-prev pager + * @param TDataGridPager the container for the pager + */ + protected function buildNextPrevPager($pager) + { + $style=$this->getPagerStyle(); + $buttonType=$style->getButtonType(); + $controls=$pager->getControls(); + $currentPageIndex=$this->getCurrentPageIndex(); + if($currentPageIndex===0) + { + if(($text=$style->getFirstPageText())!=='') + { + $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); + $controls->add($label); + $controls->add("\n"); + } + + $label=$this->createPagerButton($pager,$buttonType,false,$style->getPrevPageText(),'',''); + $controls->add($label); + } + else + { + if(($text=$style->getFirstPageText())!=='') + { + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); + $controls->add($button); + $controls->add("\n"); + } + + $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,self::CMD_PAGE_PREV); + $controls->add($button); + } + $controls->add("\n"); + if($currentPageIndex===$this->getPageCount()-1) + { + $label=$this->createPagerButton($pager,$buttonType,false,$style->getNextPageText(),'',''); + $controls->add($label); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); + $controls->add($label); + } + } + else + { + $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,self::CMD_PAGE_NEXT); + $controls->add($button); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); + $controls->add($button); + } + } + } + + /** + * Builds a numeric pager + * @param TDataGridPager the container for the pager + */ + protected function buildNumericPager($pager) + { + $style=$this->getPagerStyle(); + $buttonType=$style->getButtonType(); + $controls=$pager->getControls(); + $pageCount=$this->getPageCount(); + $pageIndex=$this->getCurrentPageIndex()+1; + $maxButtonCount=$style->getPageButtonCount(); + $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; + $startPageIndex=1; + $endPageIndex=$buttonCount; + if($pageIndex>$endPageIndex) + { + $startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1; + if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount) + $endPageIndex=$pageCount; + if($endPageIndex-$startPageIndex+1<$maxButtonCount) + { + if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1) + $startPageIndex=1; + } + } + + if($startPageIndex>1) + { + if(($text=$style->getFirstPageText())!=='') + { + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); + $controls->add($button); + $controls->add("\n"); + } + $prevPageIndex=$startPageIndex-1; + $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex"); + $controls->add($button); + $controls->add("\n"); + } + + for($i=$startPageIndex;$i<=$endPageIndex;++$i) + { + if($i===$pageIndex) + { + $label=$this->createPagerButton($pager,$buttonType,false,"$i",'',''); + $controls->add($label); + } + else + { + $button=$this->createPagerButton($pager,$buttonType,true,"$i",self::CMD_PAGE,"$i"); + $controls->add($button); + } + if($i<$endPageIndex) + $controls->add("\n"); + } + + if($pageCount>$endPageIndex) + { + $controls->add("\n"); + $nextPageIndex=$endPageIndex+1; + $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,"$nextPageIndex"); + $controls->add($button); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); + $controls->add($button); + } + } + } + + /** + * Automatically generates datagrid columns based on datasource schema + * @param Traversable data source bound to the datagrid + * @return TDataGridColumnCollection + */ + protected function createAutoColumns($dataSource) + { + if(!$dataSource) + return null; + $autoColumns=$this->getAutoColumns(); + $autoColumns->clear(); + foreach($dataSource as $row) + { + foreach($row as $key=>$value) + { + $column=new $this->AutoGenerateColumnName; + if(is_string($key)) + { + $column->setHeaderText($key); + $column->setDataField($key); + $column->setSortExpression($key); + $autoColumns->add($column); + } + else + { + $column->setHeaderText(TListItemType::Item); + $column->setDataField($key); + $column->setSortExpression(TListItemType::Item); + $autoColumns->add($column); + } + } + break; + } + return $autoColumns; + } + + /** + * Applies styles to items, header, footer and separators. + * Item styles are applied in a hierarchical way. Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, + * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + */ + protected function applyItemStyles() + { + $itemStyle=$this->getViewState('ItemStyle',null); + + $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); + if($itemStyle!==null) + { + if($alternatingItemStyle===null) + $alternatingItemStyle=$itemStyle; + else + $alternatingItemStyle->mergeWith($itemStyle); + } + + $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); + + $editItemStyle=$this->getViewState('EditItemStyle',null); + if($selectedItemStyle!==null) + { + if($editItemStyle===null) + $editItemStyle=$selectedItemStyle; + else + $editItemStyle->mergeWith($selectedItemStyle); + } + + $headerStyle=$this->getViewState('HeaderStyle',null); + $footerStyle=$this->getViewState('FooterStyle',null); + $pagerStyle=$this->getViewState('PagerStyle',null); + $separatorStyle=$this->getViewState('SeparatorStyle',null); + + foreach($this->getControls() as $index=>$item) + { + if(!($item instanceof TDataGridItem) && !($item instanceof TDataGridPager)) + continue; + $itemType=$item->getItemType(); + switch($itemType) + { + case TListItemType::Header: + if($headerStyle) + $item->getStyle()->mergeWith($headerStyle); + if(!$this->getShowHeader()) + $item->setVisible(false); + break; + case TListItemType::Footer: + if($footerStyle) + $item->getStyle()->mergeWith($footerStyle); + if(!$this->getShowFooter()) + $item->setVisible(false); + break; + case TListItemType::Separator: + if($separatorStyle) + $item->getStyle()->mergeWith($separatorStyle); + break; + case TListItemType::Item: + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + break; + case TListItemType::AlternatingItem: + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + break; + case TListItemType::SelectedItem: + if($selectedItemStyle) + $item->getStyle()->mergeWith($selectedItemStyle); + if($index % 2==1) + { + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + } + else + { + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + } + break; + case TListItemType::EditItem: + if($editItemStyle) + $item->getStyle()->mergeWith($editItemStyle); + if($index % 2==1) + { + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + } + else + { + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + } + break; + case TListItemType::Pager: + if($pagerStyle) + { + $item->getStyle()->mergeWith($pagerStyle); + if($index===0) + { + if($pagerStyle->getPosition()===TDataGridPagerPosition::Bottom || !$pagerStyle->getVisible()) + $item->setVisible(false); + } + else + { + if($pagerStyle->getPosition()===TDataGridPagerPosition::Top || !$pagerStyle->getVisible()) + $item->setVisible(false); + } + } + break; + default: + break; + } + if($this->_columns && $itemType!==TListItemType::Pager) + { + $n=$this->_columns->getCount(); + $cells=$item->getCells(); + for($i=0;$i<$n;++$i) + { + $cell=$cells->itemAt($i); + $column=$this->_columns->itemAt($i); + if(!$column->getVisible()) + $cell->setVisible(false); + else + { + if($itemType===TListItemType::Header) + $style=$column->getHeaderStyle(false); + else if($itemType===TListItemType::Footer) + $style=$column->getFooterStyle(false); + else + $style=$column->getItemStyle(false); + if($style!==null) + $cell->getStyle()->mergeWith($style); + } + } + } + } + } + + /** + * Renders the openning tag for the datagrid control which will render table caption if present. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if(($caption=$this->getCaption())!=='') + { + if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) + $writer->addAttribute('align',strtolower($align)); + $writer->renderBeginTag('caption'); + $writer->write($caption); + $writer->renderEndTag(); + } + } + + /** + * Renders the datagrid. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->getHasControls()) + { + $this->groupCells(); + if($this->_useEmptyTemplate) + { + $control=new TWebControl; + $control->setID($this->getClientID()); + $control->copyBaseAttributes($this); + if($this->getHasStyle()) + $control->getStyle()->copyFrom($this->getStyle()); + $control->renderBeginTag($writer); + $this->renderContents($writer); + $control->renderEndTag($writer); + } + else if($this->getViewState('ItemCount',0)>0) + { + $this->applyItemStyles(); + if($this->_topPager) + { + $this->_topPager->renderControl($writer); + $writer->writeLine(); + } + $this->renderTable($writer); + if($this->_bottomPager) + { + $writer->writeLine(); + $this->_bottomPager->renderControl($writer); + } + } + } + } + + /** + * Renders the tabular data. + * @param THtmlWriter writer + */ + protected function renderTable($writer) + { + $this->renderBeginTag($writer); + if($this->_header && $this->_header->getVisible()) + { + $writer->writeLine(); + if($style=$this->getViewState('TableHeadStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('thead'); + $this->_header->render($writer); + $writer->renderEndTag(); + } + $writer->writeLine(); + if($style=$this->getViewState('TableBodyStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('tbody'); + foreach($this->getItems() as $item) + $item->renderControl($writer); + $writer->renderEndTag(); + + if($this->_footer && $this->_footer->getVisible()) + { + $writer->writeLine(); + if($style=$this->getViewState('TableFootStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('tfoot'); + $this->_footer->render($writer); + $writer->renderEndTag(); + } + + $writer->writeLine(); + $this->renderEndTag($writer); + } +} + +/** + * TDataGridItemEventParameter class + * + * TDataGridItemEventParameter encapsulates the parameter data for + * {@link TDataGrid::onItemCreated OnItemCreated} event of {@link TDataGrid} controls. + * The {@link getItem Item} property indicates the datagrid item related with the event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItemEventParameter extends TEventParameter +{ + /** + * The TDataGridItem control responsible for the event. + * @var TDataGridItem + */ + private $_item=null; + + /** + * Constructor. + * @param TDataGridItem datagrid item related with the corresponding event + */ + public function __construct(TDataGridItem $item) + { + $this->_item=$item; + } + + /** + * @return TDataGridItem datagrid item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TDataGridPagerEventParameter class + * + * TDataGridPagerEventParameter encapsulates the parameter data for + * {@link TDataGrid::onPagerCreated OnPagerCreated} event of {@link TDataGrid} controls. + * The {@link getPager Pager} property indicates the datagrid pager related with the event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPagerEventParameter extends TEventParameter +{ + /** + * The TDataGridPager control responsible for the event. + * @var TDataGridPager + */ + protected $_pager=null; + + /** + * Constructor. + * @param TDataGridPager datagrid pager related with the corresponding event + */ + public function __construct(TDataGridPager $pager) + { + $this->_pager=$pager; + } + + /** + * @return TDataGridPager datagrid pager related with the corresponding event + */ + public function getPager() + { + return $this->_pager; + } +} + +/** + * TDataGridCommandEventParameter class + * + * TDataGridCommandEventParameter encapsulates the parameter data for + * {@link TDataGrid::onItemCommand ItemCommand} event of {@link TDataGrid} controls. + * + * The {@link getItem Item} property indicates the datagrid item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TDataGridItem the TDataGridItem control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the Command event. + */ + private $_source=null; + + /** + * Constructor. + * @param TDataGridItem datagrid item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TDataGridItem the TDataGridItem control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the Command event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TDataGridSortCommandEventParameter class + * + * TDataGridSortCommandEventParameter encapsulates the parameter data for + * {@link TDataGrid::onSortCommand SortCommand} event of {@link TDataGrid} controls. + * + * The {@link getCommandSource CommandSource} property refers to the control + * that originally raises the OnCommand event, while {@link getSortExpression SortExpression} + * gives the sort expression carried with the sort command. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridSortCommandEventParameter extends TEventParameter +{ + /** + * @var string sort expression + */ + private $_sortExpression=''; + /** + * @var TControl original event sender + */ + private $_source=null; + + /** + * Constructor. + * @param TControl the control originally raises the OnCommand event. + * @param TDataGridCommandEventParameter command event parameter + */ + public function __construct($source,TDataGridCommandEventParameter $param) + { + $this->_source=$source; + $this->_sortExpression=$param->getCommandParameter(); + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } + + /** + * @return string sort expression + */ + public function getSortExpression() + { + return $this->_sortExpression; + } +} + +/** + * TDataGridPageChangedEventParameter class + * + * TDataGridPageChangedEventParameter encapsulates the parameter data for + * {@link TDataGrid::onPageIndexChanged PageIndexChanged} event of {@link TDataGrid} controls. + * + * The {@link getCommandSource CommandSource} property refers to the control + * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex} + * returns the new page index carried with the page command. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPageChangedEventParameter extends TEventParameter +{ + /** + * @var integer new page index + */ + private $_newIndex; + /** + * @var TControl original event sender + */ + private $_source=null; + + /** + * Constructor. + * @param TControl the control originally raises the OnCommand event. + * @param integer new page index + */ + public function __construct($source,$newPageIndex) + { + $this->_source=$source; + $this->_newIndex=$newPageIndex; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } + + /** + * @return integer new page index + */ + public function getNewPageIndex() + { + return $this->_newIndex; + } +} + +/** + * TDataGridItem class + * + * A TDataGridItem control represents an item in the {@link TDataGrid} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. Property {@link getDataSourceIndex DataSourceIndex} + * gives the index of the item from the bound data source. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItem extends TTableRow implements INamingContainer +{ + /** + * @var integer index of the data item in the Items collection of datagrid + */ + private $_itemIndex=''; + /** + * @var integer index of the item from the bound data source + */ + private $_dataSourceIndex=0; + /** + * type of the TDataGridItem + * @var string + */ + private $_itemType=''; + /** + * value of the data item + * @var mixed + */ + private $_data=null; + + /** + * Constructor. + * @param integer zero-based index of the item in the item collection of datagrid + * @param TListItemType item type + */ + public function __construct($itemIndex,$dataSourceIndex,$itemType) + { + $this->_itemIndex=$itemIndex; + $this->_dataSourceIndex=$dataSourceIndex; + $this->setItemType($itemType); + if($itemType===TListItemType::Header) + $this->setTableSection(TTableRowSection::Header); + else if($itemType===TListItemType::Footer) + $this->setTableSection(TTableRowSection::Footer); + } + + /** + * @return TListItemType item type. + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * @return integer zero-based index of the item in the item collection of datagrid + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * @return integer the index of the datagrid item from the bound data source + */ + public function getDataSourceIndex() + { + return $this->_dataSourceIndex; + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + + +/** + * TDataGridPager class. + * + * TDataGridPager represents a datagrid pager. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPager extends TPanel implements INamingContainer +{ + private $_dataGrid; + + /** + * Constructor. + * @param TDataGrid datagrid object + */ + public function __construct($dataGrid) + { + $this->_dataGrid=$dataGrid; + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } + + /** + * @return TDataGrid the datagrid owning this pager + */ + public function getDataGrid() + { + return $this->_dataGrid; + } + + /** + * @return string item type. + */ + public function getItemType() + { + return TListItemType::Pager; + } +} + + +/** + * TDataGridItemCollection class. + * + * TDataGridItemCollection represents a collection of data grid items. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TDataGridItem. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridItem. + */ + public function insertAt($index,$item) + { + if($item instanceof TDataGridItem) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('datagriditemcollection_datagriditem_required'); + } +} + +/** + * TDataGridColumnCollection class. + * + * TDataGridColumnCollection represents a collection of data grid columns. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridColumnCollection extends TList +{ + /** + * the control that owns this collection. + * @var TControl + */ + private $_o; + + /** + * Constructor. + * @param TDataGrid the control that owns this collection. + */ + public function __construct(TDataGrid $owner) + { + $this->_o=$owner; + } + + /** + * @return TDataGrid the control that owns this collection. + */ + protected function getOwner() + { + return $this->_o; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TDataGridColumn. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridColumn. + */ + public function insertAt($index,$item) + { + if($item instanceof TDataGridColumn) + { + $item->setOwner($this->_o); + parent::insertAt($index,$item); + } + else + throw new TInvalidDataTypeException('datagridcolumncollection_datagridcolumn_required'); + } +} + +/** + * TDataGridPagerMode class. + * TDataGridPagerMode defines the enumerable type for the possible modes that a datagrid pager can take. + * + * The following enumerable values are defined: + * - NextPrev: pager buttons are displayed as next and previous pages + * - Numeric: pager buttons are displayed as numeric page numbers + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerMode extends TEnumerable +{ + const NextPrev='NextPrev'; + const Numeric='Numeric'; +} + + +/** + * TDataGridPagerButtonType class. + * TDataGridPagerButtonType defines the enumerable type for the possible types of datagrid pager buttons. + * + * The following enumerable values are defined: + * - LinkButton: link buttons + * - PushButton: form submit buttons + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerButtonType extends TEnumerable +{ + const LinkButton='LinkButton'; + const PushButton='PushButton'; +} + + +/** + * TDataGridPagerPosition class. + * TDataGridPagerPosition defines the enumerable type for the possible positions that a datagrid pager can be located at. + * + * The following enumerable values are defined: + * - Bottom: pager appears only at the bottom of the data grid. + * - Top: pager appears only at the top of the data grid. + * - TopAndBottom: pager appears on both top and bottom of the data grid. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerPosition extends TEnumerable +{ + const Bottom='Bottom'; + const Top='Top'; + const TopAndBottom='TopAndBottom'; +} + diff --git a/framework/Web/UI/WebControls/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php index d37fbc40..4794fbf8 100644 --- a/framework/Web/UI/WebControls/TDataGridColumn.php +++ b/framework/Web/UI/WebControls/TDataGridColumn.php @@ -1,567 +1,567 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Util.TDataFieldAccessor'); -Prado::using('System.Web.UI.WebControls.TDataGrid'); - -/** - * TDataGridColumn class - * - * TDataGridColumn serves as the base class for the different column types of - * the {@link TDataGrid} control. - * TDataGridColumn defines the properties and methods that are common among - * all datagrid column types. In particular, it initializes header and footer - * cells according to {@link setHeaderText HeaderText} and {@link getHeaderStyle HeaderStyle} - * {@link setFooterText FooterText} and {@link getFooterStyle FooterStyle} properties. - * If {@link setHeaderImageUrl HeaderImageUrl} is specified, the image - * will be displayed instead in the header cell. - * The {@link getItemStyle ItemStyle} is applied to cells that belong to - * non-header and -footer datagrid items. - * - * When the datagrid enables sorting, if the {@link setSortExpression SortExpression} - * is not empty, the header cell will display a button (linkbutton or imagebutton) - * that will bubble the sort command event to the datagrid. - * - * Since v3.1.0, TDataGridColumn has introduced two new properties {@link setHeaderRenderer HeaderRenderer} - * and {@link setFooterRenderer FooterRenderer} which can be used to specify - * the layout of header and footer column cells. - * A renderer refers to a control class that is to be instantiated as a control. - * For more details, see {@link TRepeater} and {@link TDataList}. - * - * Since v3.1.1, TDataGridColumn has introduced {@link setEnableCellGrouping EnableCellGrouping}. - * If a column has this property set true, consecutive cells having the same content in this - * column will be grouped into one cell. - * Note, there are some limitations to cell grouping. We determine the cell content according to - * the cell's {@link TTableCell::getText Text} property. If the text is empty and the cell has - * some child controls, we will pick up the first control who implements {@link IDataRenderer} - * and obtain its {@link IDataRenderer::getData Data} property. - * - * The following datagrid column types are provided by the framework currently, - * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. - * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons - * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state - * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. - * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. - * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. - * - {@link TTemplateColumn}, displaying content based on templates. - * - * To create your own column class, simply override {@link initializeCell()} method, - * which is the major logic for managing the data and presentation of cells in the column. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TDataGridColumn extends TApplicationComponent -{ - private $_id=''; - private $_owner=null; - private $_viewState=array(); - - /** - * @return string the ID of the column. - */ - public function getID() - { - return $this->_id; - } - - /** - * Sets the ID of the column. - * By explicitly specifying the column ID, one can access the column - * by $templateControl->ColumnID. - * @param string the ID of the column. - * @throws TInvalidDataValueException if the ID is of bad format - */ - public function setID($value) - { - if(!preg_match(TControl::ID_FORMAT,$value)) - throw new TInvalidDataValueException('datagridcolumn_id_invalid',get_class($this),$value); - $this->_id=$value; - } - - /** - * @return string the text to be displayed in the header of this column - */ - public function getHeaderText() - { - return $this->getViewState('HeaderText',''); - } - - /** - * @param string text to be displayed in the header of this column - */ - public function setHeaderText($value) - { - $this->setViewState('HeaderText',$value,''); - } - - /** - * @return string the url of the image to be displayed in header - */ - public function getHeaderImageUrl() - { - return $this->getViewState('HeaderImageUrl',''); - } - - /** - * @param string the url of the image to be displayed in header - */ - public function setHeaderImageUrl($value) - { - $this->setViewState('HeaderImageUrl',$value,''); - } - - /** - * @return string the class name for the column header cell renderer. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getHeaderRenderer() - { - return $this->getViewState('HeaderRenderer',''); - } - - /** - * Sets the column header cell renderer class. - * - * If not empty, the class will be used to instantiate as a child control in the column header cell. - * If the class implements {@link IDataRenderer}, the Data property - * will be set as the {@link getFooterText FooterText}. - * - * @param string the renderer class name in namespace format. - * @since 3.1.0 - */ - public function setHeaderRenderer($value) - { - $this->setViewState('HeaderRenderer',$value,''); - } - - /** - * @param boolean whether to create a style if previously not existing - * @return TTableItemStyle the style for header - */ - public function getHeaderStyle($createStyle=true) - { - if(($style=$this->getViewState('HeaderStyle',null))===null && $createStyle) - { - $style=new TTableItemStyle; - $this->setViewState('HeaderStyle',$style,null); - } - return $style; - } - - /** - * @return string the text to be displayed in the footer of this column - */ - public function getFooterText() - { - return $this->getViewState('FooterText',''); - } - - /** - * @param string text to be displayed in the footer of this column - */ - public function setFooterText($value) - { - $this->setViewState('FooterText',$value,''); - } - - /** - * @return string the class name for the column footer cell renderer. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getFooterRenderer() - { - return $this->getViewState('FooterRenderer',''); - } - - /** - * Sets the column footer cell renderer class. - * - * If not empty, the class will be used to instantiate as a child control in the column footer cell. - * If the class implements {@link IDataRenderer}, the Data property - * will be set as the {@link getFooterText FooterText}. - * - * @param string the renderer class name in namespace format. - * @since 3.1.0 - */ - public function setFooterRenderer($value) - { - $this->setViewState('FooterRenderer',$value,''); - } - - /** - * @param boolean whether to create a style if previously not existing - * @return TTableItemStyle the style for footer - */ - public function getFooterStyle($createStyle=true) - { - if(($style=$this->getViewState('FooterStyle',null))===null && $createStyle) - { - $style=new TTableItemStyle; - $this->setViewState('FooterStyle',$style,null); - } - return $style; - } - - /** - * @param boolean whether to create a style if previously not existing - * @return TTableItemStyle the style for item - */ - public function getItemStyle($createStyle=true) - { - if(($style=$this->getViewState('ItemStyle',null))===null && $createStyle) - { - $style=new TTableItemStyle; - $this->setViewState('ItemStyle',$style,null); - } - return $style; - } - - /** - * @return string the name of the field or expression for sorting - */ - public function getSortExpression() - { - return $this->getViewState('SortExpression',''); - } - - /** - * @param string the name of the field or expression for sorting - */ - public function setSortExpression($value) - { - $this->setViewState('SortExpression',$value,''); - } - - /** - * @return boolean whether cells having the same content should be grouped together. Defaults to false. - * @since 3.1.1 - */ - public function getEnableCellGrouping() - { - return $this->getViewState('EnableCellGrouping',false); - } - - /** - * @param boolean whether cells having the same content should be grouped together. - * @since 3.1.1 - */ - public function setEnableCellGrouping($value) - { - $this->setViewState('EnableCellGrouping',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether the column is visible. Defaults to true. - */ - public function getVisible($checkParents=true) - { - return $this->getViewState('Visible',true); - } - - /** - * @param boolean whether the column is visible - */ - public function setVisible($value) - { - $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Returns a viewstate value. - * - * @param string the name of the viewstate value to be returned - * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned - * @return mixed the viewstate value corresponding to $key - */ - protected function getViewState($key,$defaultValue=null) - { - return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; - } - - /** - * Sets a viewstate value. - * - * Make sure that the viewstate value must be serializable and unserializable. - * @param string the name of the viewstate value - * @param mixed the viewstate value to be set - * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. - */ - protected function setViewState($key,$value,$defaultValue=null) - { - if($value===$defaultValue) - unset($this->_viewState[$key]); - else - $this->_viewState[$key]=$value; - } - - /** - * Loads persistent state values. - * @param mixed state values - */ - public function loadState($state) - { - $this->_viewState=$state; - } - - /** - * Saves persistent state values. - * @return mixed values to be saved - */ - public function saveState() - { - return $this->_viewState; - } - - /** - * @return TDataGrid datagrid that owns this column - */ - public function getOwner() - { - return $this->_owner; - } - - /** - * @param TDataGrid datagrid object that owns this column - */ - public function setOwner(TDataGrid $value) - { - $this->_owner=$value; - } - - /** - * Initializes the column. - * This method is invoked by {@link TDataGrid} when the column - * is about to be used to initialize datagrid items. - * Derived classes may override this method to do additional initialization. - */ - public function initialize() - { - } - - /** - * Fetches the value of the data at the specified field. - * If the data is an array, the field is used as an array key. - * If the data is an of {@link TMap}, {@link TList} or their derived class, - * the field is used as a key value. - * If the data is a component, the field is used as the name of a property. - * @param mixed data containing the field of value - * @param string the data field - * @return mixed data value at the specified field - * @throws TInvalidDataValueException if the data or the field is invalid. - */ - protected function getDataFieldValue($data,$field) - { - return TDataFieldAccessor::getDataFieldValue($data,$field); - } - - - /** - * Initializes the specified cell to its initial values. - * The default implementation sets the content of header and footer cells. - * If sorting is enabled by the grid and sort expression is specified in the column, - * the header cell will show a link/image button. Otherwise, the header/footer cell - * will only show static text/image. - * This method can be overriden to provide customized intialization to column cells. - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if($itemType===TListItemType::Header) - $this->initializeHeaderCell($cell,$columnIndex); - else if($itemType===TListItemType::Footer) - $this->initializeFooterCell($cell,$columnIndex); - } - - /** - * Returns a value indicating whether this column allows sorting. - * The column allows sorting only when {@link getSortExpression SortExpression} - * is not empty and the datagrid allows sorting. - * @return boolean whether this column allows sorting - */ - public function getAllowSorting() - { - return $this->getSortExpression()!=='' && (!$this->_owner || $this->_owner->getAllowSorting()); - } - - /** - * Initializes the header cell. - * - * This method attempts to use {@link getHeaderRenderer HeaderRenderer} to - * instantiate the header cell. If that is not available, it will populate - * the cell with an image or a text string, depending on {@link getHeaderImageUrl HeaderImageUrl} - * and {@link getHeaderText HeaderText} property values. - * - * If the column allows sorting, image or text will be created as - * a button which issues Sort command upon user click. - * - * @param TTableCell the cell to be initialized - * @param integer the index to the Columns property that the cell resides in. - */ - protected function initializeHeaderCell($cell,$columnIndex) - { - $text=$this->getHeaderText(); - - if(($classPath=$this->getHeaderRenderer())!=='') - { - $control=Prado::createComponent($classPath); - $cell->getControls()->add($control); - if($control instanceof IDataRenderer) - { - if($control instanceof IItemDataRenderer) - { - $item=$cell->getParent(); - $control->setItemIndex($item->getItemIndex()); - $control->setItemType($item->getItemType()); - } - $control->setData($text); - } - } - else if($this->getAllowSorting()) - { - $sortExpression=$this->getSortExpression(); - if(($url=$this->getHeaderImageUrl())!=='') - { - $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); - $button->setImageUrl($url); - $button->setCommandName(TDataGrid::CMD_SORT); - $button->setCommandParameter($sortExpression); - if($text!=='') - $button->setAlternateText($text); - $button->setCausesValidation(false); - $cell->getControls()->add($button); - } - else if($text!=='') - { - $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); - $button->setText($text); - $button->setCommandName(TDataGrid::CMD_SORT); - $button->setCommandParameter($sortExpression); - $button->setCausesValidation(false); - $cell->getControls()->add($button); - } - else - $cell->setText(' '); - } - else - { - if(($url=$this->getHeaderImageUrl())!=='') - { - $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); - $image->setImageUrl($url); - if($text!=='') - $image->setAlternateText($text); - $cell->getControls()->add($image); - } - else if($text!=='') - $cell->setText($text); - else - $cell->setText(' '); - } - } - - /** - * Initializes the footer cell. - * - * This method attempts to use {@link getFooterRenderer FooterRenderer} to - * instantiate the footer cell. If that is not available, it will populate - * the cell with a text string specified by {@link getFooterImageUrl FooterImageUrl} - * - * @param TTableCell the cell to be initialized - * @param integer the index to the Columns property that the cell resides in. - */ - protected function initializeFooterCell($cell,$columnIndex) - { - $text=$this->getFooterText(); - if(($classPath=$this->getFooterRenderer())!=='') - { - $control=Prado::createComponent($classPath); - $cell->getControls()->add($control); - if($control instanceof IDataRenderer) - { - if($control instanceof IItemDataRenderer) - { - $item=$cell->getParent(); - $control->setItemIndex($item->getItemIndex()); - $control->setItemType($item->getItemType()); - } - $control->setData($text); - } - } - else if($text!=='') - $cell->setText($text); - else - $cell->setText(' '); - } - - /** - * Formats the text value according to a format string. - * If the format string is empty, the original value is converted into - * a string and returned. - * If the format string starts with '#', the string is treated as a PHP expression - * within which the token '{0}' is translated with the data value to be formated. - * Otherwise, the format string and the data value are passed - * as the first and second parameters in {@link sprintf}. - * @param string format string - * @param mixed the data to be formatted - * @return string the formatted result - */ - protected function formatDataValue($formatString,$value) - { - if($formatString==='') - return TPropertyValue::ensureString($value); - else if($formatString[0]==='#') - { - $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); - try - { - if(eval("\$result=$expression;")===false) - throw new Exception(''); - return $result; - } - catch(Exception $e) - { - throw new TInvalidDataValueException('datagridcolumn_expression_invalid',get_class($this),$expression,$e->getMessage()); - } - } - else - return sprintf($formatString,$value); - } -} - - -/** - * TButtonColumnType class. - * TButtonColumnType defines the enumerable type for the possible types of buttons - * that can be used in a {@link TButtonColumn}. - * - * The following enumerable values are defined: - * - LinkButton: link buttons - * - PushButton: form buttons - * - ImageButton: image buttons - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TButtonColumnType extends TEnumerable -{ - const LinkButton='LinkButton'; - const PushButton='PushButton'; - const ImageButton='ImageButton'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Util.TDataFieldAccessor'); +Prado::using('System.Web.UI.WebControls.TDataGrid'); + +/** + * TDataGridColumn class + * + * TDataGridColumn serves as the base class for the different column types of + * the {@link TDataGrid} control. + * TDataGridColumn defines the properties and methods that are common among + * all datagrid column types. In particular, it initializes header and footer + * cells according to {@link setHeaderText HeaderText} and {@link getHeaderStyle HeaderStyle} + * {@link setFooterText FooterText} and {@link getFooterStyle FooterStyle} properties. + * If {@link setHeaderImageUrl HeaderImageUrl} is specified, the image + * will be displayed instead in the header cell. + * The {@link getItemStyle ItemStyle} is applied to cells that belong to + * non-header and -footer datagrid items. + * + * When the datagrid enables sorting, if the {@link setSortExpression SortExpression} + * is not empty, the header cell will display a button (linkbutton or imagebutton) + * that will bubble the sort command event to the datagrid. + * + * Since v3.1.0, TDataGridColumn has introduced two new properties {@link setHeaderRenderer HeaderRenderer} + * and {@link setFooterRenderer FooterRenderer} which can be used to specify + * the layout of header and footer column cells. + * A renderer refers to a control class that is to be instantiated as a control. + * For more details, see {@link TRepeater} and {@link TDataList}. + * + * Since v3.1.1, TDataGridColumn has introduced {@link setEnableCellGrouping EnableCellGrouping}. + * If a column has this property set true, consecutive cells having the same content in this + * column will be grouped into one cell. + * Note, there are some limitations to cell grouping. We determine the cell content according to + * the cell's {@link TTableCell::getText Text} property. If the text is empty and the cell has + * some child controls, we will pick up the first control who implements {@link IDataRenderer} + * and obtain its {@link IDataRenderer::getData Data} property. + * + * The following datagrid column types are provided by the framework currently, + * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. + * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons + * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state + * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. + * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. + * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. + * - {@link TTemplateColumn}, displaying content based on templates. + * + * To create your own column class, simply override {@link initializeCell()} method, + * which is the major logic for managing the data and presentation of cells in the column. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataGridColumn extends TApplicationComponent +{ + private $_id=''; + private $_owner=null; + private $_viewState=array(); + + /** + * @return string the ID of the column. + */ + public function getID() + { + return $this->_id; + } + + /** + * Sets the ID of the column. + * By explicitly specifying the column ID, one can access the column + * by $templateControl->ColumnID. + * @param string the ID of the column. + * @throws TInvalidDataValueException if the ID is of bad format + */ + public function setID($value) + { + if(!preg_match(TControl::ID_FORMAT,$value)) + throw new TInvalidDataValueException('datagridcolumn_id_invalid',get_class($this),$value); + $this->_id=$value; + } + + /** + * @return string the text to be displayed in the header of this column + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * @param string text to be displayed in the header of this column + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',$value,''); + } + + /** + * @return string the url of the image to be displayed in header + */ + public function getHeaderImageUrl() + { + return $this->getViewState('HeaderImageUrl',''); + } + + /** + * @param string the url of the image to be displayed in header + */ + public function setHeaderImageUrl($value) + { + $this->setViewState('HeaderImageUrl',$value,''); + } + + /** + * @return string the class name for the column header cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the column header cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the column header cell. + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the {@link getFooterText FooterText}. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle($createStyle=true) + { + if(($style=$this->getViewState('HeaderStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return string the text to be displayed in the footer of this column + */ + public function getFooterText() + { + return $this->getViewState('FooterText',''); + } + + /** + * @param string text to be displayed in the footer of this column + */ + public function setFooterText($value) + { + $this->setViewState('FooterText',$value,''); + } + + /** + * @return string the class name for the column footer cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the column footer cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the column footer cell. + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the {@link getFooterText FooterText}. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle($createStyle=true) + { + if(($style=$this->getViewState('FooterStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for item + */ + public function getItemStyle($createStyle=true) + { + if(($style=$this->getViewState('ItemStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return string the name of the field or expression for sorting + */ + public function getSortExpression() + { + return $this->getViewState('SortExpression',''); + } + + /** + * @param string the name of the field or expression for sorting + */ + public function setSortExpression($value) + { + $this->setViewState('SortExpression',$value,''); + } + + /** + * @return boolean whether cells having the same content should be grouped together. Defaults to false. + * @since 3.1.1 + */ + public function getEnableCellGrouping() + { + return $this->getViewState('EnableCellGrouping',false); + } + + /** + * @param boolean whether cells having the same content should be grouped together. + * @since 3.1.1 + */ + public function setEnableCellGrouping($value) + { + $this->setViewState('EnableCellGrouping',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the column is visible. Defaults to true. + */ + public function getVisible($checkParents=true) + { + return $this->getViewState('Visible',true); + } + + /** + * @param boolean whether the column is visible + */ + public function setVisible($value) + { + $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Returns a viewstate value. + * + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + protected function getViewState($key,$defaultValue=null) + { + return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; + } + + /** + * Sets a viewstate value. + * + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + protected function setViewState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_viewState[$key]); + else + $this->_viewState[$key]=$value; + } + + /** + * Loads persistent state values. + * @param mixed state values + */ + public function loadState($state) + { + $this->_viewState=$state; + } + + /** + * Saves persistent state values. + * @return mixed values to be saved + */ + public function saveState() + { + return $this->_viewState; + } + + /** + * @return TDataGrid datagrid that owns this column + */ + public function getOwner() + { + return $this->_owner; + } + + /** + * @param TDataGrid datagrid object that owns this column + */ + public function setOwner(TDataGrid $value) + { + $this->_owner=$value; + } + + /** + * Initializes the column. + * This method is invoked by {@link TDataGrid} when the column + * is about to be used to initialize datagrid items. + * Derived classes may override this method to do additional initialization. + */ + public function initialize() + { + } + + /** + * Fetches the value of the data at the specified field. + * If the data is an array, the field is used as an array key. + * If the data is an of {@link TMap}, {@link TList} or their derived class, + * the field is used as a key value. + * If the data is a component, the field is used as the name of a property. + * @param mixed data containing the field of value + * @param string the data field + * @return mixed data value at the specified field + * @throws TInvalidDataValueException if the data or the field is invalid. + */ + protected function getDataFieldValue($data,$field) + { + return TDataFieldAccessor::getDataFieldValue($data,$field); + } + + + /** + * Initializes the specified cell to its initial values. + * The default implementation sets the content of header and footer cells. + * If sorting is enabled by the grid and sort expression is specified in the column, + * the header cell will show a link/image button. Otherwise, the header/footer cell + * will only show static text/image. + * This method can be overriden to provide customized intialization to column cells. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Header) + $this->initializeHeaderCell($cell,$columnIndex); + else if($itemType===TListItemType::Footer) + $this->initializeFooterCell($cell,$columnIndex); + } + + /** + * Returns a value indicating whether this column allows sorting. + * The column allows sorting only when {@link getSortExpression SortExpression} + * is not empty and the datagrid allows sorting. + * @return boolean whether this column allows sorting + */ + public function getAllowSorting() + { + return $this->getSortExpression()!=='' && (!$this->_owner || $this->_owner->getAllowSorting()); + } + + /** + * Initializes the header cell. + * + * This method attempts to use {@link getHeaderRenderer HeaderRenderer} to + * instantiate the header cell. If that is not available, it will populate + * the cell with an image or a text string, depending on {@link getHeaderImageUrl HeaderImageUrl} + * and {@link getHeaderText HeaderText} property values. + * + * If the column allows sorting, image or text will be created as + * a button which issues Sort command upon user click. + * + * @param TTableCell the cell to be initialized + * @param integer the index to the Columns property that the cell resides in. + */ + protected function initializeHeaderCell($cell,$columnIndex) + { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + } + else if($this->getAllowSorting()) + { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else + { + if(($url=$this->getHeaderImageUrl())!=='') + { + $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } + + /** + * Initializes the footer cell. + * + * This method attempts to use {@link getFooterRenderer FooterRenderer} to + * instantiate the footer cell. If that is not available, it will populate + * the cell with a text string specified by {@link getFooterImageUrl FooterImageUrl} + * + * @param TTableCell the cell to be initialized + * @param integer the index to the Columns property that the cell resides in. + */ + protected function initializeFooterCell($cell,$columnIndex) + { + $text=$this->getFooterText(); + if(($classPath=$this->getFooterRenderer())!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + + /** + * Formats the text value according to a format string. + * If the format string is empty, the original value is converted into + * a string and returned. + * If the format string starts with '#', the string is treated as a PHP expression + * within which the token '{0}' is translated with the data value to be formated. + * Otherwise, the format string and the data value are passed + * as the first and second parameters in {@link sprintf}. + * @param string format string + * @param mixed the data to be formatted + * @return string the formatted result + */ + protected function formatDataValue($formatString,$value) + { + if($formatString==='') + return TPropertyValue::ensureString($value); + else if($formatString[0]==='#') + { + $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidDataValueException('datagridcolumn_expression_invalid',get_class($this),$expression,$e->getMessage()); + } + } + else + return sprintf($formatString,$value); + } +} + + +/** + * TButtonColumnType class. + * TButtonColumnType defines the enumerable type for the possible types of buttons + * that can be used in a {@link TButtonColumn}. + * + * The following enumerable values are defined: + * - LinkButton: link buttons + * - PushButton: form buttons + * - ImageButton: image buttons + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TButtonColumnType extends TEnumerable +{ + const LinkButton='LinkButton'; + const PushButton='PushButton'; + const ImageButton='ImageButton'; +} + diff --git a/framework/Web/UI/WebControls/TDataGridItemRenderer.php b/framework/Web/UI/WebControls/TDataGridItemRenderer.php index 0e30a062..dbbbec11 100644 --- a/framework/Web/UI/WebControls/TDataGridItemRenderer.php +++ b/framework/Web/UI/WebControls/TDataGridItemRenderer.php @@ -1,30 +1,30 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TDataGrid'); -Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); - -/** - * TDataGridItemRenderer class - * - * TDataGridItemRenderer can be used as a convenient base class to - * define an item renderer class specific for {@link TDataGrid}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.0 - */ -class TDataGridItemRenderer extends TItemDataRenderer -{ -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGrid'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TDataGridItemRenderer class + * + * TDataGridItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TDataGrid}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TDataGridItemRenderer extends TItemDataRenderer +{ +} + diff --git a/framework/Web/UI/WebControls/TDataGridPagerStyle.php b/framework/Web/UI/WebControls/TDataGridPagerStyle.php index 12346c06..2464291a 100644 --- a/framework/Web/UI/WebControls/TDataGridPagerStyle.php +++ b/framework/Web/UI/WebControls/TDataGridPagerStyle.php @@ -1,255 +1,255 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TDataGrid'); - -/** - * TDataGridPagerStyle class. - * - * TDataGridPagerStyle specifies the styles available for a datagrid pager. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataGridPagerStyle extends TPanelStyle -{ - private $_mode=null; - private $_nextText=null; - private $_prevText=null; - private $_firstText=null; - private $_lastText=null; - private $_buttonCount=null; - private $_position=null; - private $_visible=null; - private $_buttonType=null; - - /** - * @return TDataGridPagerMode pager mode. Defaults to TDataGridPagerMode::NextPrev. - */ - public function getMode() - { - return $this->_mode===null?TDataGridPagerMode::NextPrev : $this->_mode; - } - - /** - * @param TDataGridPagerMode pager mode. - */ - public function setMode($value) - { - $this->_mode=TPropertyValue::ensureEnum($value,'TDataGridPagerMode'); - } - - /** - * @return TDataGridPagerButtonType the type of command button. Defaults to TDataGridPagerButtonType::LinkButton. - */ - public function getButtonType() - { - return $this->_buttonType===null?TDataGridPagerButtonType::LinkButton:$this->_buttonType; - } - - /** - * @param TDataGridPagerButtonType the type of command button - */ - public function setButtonType($value) - { - $this->_buttonType=TPropertyValue::ensureEnum($value,'TDataGridPagerButtonType'); - } - - /** - * @return string text for the next page button. Defaults to '>'. - */ - public function getNextPageText() - { - return $this->_nextText===null?'>':$this->_nextText; - } - - /** - * @param string text for the next page button. - */ - public function setNextPageText($value) - { - $this->_nextText=$value; - } - - /** - * @return string text for the previous page button. Defaults to '<'. - */ - public function getPrevPageText() - { - return $this->_prevText===null?'<':$this->_prevText; - } - - /** - * @param string text for the previous page button. - */ - public function setPrevPageText($value) - { - $this->_prevText=$value; - } - - /** - * @return string text for the first page button. Defaults to '<<'. - */ - public function getFirstPageText() - { - return $this->_firstText===null?'<<':$this->_firstText; - } - - /** - * @param string text for the first page button. - */ - public function setFirstPageText($value) - { - $this->_firstText=$value; - } - - /** - * @return string text for the last page button. Defaults to '>>'. - */ - public function getLastPageText() - { - return $this->_lastText===null?'>>':$this->_lastText; - } - - /** - * @param string text for the last page button. - */ - public function setLastPageText($value) - { - $this->_lastText=$value; - } - - /** - * @return integer maximum number of pager buttons to be displayed. Defaults to 10. - */ - public function getPageButtonCount() - { - return $this->_buttonCount===null?10:$this->_buttonCount; - } - - /** - * @param integer maximum number of pager buttons to be displayed - * @throws TInvalidDataValueException if the value is less than 1. - */ - public function setPageButtonCount($value) - { - if(($value=TPropertyValue::ensureInteger($value))<1) - throw new TInvalidDataValueException('datagridpagerstyle_pagebuttoncount_invalid'); - $this->_buttonCount=$value; - } - - /** - * @return TDataGridPagerPosition where the pager is to be displayed. Defaults to TDataGridPagerPosition::Bottom. - */ - public function getPosition() - { - return $this->_position===null?TDataGridPagerPosition::Bottom:$this->_position; - } - - /** - * @param TDataGridPagerPosition where the pager is to be displayed. - */ - public function setPosition($value) - { - $this->_position=TPropertyValue::ensureEnum($value,'TDataGridPagerPosition'); - } - - /** - * @return boolean whether the pager is visible. Defaults to true. - */ - public function getVisible() - { - return $this->_visible===null?true:$this->_visible; - } - - /** - * @param boolean whether the pager is visible. - */ - public function setVisible($value) - { - $this->_visible=TPropertyValue::ensureBoolean($value); - } - - /** - * Resets the style to the original empty state. - */ - public function reset() - { - parent::reset(); - $this->_visible=null; - $this->_position=null; - $this->_buttonCount=null; - $this->_prevText=null; - $this->_nextText=null; - $this->_mode=null; - $this->_buttonType=null; - } - - /** - * Copies the fields in a new style to this style. - * If a style field is set in the new style, the corresponding field - * in this style will be overwritten. - * @param TStyle the new style - */ - public function copyFrom($style) - { - parent::copyFrom($style); - if($style instanceof TDataGridPagerStyle) - { - if($style->_visible!==null) - $this->_visible=$style->_visible; - if($style->_position!==null) - $this->_position=$style->_position; - if($style->_buttonCount!==null) - $this->_buttonCount=$style->_buttonCount; - if($style->_prevText!==null) - $this->_prevText=$style->_prevText; - if($style->_nextText!==null) - $this->_nextText=$style->_nextText; - if($style->_mode!==null) - $this->_mode=$style->_mode; - if($style->_buttonType!==null) - $this->_buttonType=$style->_buttonType; - } - } - - /** - * Merges the style with a new one. - * If a style field is not set in this style, it will be overwritten by - * the new one. - * @param TStyle the new style - */ - public function mergeWith($style) - { - parent::mergeWith($style); - if($style instanceof TDataGridPagerStyle) - { - if($this->_visible===null) - $this->_visible=$style->_visible; - if($this->_position===null) - $this->_position=$style->_position; - if($this->_buttonCount===null) - $this->_buttonCount=$style->_buttonCount; - if($this->_prevText===null) - $this->_prevText=$style->_prevText; - if($this->_nextText===null) - $this->_nextText=$style->_nextText; - if($this->_mode===null) - $this->_mode=$style->_mode; - if($this->_buttonType===null) - $this->_buttonType=$style->_buttonType; - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGrid'); + +/** + * TDataGridPagerStyle class. + * + * TDataGridPagerStyle specifies the styles available for a datagrid pager. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPagerStyle extends TPanelStyle +{ + private $_mode=null; + private $_nextText=null; + private $_prevText=null; + private $_firstText=null; + private $_lastText=null; + private $_buttonCount=null; + private $_position=null; + private $_visible=null; + private $_buttonType=null; + + /** + * @return TDataGridPagerMode pager mode. Defaults to TDataGridPagerMode::NextPrev. + */ + public function getMode() + { + return $this->_mode===null?TDataGridPagerMode::NextPrev : $this->_mode; + } + + /** + * @param TDataGridPagerMode pager mode. + */ + public function setMode($value) + { + $this->_mode=TPropertyValue::ensureEnum($value,'TDataGridPagerMode'); + } + + /** + * @return TDataGridPagerButtonType the type of command button. Defaults to TDataGridPagerButtonType::LinkButton. + */ + public function getButtonType() + { + return $this->_buttonType===null?TDataGridPagerButtonType::LinkButton:$this->_buttonType; + } + + /** + * @param TDataGridPagerButtonType the type of command button + */ + public function setButtonType($value) + { + $this->_buttonType=TPropertyValue::ensureEnum($value,'TDataGridPagerButtonType'); + } + + /** + * @return string text for the next page button. Defaults to '>'. + */ + public function getNextPageText() + { + return $this->_nextText===null?'>':$this->_nextText; + } + + /** + * @param string text for the next page button. + */ + public function setNextPageText($value) + { + $this->_nextText=$value; + } + + /** + * @return string text for the previous page button. Defaults to '<'. + */ + public function getPrevPageText() + { + return $this->_prevText===null?'<':$this->_prevText; + } + + /** + * @param string text for the previous page button. + */ + public function setPrevPageText($value) + { + $this->_prevText=$value; + } + + /** + * @return string text for the first page button. Defaults to '<<'. + */ + public function getFirstPageText() + { + return $this->_firstText===null?'<<':$this->_firstText; + } + + /** + * @param string text for the first page button. + */ + public function setFirstPageText($value) + { + $this->_firstText=$value; + } + + /** + * @return string text for the last page button. Defaults to '>>'. + */ + public function getLastPageText() + { + return $this->_lastText===null?'>>':$this->_lastText; + } + + /** + * @param string text for the last page button. + */ + public function setLastPageText($value) + { + $this->_lastText=$value; + } + + /** + * @return integer maximum number of pager buttons to be displayed. Defaults to 10. + */ + public function getPageButtonCount() + { + return $this->_buttonCount===null?10:$this->_buttonCount; + } + + /** + * @param integer maximum number of pager buttons to be displayed + * @throws TInvalidDataValueException if the value is less than 1. + */ + public function setPageButtonCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('datagridpagerstyle_pagebuttoncount_invalid'); + $this->_buttonCount=$value; + } + + /** + * @return TDataGridPagerPosition where the pager is to be displayed. Defaults to TDataGridPagerPosition::Bottom. + */ + public function getPosition() + { + return $this->_position===null?TDataGridPagerPosition::Bottom:$this->_position; + } + + /** + * @param TDataGridPagerPosition where the pager is to be displayed. + */ + public function setPosition($value) + { + $this->_position=TPropertyValue::ensureEnum($value,'TDataGridPagerPosition'); + } + + /** + * @return boolean whether the pager is visible. Defaults to true. + */ + public function getVisible() + { + return $this->_visible===null?true:$this->_visible; + } + + /** + * @param boolean whether the pager is visible. + */ + public function setVisible($value) + { + $this->_visible=TPropertyValue::ensureBoolean($value); + } + + /** + * Resets the style to the original empty state. + */ + public function reset() + { + parent::reset(); + $this->_visible=null; + $this->_position=null; + $this->_buttonCount=null; + $this->_prevText=null; + $this->_nextText=null; + $this->_mode=null; + $this->_buttonType=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TDataGridPagerStyle) + { + if($style->_visible!==null) + $this->_visible=$style->_visible; + if($style->_position!==null) + $this->_position=$style->_position; + if($style->_buttonCount!==null) + $this->_buttonCount=$style->_buttonCount; + if($style->_prevText!==null) + $this->_prevText=$style->_prevText; + if($style->_nextText!==null) + $this->_nextText=$style->_nextText; + if($style->_mode!==null) + $this->_mode=$style->_mode; + if($style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TDataGridPagerStyle) + { + if($this->_visible===null) + $this->_visible=$style->_visible; + if($this->_position===null) + $this->_position=$style->_position; + if($this->_buttonCount===null) + $this->_buttonCount=$style->_buttonCount; + if($this->_prevText===null) + $this->_prevText=$style->_prevText; + if($this->_nextText===null) + $this->_nextText=$style->_nextText; + if($this->_mode===null) + $this->_mode=$style->_mode; + if($this->_buttonType===null) + $this->_buttonType=$style->_buttonType; + } + } +} + diff --git a/framework/Web/UI/WebControls/TDataList.php b/framework/Web/UI/WebControls/TDataList.php index 43980c0c..49e9c749 100644 --- a/framework/Web/UI/WebControls/TDataList.php +++ b/framework/Web/UI/WebControls/TDataList.php @@ -1,1766 +1,1766 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TBaseDataList class - */ -Prado::using('System.Web.UI.WebControls.TBaseDataList'); -/** - * Includes TRepeatInfo class - */ -Prado::using('System.Web.UI.WebControls.TRepeatInfo'); - -/** - * TDataList class - * - * TDataList represents a data bound and updatable list control. - * - * Like {@link TRepeater}, TDataList displays its content repeatedly based on - * the data fetched from {@link setDataSource DataSource}. - * The repeated contents in TDataList are called items, which are controls and - * can be accessed through {@link getItems Items}. When {@link dataBind()} is - * invoked, TDataList creates an item for each row of data and binds the data - * row to the item. Optionally, a TDataList can have a header, a footer and/or - * separators between items. - * - * TDataList differs from {@link TRepeater} in that it supports tiling the items - * in different manners and it maintains status of items to handle data update. - * - * The layout of the repeated contents are specified by inline templates. - * TDataList items, header, footer, etc. are being instantiated with the corresponding - * templates when data is being bound to the repeater. - * - * Since v3.1.0, the layout can also be by renderers. A renderer is a control class - * that can be instantiated as datalist items, header, etc. A renderer can thus be viewed - * as an external template (in fact, it can also be non-templated controls). - * - * A renderer can be any control class. - * - If the class implements {@link IDataRenderer}, the Data - * property will be set as the data row during databinding. Many PRADO controls - * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. - * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set - * as the zero-based index of the item in the datalist item collection, and - * the ItemType property as the item's type (such as TListItemType::Item). - * {@link TDataListItemRenderer} may be used as the convenient base class which - * already implements {@link IDataItemRenderer}. - * - * The following properties are used to specify different types of template and renderer - * for a datalist: - * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: - * for each repeated row of data - * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: - * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} - * will be used instead. - * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: - * for the datalist header. - * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: - * for the datalist footer. - * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: - * for content to be displayed between items. - * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: - * used when data bound to the datalist is empty. - * - {@link setEditItemTemplate EditItemTemplate}, {@link setEditItemRenderer EditItemRenderer}: - * for the row being editted. - * - {@link setSelectedItemTemplate SelectedItemTemplate}, {@link setSelectedItemRenderer SelectedItemRenderer}: - * for the row being selected. - * - * If a content type is defined with both a template and a renderer, the latter takes precedence. - * - * When {@link dataBind()} is being called, TDataList undergoes the following lifecycles for each row of data: - * - create item based on templates or renderers - * - set the row of data to the item - * - raise {@link onItemCreated OnItemCreated}: - * - add the item as a child control - * - call dataBind() of the item - * - raise {@link onItemDataBound OnItemDataBound}: - * - * TDataList raises an {@link onItemCommand OnItemCommand} whenever a button control - * within some datalist item raises a OnCommand event. Therefore, - * you can handle all sorts of OnCommand event in a central place by - * writing an event handler for {@link onItemCommand OnItemCommand}. - * - * An additional event is raised if the OnCommand event has one of the following - * command names: - * - edit: user wants to edit an item. OnEditCommand event will be raised. - * - update: user wants to save the change to an item. OnUpdateCommand event will be raised. - * - select: user selects an item. OnSelectedIndexChanged event will be raised. - * - delete: user deletes an item. OnDeleteCommand event will be raised. - * - cancel: user cancels previously editting action. OnCancelCommand event will be raised. - * - * TDataList provides a few properties to support tiling the items. - * The number of columns used to display the data items is specified via - * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} - * governs the order of the items being rendered. - * The layout of the data items in the list is specified via {@link setRepeatLayout RepeatLayout}, - * which can take one of the following values: - * - Table (default): items are organized using HTML table and cells. - * When using this layout, one can set {@link setCellPadding CellPadding} and - * {@link setCellSpacing CellSpacing} to adjust the cellpadding and cellpadding - * of the table, and {@link setCaption Caption} and {@link setCaptionAlign CaptionAlign} - * to add a table caption with the specified alignment. - * - Flow: items are organized using HTML spans and breaks. - * - Raw: TDataList does not generate any HTML tags to do the tiling. - * - * Items in TDataList can be in one of the three status: normal browsing, - * being editted and being selected. To change the status of a particular - * item, set {@link setSelectedItemIndex SelectedItemIndex} or - * {@link setEditItemIndex EditItemIndex}. The former will change - * the indicated item to selected mode, which will cause the item to - * use {@link setSelectedItemTemplate SelectedItemTemplate} or - * {@link setSelectedItemRenderer SelectedItemRenderer} for presentation. - * The latter will change the indicated item to edit mode and to use corresponding - * template or renderer. - * Note, if an item is in edit mode, then selecting this item will have no effect. - * - * Different styles may be applied to items in different status. The style - * application is performed in a hierarchical way: Style in higher hierarchy - * will inherit from styles in lower hierarchy. - * Starting from the lowest hierarchy, the item styles include - * - item's own style - * - {@link getItemStyle ItemStyle} - * - {@link getAlternatingItemStyle AlternatingItemStyle} - * - {@link getSelectedItemStyle SelectedItemStyle} - * - {@link getEditItemStyle EditItemStyle}. - * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, - * {@link getEditItemStyle EditItemStyle} will also have red background color - * unless it is set to a different value explicitly. - * - * When a page containing a datalist is post back, the datalist will restore automatically - * all its contents, including items, header, footer and separators. - * However, the data row associated with each item will not be recovered and become null. - * To access the data, use one of the following ways: - * - Use {@link getDataKeys DataKeys} to obtain the data key associated with - * the specified datalist item and use the key to fetch the corresponding data - * from some persistent storage such as DB. - * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. - * Be aware though, if the size of your dataset is big, your page size will become big. Some - * complex data may also have serializing problem if saved in viewstate. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUser -{ - /** - * Command name that TDataList understands. They are case-insensitive. - */ - const CMD_SELECT='Select'; - const CMD_EDIT='Edit'; - const CMD_UPDATE='Update'; - const CMD_DELETE='Delete'; - const CMD_CANCEL='Cancel'; - - /** - * @var TDataListItemCollection item list - */ - private $_items=null; - /** - * @var Itemplate various item templates - */ - private $_itemTemplate=null; - private $_emptyTemplate=null; - private $_alternatingItemTemplate=null; - private $_selectedItemTemplate=null; - private $_editItemTemplate=null; - private $_headerTemplate=null; - private $_footerTemplate=null; - private $_separatorTemplate=null; - /** - * @var TControl header item - */ - private $_header=null; - /** - * @var TControl footer item - */ - private $_footer=null; - - /** - * @return TDataListItemCollection item list - */ - public function getItems() - { - if(!$this->_items) - $this->_items=new TDataListItemCollection; - return $this->_items; - } - - /** - * @return integer number of items - */ - public function getItemCount() - { - return $this->_items?$this->_items->getCount():0; - } - - /** - * @return string the class name for datalist items. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getItemRenderer() - { - return $this->getViewState('ItemRenderer',''); - } - - /** - * Sets the item renderer class. - * - * If not empty, the class will be used to instantiate as datalist items. - * This property takes precedence over {@link getItemTemplate ItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setItemTemplate - * @since 3.1.0 - */ - public function setItemRenderer($value) - { - $this->setViewState('ItemRenderer',$value,''); - } - - /** - * @return string the class name for alternative datalist items. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getAlternatingItemRenderer() - { - return $this->getViewState('AlternatingItemRenderer',''); - } - - /** - * Sets the alternative item renderer class. - * - * If not empty, the class will be used to instantiate as alternative datalist items. - * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setAlternatingItemTemplate - * @since 3.1.0 - */ - public function setAlternatingItemRenderer($value) - { - $this->setViewState('AlternatingItemRenderer',$value,''); - } - - /** - * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getEditItemRenderer() - { - return $this->getViewState('EditItemRenderer',''); - } - - /** - * Sets the renderer class for the datalist item being editted. - * - * If not empty, the class will be used to instantiate as the datalist item. - * This property takes precedence over {@link getEditItemTemplate EditItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setEditItemTemplate - * @since 3.1.0 - */ - public function setEditItemRenderer($value) - { - $this->setViewState('EditItemRenderer',$value,''); - } - - /** - * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getSelectedItemRenderer() - { - return $this->getViewState('SelectedItemRenderer',''); - } - - /** - * Sets the renderer class for the datalist item being selected. - * - * If not empty, the class will be used to instantiate as the datalist item. - * This property takes precedence over {@link getSelectedItemTemplate SelectedItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setSelectedItemTemplate - * @since 3.1.0 - */ - public function setSelectedItemRenderer($value) - { - $this->setViewState('SelectedItemRenderer',$value,''); - } - - /** - * @return string the class name for datalist item separators. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getSeparatorRenderer() - { - return $this->getViewState('SeparatorRenderer',''); - } - - /** - * Sets the datalist item separator renderer class. - * - * If not empty, the class will be used to instantiate as datalist item separators. - * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setSeparatorTemplate - * @since 3.1.0 - */ - public function setSeparatorRenderer($value) - { - $this->setViewState('SeparatorRenderer',$value,''); - } - - /** - * @return string the class name for datalist header item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getHeaderRenderer() - { - return $this->getViewState('HeaderRenderer',''); - } - - /** - * Sets the datalist header renderer class. - * - * If not empty, the class will be used to instantiate as datalist header item. - * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setHeaderTemplate - * @since 3.1.0 - */ - public function setHeaderRenderer($value) - { - $this->setViewState('HeaderRenderer',$value,''); - } - - /** - * @return string the class name for datalist footer item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getFooterRenderer() - { - return $this->getViewState('FooterRenderer',''); - } - - /** - * Sets the datalist footer renderer class. - * - * If not empty, the class will be used to instantiate as datalist footer item. - * This property takes precedence over {@link getFooterTemplate FooterTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setFooterTemplate - * @since 3.1.0 - */ - public function setFooterRenderer($value) - { - $this->setViewState('FooterRenderer',$value,''); - } - - /** - * @return string the class name for empty datalist item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getEmptyRenderer() - { - return $this->getViewState('EmptyRenderer',''); - } - - /** - * Sets the datalist empty renderer class. - * - * The empty renderer is created as the child of the datalist - * if data bound to the datalist is empty. - * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setEmptyTemplate - * @since 3.1.0 - */ - public function setEmptyRenderer($value) - { - $this->setViewState('EmptyRenderer',$value,''); - } - - /** - * @return ITemplate the template for item - */ - public function getItemTemplate() - { - return $this->_itemTemplate; - } - - /** - * @param ITemplate the template for item - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_itemTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','ItemTemplate'); - } - - /** - * @return TTableItemStyle the style for item - */ - public function getItemStyle() - { - if(($style=$this->getViewState('ItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('ItemStyle',$style,null); - } - return $style; - } - - /** - * @return ITemplate the template for each alternating item - */ - public function getAlternatingItemTemplate() - { - return $this->_alternatingItemTemplate; - } - - /** - * @param ITemplate the template for each alternating item - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setAlternatingItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_alternatingItemTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','AlternatingItemType'); - } - - /** - * @return TTableItemStyle the style for each alternating item - */ - public function getAlternatingItemStyle() - { - if(($style=$this->getViewState('AlternatingItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('AlternatingItemStyle',$style,null); - } - return $style; - } - - /** - * @return ITemplate the selected item template - */ - public function getSelectedItemTemplate() - { - return $this->_selectedItemTemplate; - } - - /** - * @param ITemplate the selected item template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setSelectedItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_selectedItemTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','SelectedItemTemplate'); - } - - /** - * @return TTableItemStyle the style for selected item - */ - public function getSelectedItemStyle() - { - if(($style=$this->getViewState('SelectedItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('SelectedItemStyle',$style,null); - } - return $style; - } - - /** - * @return ITemplate the edit item template - */ - public function getEditItemTemplate() - { - return $this->_editItemTemplate; - } - - /** - * @param ITemplate the edit item template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setEditItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_editItemTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','EditItemTemplate'); - } - - /** - * @return TTableItemStyle the style for edit item - */ - public function getEditItemStyle() - { - if(($style=$this->getViewState('EditItemStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('EditItemStyle',$style,null); - } - return $style; - } - - /** - * @return ITemplate the header template - */ - public function getHeaderTemplate() - { - return $this->_headerTemplate; - } - - /** - * @param ITemplate the header template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setHeaderTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_headerTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','HeaderTemplate'); - } - - /** - * @return TTableItemStyle the style for header - */ - public function getHeaderStyle() - { - if(($style=$this->getViewState('HeaderStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('HeaderStyle',$style,null); - } - return $style; - } - - /** - * @return TControl the header item - */ - public function getHeader() - { - return $this->_header; - } - - /** - * @return ITemplate the footer template - */ - public function getFooterTemplate() - { - return $this->_footerTemplate; - } - - /** - * @param ITemplate the footer template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setFooterTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_footerTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','FooterTemplate'); - } - - /** - * @return TTableItemStyle the style for footer - */ - public function getFooterStyle() - { - if(($style=$this->getViewState('FooterStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('FooterStyle',$style,null); - } - return $style; - } - - /** - * @return TControl the footer item - */ - public function getFooter() - { - return $this->_footer; - } - - /** - * @return ITemplate the template applied when no data is bound to the datalist - */ - public function getEmptyTemplate() - { - return $this->_emptyTemplate; - } - - /** - * @param ITemplate the template applied when no data is bound to the datalist - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setEmptyTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_emptyTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','EmptyTemplate'); - } - - /** - * @return ITemplate the separator template - */ - public function getSeparatorTemplate() - { - return $this->_separatorTemplate; - } - - /** - * @param ITemplate the separator template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setSeparatorTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_separatorTemplate=$value; - else - throw new TInvalidDataTypeException('datalist_template_required','SeparatorTemplate'); - } - - /** - * @return TTableItemStyle the style for separator - */ - public function getSeparatorStyle() - { - if(($style=$this->getViewState('SeparatorStyle',null))===null) - { - $style=new TTableItemStyle; - $this->setViewState('SeparatorStyle',$style,null); - } - return $style; - } - - /** - * @return integer the zero-based index of the selected item in {@link getItems Items}. - * A value -1 means no item selected. - */ - public function getSelectedItemIndex() - { - return $this->getViewState('SelectedItemIndex',-1); - } - - /** - * Selects an item by its index in {@link getItems Items}. - * Previously selected item will be un-selected. - * If the item to be selected is already in edit mode, it will remain in edit mode. - * If the index is less than 0, any existing selection will be cleared up. - * @param integer the selected item index - */ - public function setSelectedItemIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - if(($current=$this->getSelectedItemIndex())!==$value) - { - $this->setViewState('SelectedItemIndex',$value,-1); - $items=$this->getItems(); - $itemCount=$items->getCount(); - if($current>=0 && $current<$itemCount) - { - $item=$items->itemAt($current); - if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) - $item->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); - } - if($value>=0 && $value<$itemCount) - { - $item=$items->itemAt($value); - if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) - $item->setItemType(TListItemType::SelectedItem); - } - } - } - - /** - * @return TControl the selected item, null if no item is selected. - */ - public function getSelectedItem() - { - $index=$this->getSelectedItemIndex(); - $items=$this->getItems(); - if($index>=0 && $index<$items->getCount()) - return $items->itemAt($index); - else - return null; - } - - /** - * @return mixed the key value of the currently selected item - * @throws TInvalidOperationException if {@link getDataKeyField DataKeyField} is empty. - */ - public function getSelectedDataKey() - { - if($this->getDataKeyField()==='') - throw new TInvalidOperationException('datalist_datakeyfield_required'); - $index=$this->getSelectedItemIndex(); - $dataKeys=$this->getDataKeys(); - if($index>=0 && $index<$dataKeys->getCount()) - return $dataKeys->itemAt($index); - else - return null; - } - - /** - * @return integer the zero-based index of the edit item in {@link getItems Items}. - * A value -1 means no item is in edit mode. - */ - public function getEditItemIndex() - { - return $this->getViewState('EditItemIndex',-1); - } - - /** - * Edits an item by its index in {@link getItems Items}. - * Previously editting item will change to normal item state. - * If the index is less than 0, any existing edit item will be cleared up. - * @param integer the edit item index - */ - public function setEditItemIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - if(($current=$this->getEditItemIndex())!==$value) - { - $this->setViewState('EditItemIndex',$value,-1); - $items=$this->getItems(); - $itemCount=$items->getCount(); - if($current>=0 && $current<$itemCount) - $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); - if($value>=0 && $value<$itemCount) - $items->itemAt($value)->setItemType(TListItemType::EditItem); - } - } - - /** - * @return TControl the edit item - */ - public function getEditItem() - { - $index=$this->getEditItemIndex(); - $items=$this->getItems(); - if($index>=0 && $index<$items->getCount()) - return $items->itemAt($index); - else - return null; - } - - /** - * @return boolean whether the header should be shown. Defaults to true. - */ - public function getShowHeader() - { - return $this->getViewState('ShowHeader',true); - } - - /** - * @param boolean whether to show header - */ - public function setShowHeader($value) - { - $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the footer should be shown. Defaults to true. - */ - public function getShowFooter() - { - return $this->getViewState('ShowFooter',true); - } - - /** - * @param boolean whether to show footer - */ - public function setShowFooter($value) - { - $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return TRepeatInfo repeat information (primarily used by control developers) - */ - protected function getRepeatInfo() - { - if(($repeatInfo=$this->getViewState('RepeatInfo',null))===null) - { - $repeatInfo=new TRepeatInfo; - $this->setViewState('RepeatInfo',$repeatInfo,null); - } - return $repeatInfo; - } - - /** - * @return string caption of the table layout - */ - public function getCaption() - { - return $this->getRepeatInfo()->getCaption(); - } - - /** - * @param string caption of the table layout - */ - public function setCaption($value) - { - $this->getRepeatInfo()->setCaption($value); - } - - /** - * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. - */ - public function getCaptionAlign() - { - return $this->getRepeatInfo()->getCaptionAlign(); - } - - /** - * @return TTableCaptionAlign alignment of the caption of the table layout. - */ - public function setCaptionAlign($value) - { - $this->getRepeatInfo()->setCaptionAlign($value); - } - - /** - * @return integer the number of columns that the list should be displayed with. Defaults to 0 meaning not set. - */ - public function getRepeatColumns() - { - return $this->getRepeatInfo()->getRepeatColumns(); - } - - /** - * @param integer the number of columns that the list should be displayed with. - */ - public function setRepeatColumns($value) - { - $this->getRepeatInfo()->setRepeatColumns($value); - } - - /** - * @return TRepeatDirection the direction of traversing the list, defaults to TRepeatDirection::Vertical - */ - public function getRepeatDirection() - { - return $this->getRepeatInfo()->getRepeatDirection(); - } - - /** - * @param TRepeatDirection the direction of traversing the list - */ - public function setRepeatDirection($value) - { - $this->getRepeatInfo()->setRepeatDirection($value); - } - - /** - * @return TRepeatLayout how the list should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. - */ - public function getRepeatLayout() - { - return $this->getRepeatInfo()->getRepeatLayout(); - } - - /** - * @param TRepeatLayout how the list should be displayed, using table or using line breaks - */ - public function setRepeatLayout($value) - { - $this->getRepeatInfo()->setRepeatLayout($value); - } - - /** - * This method overrides parent's implementation to handle - * {@link onItemCommand OnItemCommand} event which is bubbled from - * datalist items and their child controls. - * If the event parameter is {@link TDataListCommandEventParameter} and - * the command name is a recognized one, which includes 'select', 'edit', - * 'delete', 'update', and 'cancel' (case-insensitive), then a - * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). - * This method should only be used by control developers. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TDataListCommandEventParameter) - { - $this->onItemCommand($param); - $command=$param->getCommandName(); - if(strcasecmp($command,self::CMD_SELECT)===0) - { - if(($item=$param->getItem()) instanceof IItemDataRenderer) - $this->setSelectedItemIndex($item->getItemIndex()); - $this->onSelectedIndexChanged($param); - return true; - } - else if(strcasecmp($command,self::CMD_EDIT)===0) - { - $this->onEditCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_DELETE)===0) - { - $this->onDeleteCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_UPDATE)===0) - { - $this->onUpdateCommand($param); - return true; - } - else if(strcasecmp($command,self::CMD_CANCEL)===0) - { - $this->onCancelCommand($param); - return true; - } - } - return false; - } - - - /** - * Raises OnItemCreated event. - * This method is invoked after a data list item is created and instantiated with - * template, but before added to the page hierarchy. - * The datalist item control responsible for the event - * can be determined from the event parameter. - * If you override this method, be sure to call parent's implementation - * so that event handlers have chance to respond to the event. - * @param TDataListItemEventParameter event parameter - */ - public function onItemCreated($param) - { - $this->raiseEvent('OnItemCreated',$this,$param); - } - - /** - * Raises OnItemDataBound event. - * This method is invoked right after an item is data bound. - * The datalist item control responsible for the event - * can be determined from the event parameter. - * If you override this method, be sure to call parent's implementation - * so that event handlers have chance to respond to the event. - * @param TDataListItemEventParameter event parameter - */ - public function onItemDataBound($param) - { - $this->raiseEvent('OnItemDataBound',$this,$param); - } - - /** - * Raises OnItemCommand event. - * This method is invoked when a child control of the data list - * raises an OnCommand event. - * @param TDataListCommandEventParameter event parameter - */ - public function onItemCommand($param) - { - $this->raiseEvent('OnItemCommand',$this,$param); - } - - /** - * Raises OnEditCommand event. - * This method is invoked when a child control of the data list - * raises an OnCommand event and the command name is 'edit' (case-insensitive). - * @param TDataListCommandEventParameter event parameter - */ - public function onEditCommand($param) - { - $this->raiseEvent('OnEditCommand',$this,$param); - } - - /** - * Raises OnDeleteCommand event. - * This method is invoked when a child control of the data list - * raises an OnCommand event and the command name is 'delete' (case-insensitive). - * @param TDataListCommandEventParameter event parameter - */ - public function onDeleteCommand($param) - { - $this->raiseEvent('OnDeleteCommand',$this,$param); - } - - /** - * Raises OnUpdateCommand event. - * This method is invoked when a child control of the data list - * raises an OnCommand event and the command name is 'update' (case-insensitive). - * @param TDataListCommandEventParameter event parameter - */ - public function onUpdateCommand($param) - { - $this->raiseEvent('OnUpdateCommand',$this,$param); - } - - /** - * Raises OnCancelCommand event. - * This method is invoked when a child control of the data list - * raises an OnCommand event and the command name is 'cancel' (case-insensitive). - * @param TDataListCommandEventParameter event parameter - */ - public function onCancelCommand($param) - { - $this->raiseEvent('OnCancelCommand',$this,$param); - } - - /** - * Returns a value indicating whether this control contains header item. - * This method is required by {@link IRepeatInfoUser} interface. - * @return boolean whether the datalist has header - */ - public function getHasHeader() - { - return ($this->getShowHeader() && ($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='')); - } - - /** - * Returns a value indicating whether this control contains footer item. - * This method is required by {@link IRepeatInfoUser} interface. - * @return boolean whether the datalist has footer - */ - public function getHasFooter() - { - return ($this->getShowFooter() && ($this->_footerTemplate!==null || $this->getFooterRenderer()!=='')); - } - - /** - * Returns a value indicating whether this control contains separator items. - * This method is required by {@link IRepeatInfoUser} interface. - * @return boolean always false. - */ - public function getHasSeparators() - { - return $this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; - } - - /** - * Returns a style used for rendering items. - * This method is required by {@link IRepeatInfoUser} interface. - * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) - * @param integer index of the item being rendered - * @return TStyle item style - */ - public function generateItemStyle($itemType,$index) - { - if(($item=$this->getItem($itemType,$index))!==null && ($item instanceof IStyleable) && $item->getHasStyle()) - { - $style=$item->getStyle(); - $item->clearStyle(); - return $style; - } - else - return null; - } - - /** - * Renders an item in the list. - * This method is required by {@link IRepeatInfoUser} interface. - * @param THtmlWriter writer for rendering purpose - * @param TRepeatInfo repeat information - * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) - * @param integer zero-based index of the item in the item list - */ - public function renderItem($writer,$repeatInfo,$itemType,$index) - { - $item=$this->getItem($itemType,$index); - if($repeatInfo->getRepeatLayout()===TRepeatLayout::Raw && get_class($item)==='TDataListItem') - $item->setTagName('div'); - $item->renderControl($writer); - } - - /** - * @param TListItemType item type - * @param integer item index - * @return TControl data list item with the specified item type and index - */ - private function getItem($itemType,$index) - { - switch($itemType) - { - case TListItemType::Item: - case TListItemType::AlternatingItem: - case TListItemType::SelectedItem: - case TListItemType::EditItem: - return $this->getItems()->itemAt($index); - case TListItemType::Header: - return $this->getControls()->itemAt(0); - case TListItemType::Footer: - return $this->getControls()->itemAt($this->getControls()->getCount()-1); - case TListItemType::Separator: - $i=$index+$index+1; - if($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='') - $i++; - return $this->getControls()->itemAt($i); - } - return null; - } - - /** - * Creates a datalist item. - * This method invokes {@link createItem} to create a new datalist item. - * @param integer zero-based item index. - * @param TListItemType item type - * @return TControl the created item, null if item is not created - */ - private function createItemInternal($itemIndex,$itemType) - { - if(($item=$this->createItem($itemIndex,$itemType))!==null) - { - $param=new TDataListItemEventParameter($item); - $this->onItemCreated($param); - $this->getControls()->add($item); - return $item; - } - else - return null; - } - - /** - * Creates a datalist item and performs databinding. - * This method invokes {@link createItem} to create a new datalist item. - * @param integer zero-based item index. - * @param TListItemType item type - * @param mixed data to be associated with the item - * @return TControl the created item, null if item is not created - */ - private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) - { - if(($item=$this->createItem($itemIndex,$itemType))!==null) - { - $param=new TDataListItemEventParameter($item); - if($item instanceof IDataRenderer) - $item->setData($dataItem); - $this->onItemCreated($param); - $this->getControls()->add($item); - $item->dataBind(); - $this->onItemDataBound($param); - return $item; - } - else - return null; - } - - private function getAlternatingItemDisplay() - { - if(($classPath=$this->getAlternatingItemRenderer())==='' && $this->_alternatingItemTemplate===null) - return array($this->getItemRenderer(),$this->_itemTemplate); - else - return array($classPath,$this->_alternatingItemTemplate); - } - - private function getSelectedItemDisplay($itemIndex) - { - if(($classPath=$this->getSelectedItemRenderer())==='' && $this->_selectedItemTemplate===null) - { - if($itemIndex%2===0) - return array($this->getItemRenderer(),$this->_itemTemplate); - else - return $this->getAlternatingItemDisplay(); - } - else - return array($classPath,$this->_selectedItemTemplate); - } - - private function getEditItemDisplay($itemIndex) - { - if(($classPath=$this->getEditItemRenderer())==='' && $this->_editItemTemplate===null) - return $this->getSelectedItemDisplay($itemIndex); - else - return array($classPath,$this->_editItemTemplate); - } - - /** - * Creates a datalist item instance based on the item type and index. - * @param integer zero-based item index - * @param TListItemType item type - * @return TControl created datalist item - */ - protected function createItem($itemIndex,$itemType) - { - $template=null; - $classPath=null; - switch($itemType) - { - case TListItemType::Item : - $classPath=$this->getItemRenderer(); - $template=$this->_itemTemplate; - break; - case TListItemType::AlternatingItem : - list($classPath,$template)=$this->getAlternatingItemDisplay(); - break; - case TListItemType::SelectedItem: - list($classPath,$template)=$this->getSelectedItemDisplay($itemIndex); - break; - case TListItemType::EditItem: - list($classPath,$template)=$this->getEditItemDisplay($itemIndex); - break; - case TListItemType::Header : - $classPath=$this->getHeaderRenderer(); - $template=$this->_headerTemplate; - break; - case TListItemType::Footer : - $classPath=$this->getFooterRenderer(); - $template=$this->_footerTemplate; - break; - case TListItemType::Separator : - $classPath=$this->getSeparatorRenderer(); - $template=$this->_separatorTemplate; - break; - default: - throw new TInvalidDataValueException('datalist_itemtype_unknown',$itemType); - } - if($classPath!=='') - { - $item=Prado::createComponent($classPath); - if($item instanceof IItemDataRenderer) - { - $item->setItemIndex($itemIndex); - $item->setItemType($itemType); - } - } - else if($template!==null) - { - $item=new TDataListItem; - $item->setItemIndex($itemIndex); - $item->setItemType($itemType); - $template->instantiateIn($item); - } - else - $item=null; - - return $item; - } - - /** - * Creates empty datalist content. - */ - protected function createEmptyContent() - { - if(($classPath=$this->getEmptyRenderer())!=='') - $this->getControls()->add(Prado::createComponent($classPath)); - else if($this->_emptyTemplate!==null) - $this->_emptyTemplate->instantiateIn($this); - } - - /** - * Applies styles to items, header, footer and separators. - * Item styles are applied in a hierarchical way. Style in higher hierarchy - * will inherit from styles in lower hierarchy. - * Starting from the lowest hierarchy, the item styles include - * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, - * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. - * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, - * {@link getEditItemStyle EditItemStyle} will also have red background color - * unless it is set to a different value explicitly. - */ - protected function applyItemStyles() - { - $itemStyle=$this->getViewState('ItemStyle',null); - - $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); - if($itemStyle!==null) - { - if($alternatingItemStyle===null) - $alternatingItemStyle=$itemStyle; - else - $alternatingItemStyle->mergeWith($itemStyle); - } - - $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); - - $editItemStyle=$this->getViewState('EditItemStyle',null); - if($selectedItemStyle!==null) - { - if($editItemStyle===null) - $editItemStyle=$selectedItemStyle; - else - $editItemStyle->mergeWith($selectedItemStyle); - } - - // apply header style if any - if($this->_header!==null && $this->_header instanceof IStyleable) - { - if($headerStyle=$this->getViewState('HeaderStyle',null)) - $this->_header->getStyle()->mergeWith($headerStyle); - } - - // apply footer style if any - if($this->_footer!==null && $this->_footer instanceof IStyleable) - { - if($footerStyle=$this->getViewState('FooterStyle',null)) - $this->_footer->getStyle()->mergeWith($footerStyle); - } - - $selectedIndex=$this->getSelectedItemIndex(); - $editIndex=$this->getEditItemIndex(); - - // apply item styles if any - foreach($this->getItems() as $index=>$item) - { - if($index===$editIndex) - $style=$editItemStyle; - else if($index===$selectedIndex) - $style=$selectedItemStyle; - else if($index%2===0) - $style=$itemStyle; - else - $style=$alternatingItemStyle; - if($style && $item instanceof IStyleable) - $item->getStyle()->mergeWith($style); - } - - // apply separator style if any - if(($separatorStyle=$this->getViewState('SeparatorStyle',null))!==null && $this->getHasSeparators()) - { - $controls=$this->getControls(); - $count=$controls->getCount(); - for($i=$this->_header?2:1;$i<$count;$i+=2) - { - if(($separator=$controls->itemAt($i)) instanceof IStyleable) - $separator->getStyle()->mergeWith($separatorStyle); - } - } - } - - /** - * Saves item count in viewstate. - * This method is invoked right before control state is to be saved. - */ - public function saveState() - { - parent::saveState(); - if($this->_items) - $this->setViewState('ItemCount',$this->_items->getCount(),0); - else - $this->clearViewState('ItemCount'); - } - - /** - * Loads item count information from viewstate. - * This method is invoked right after control state is loaded. - */ - public function loadState() - { - parent::loadState(); - if(!$this->getIsDataBound()) - $this->restoreItemsFromViewState(); - $this->clearViewState('ItemCount'); - } - - /** - * Clears up all items in the data list. - */ - public function reset() - { - $this->getControls()->clear(); - $this->getItems()->clear(); - $this->_header=null; - $this->_footer=null; - } - - /** - * Creates data list items based on viewstate information. - */ - protected function restoreItemsFromViewState() - { - $this->reset(); - if(($itemCount=$this->getViewState('ItemCount',0))>0) - { - $items=$this->getItems(); - $selectedIndex=$this->getSelectedItemIndex(); - $editIndex=$this->getEditItemIndex(); - $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; - $this->_header=$this->createItemInternal(-1,TListItemType::Header); - for($i=0;$i<$itemCount;++$i) - { - if($hasSeparator && $i>0) - $this->createItemInternal($i-1,TListItemType::Separator); - if($i===$editIndex) - $itemType=TListItemType::EditItem; - else if($i===$selectedIndex) - $itemType=TListItemType::SelectedItem; - else - $itemType=$i%2?TListItemType::AlternatingItem : TListItemType::Item; - $items->add($this->createItemInternal($i,$itemType)); - } - $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); - } - else - $this->createEmptyContent(); - $this->clearChildState(); - } - - /** - * Performs databinding to populate data list items from data source. - * This method is invoked by dataBind(). - * You may override this function to provide your own way of data population. - * @param Traversable the data - */ - protected function performDataBinding($data) - { - $this->reset(); - $keys=$this->getDataKeys(); - $keys->clear(); - $keyField=$this->getDataKeyField(); - $itemIndex=0; - $items=$this->getItems(); - $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; - $selectedIndex=$this->getSelectedItemIndex(); - $editIndex=$this->getEditItemIndex(); - foreach($data as $key=>$dataItem) - { - if($keyField!=='') - $keys->add($this->getDataFieldValue($dataItem,$keyField)); - else - $keys->add($key); - if($itemIndex===0) - $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); - if($hasSeparator && $itemIndex>0) - $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); - if($itemIndex===$editIndex) - $itemType=TListItemType::EditItem; - else if($itemIndex===$selectedIndex) - $itemType=TListItemType::SelectedItem; - else - $itemType=$itemIndex%2?TListItemType::AlternatingItem : TListItemType::Item; - $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); - $itemIndex++; - } - if($itemIndex>0) - $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); - else - { - $this->createEmptyContent(); - $this->dataBindChildren(); - } - $this->setViewState('ItemCount',$itemIndex,0); - } - - /** - * Renders the data list control. - * This method overrides the parent implementation. - * @param THtmlWriter writer for rendering purpose. - */ - public function render($writer) - { - if($this->getHasControls()) - { - if($this->getItemCount()>0) - { - $this->applyItemStyles(); - $repeatInfo=$this->getRepeatInfo(); - $repeatInfo->renderRepeater($writer,$this); - } - else if($this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') - parent::render($writer); - } - } -} - - -/** - * TDataListItemEventParameter class - * - * TDataListItemEventParameter encapsulates the parameter data for - * {@link TDataList::onItemCreated ItemCreated} event of {@link TDataList} controls. - * The {@link getItem Item} property indicates the DataList item related with the event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataListItemEventParameter extends TEventParameter -{ - /** - * The datalist item control responsible for the event. - * @var TControl - */ - private $_item=null; - - /** - * Constructor. - * @param TControl DataList item related with the corresponding event - */ - public function __construct($item) - { - $this->_item=$item; - } - - /** - * @return TControl datalist item related with the corresponding event - */ - public function getItem() - { - return $this->_item; - } -} - -/** - * TDataListCommandEventParameter class - * - * TDataListCommandEventParameter encapsulates the parameter data for - * {@link TDataList::onItemCommand ItemCommand} event of {@link TDataList} controls. - * - * The {@link getItem Item} property indicates the DataList item related with the event. - * The {@link getCommandSource CommandSource} refers to the control that originally - * raises the Command event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataListCommandEventParameter extends TCommandEventParameter -{ - /** - * @var TControl the datalist item control responsible for the event. - */ - private $_item=null; - /** - * @var TControl the control originally raises the OnCommand event. - */ - private $_source=null; - - /** - * Constructor. - * @param TControl datalist item responsible for the event - * @param TControl original event sender - * @param TCommandEventParameter original event parameter - */ - public function __construct($item,$source,TCommandEventParameter $param) - { - $this->_item=$item; - $this->_source=$source; - parent::__construct($param->getCommandName(),$param->getCommandParameter()); - } - - /** - * @return TControl the datalist item control responsible for the event. - */ - public function getItem() - { - return $this->_item; - } - - /** - * @return TControl the control originally raises the OnCommand event. - */ - public function getCommandSource() - { - return $this->_source; - } -} - -/** - * TDataListItem class - * - * A TDataListItem control represents an item in the {@link TDataList} control, - * such as heading section, footer section, or a data item. - * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> - * and {@link getDataItem DataItem} properties, respectively. The type of the item - * is given by {@link getItemType ItemType} property. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataListItem extends TWebControl implements INamingContainer, IItemDataRenderer -{ - /** - * index of the data item in the Items collection of DataList - */ - private $_itemIndex; - /** - * type of the TDataListItem - * @var TListItemType - */ - private $_itemType; - /** - * value of the data associated with this item - * @var mixed - */ - private $_data; - - private $_tagName='span'; - - /** - * Returns the tag name used for this control. - * @return string tag name of the control to be rendered - */ - protected function getTagName() - { - return $this->_tagName; - } - - /** - * @param string tag name of the control to be rendered - */ - public function setTagName($value) - { - $this->_tagName=$value; - } - - /** - * Creates a style object for the control. - * This method creates a {@link TTableItemStyle} to be used by a datalist item. - * @return TStyle control style to be used - */ - protected function createStyle() - { - return new TTableItemStyle; - } - - /** - * @return TListItemType item type - */ - public function getItemType() - { - return $this->_itemType; - } - - /** - * @param TListItemType item type. - */ - public function setItemType($value) - { - $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); - } - - /** - * @return integer zero-based index of the item in the item collection of datalist - */ - public function getItemIndex() - { - return $this->_itemIndex; - } - - /** - * Sets the zero-based index for the item. - * If the item is not in the item collection (e.g. it is a header item), -1 should be used. - * @param integer zero-based index of the item. - */ - public function setItemIndex($value) - { - $this->_itemIndex=TPropertyValue::ensureInteger($value); - } - - /** - * @return mixed data associated with the item - * @since 3.1.0 - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed data to be associated with the item - * @since 3.1.0 - */ - public function setData($value) - { - $this->_data=$value; - } - - /** - * This property is deprecated since v3.1.0. - * @return mixed data associated with the item - * @deprecated deprecated since v3.1.0. Use {@link getData} instead. - */ - public function getDataItem() - { - return $this->getData(); - } - - /** - * This property is deprecated since v3.1.0. - * @param mixed data to be associated with the item - * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. - */ - public function setDataItem($value) - { - return $this->setData($value); - } - - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } -} - -/** - * TDataListItemCollection class. - * - * TDataListItemCollection represents a collection of data list items. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataListItemCollection extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by inserting only TControl descendants. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TControl descendant. - */ - public function insertAt($index,$item) - { - if($item instanceof TControl) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('datalistitemcollection_datalistitem_required'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TBaseDataList class + */ +Prado::using('System.Web.UI.WebControls.TBaseDataList'); +/** + * Includes TRepeatInfo class + */ +Prado::using('System.Web.UI.WebControls.TRepeatInfo'); + +/** + * TDataList class + * + * TDataList represents a data bound and updatable list control. + * + * Like {@link TRepeater}, TDataList displays its content repeatedly based on + * the data fetched from {@link setDataSource DataSource}. + * The repeated contents in TDataList are called items, which are controls and + * can be accessed through {@link getItems Items}. When {@link dataBind()} is + * invoked, TDataList creates an item for each row of data and binds the data + * row to the item. Optionally, a TDataList can have a header, a footer and/or + * separators between items. + * + * TDataList differs from {@link TRepeater} in that it supports tiling the items + * in different manners and it maintains status of items to handle data update. + * + * The layout of the repeated contents are specified by inline templates. + * TDataList items, header, footer, etc. are being instantiated with the corresponding + * templates when data is being bound to the repeater. + * + * Since v3.1.0, the layout can also be by renderers. A renderer is a control class + * that can be instantiated as datalist items, header, etc. A renderer can thus be viewed + * as an external template (in fact, it can also be non-templated controls). + * + * A renderer can be any control class. + * - If the class implements {@link IDataRenderer}, the Data + * property will be set as the data row during databinding. Many PRADO controls + * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. + * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set + * as the zero-based index of the item in the datalist item collection, and + * the ItemType property as the item's type (such as TListItemType::Item). + * {@link TDataListItemRenderer} may be used as the convenient base class which + * already implements {@link IDataItemRenderer}. + * + * The following properties are used to specify different types of template and renderer + * for a datalist: + * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: + * for each repeated row of data + * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: + * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} + * will be used instead. + * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: + * for the datalist header. + * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: + * for the datalist footer. + * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: + * for content to be displayed between items. + * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: + * used when data bound to the datalist is empty. + * - {@link setEditItemTemplate EditItemTemplate}, {@link setEditItemRenderer EditItemRenderer}: + * for the row being editted. + * - {@link setSelectedItemTemplate SelectedItemTemplate}, {@link setSelectedItemRenderer SelectedItemRenderer}: + * for the row being selected. + * + * If a content type is defined with both a template and a renderer, the latter takes precedence. + * + * When {@link dataBind()} is being called, TDataList undergoes the following lifecycles for each row of data: + * - create item based on templates or renderers + * - set the row of data to the item + * - raise {@link onItemCreated OnItemCreated}: + * - add the item as a child control + * - call dataBind() of the item + * - raise {@link onItemDataBound OnItemDataBound}: + * + * TDataList raises an {@link onItemCommand OnItemCommand} whenever a button control + * within some datalist item raises a OnCommand event. Therefore, + * you can handle all sorts of OnCommand event in a central place by + * writing an event handler for {@link onItemCommand OnItemCommand}. + * + * An additional event is raised if the OnCommand event has one of the following + * command names: + * - edit: user wants to edit an item. OnEditCommand event will be raised. + * - update: user wants to save the change to an item. OnUpdateCommand event will be raised. + * - select: user selects an item. OnSelectedIndexChanged event will be raised. + * - delete: user deletes an item. OnDeleteCommand event will be raised. + * - cancel: user cancels previously editting action. OnCancelCommand event will be raised. + * + * TDataList provides a few properties to support tiling the items. + * The number of columns used to display the data items is specified via + * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} + * governs the order of the items being rendered. + * The layout of the data items in the list is specified via {@link setRepeatLayout RepeatLayout}, + * which can take one of the following values: + * - Table (default): items are organized using HTML table and cells. + * When using this layout, one can set {@link setCellPadding CellPadding} and + * {@link setCellSpacing CellSpacing} to adjust the cellpadding and cellpadding + * of the table, and {@link setCaption Caption} and {@link setCaptionAlign CaptionAlign} + * to add a table caption with the specified alignment. + * - Flow: items are organized using HTML spans and breaks. + * - Raw: TDataList does not generate any HTML tags to do the tiling. + * + * Items in TDataList can be in one of the three status: normal browsing, + * being editted and being selected. To change the status of a particular + * item, set {@link setSelectedItemIndex SelectedItemIndex} or + * {@link setEditItemIndex EditItemIndex}. The former will change + * the indicated item to selected mode, which will cause the item to + * use {@link setSelectedItemTemplate SelectedItemTemplate} or + * {@link setSelectedItemRenderer SelectedItemRenderer} for presentation. + * The latter will change the indicated item to edit mode and to use corresponding + * template or renderer. + * Note, if an item is in edit mode, then selecting this item will have no effect. + * + * Different styles may be applied to items in different status. The style + * application is performed in a hierarchical way: Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * - item's own style + * - {@link getItemStyle ItemStyle} + * - {@link getAlternatingItemStyle AlternatingItemStyle} + * - {@link getSelectedItemStyle SelectedItemStyle} + * - {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + * + * When a page containing a datalist is post back, the datalist will restore automatically + * all its contents, including items, header, footer and separators. + * However, the data row associated with each item will not be recovered and become null. + * To access the data, use one of the following ways: + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified datalist item and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. + * Be aware though, if the size of your dataset is big, your page size will become big. Some + * complex data may also have serializing problem if saved in viewstate. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUser +{ + /** + * Command name that TDataList understands. They are case-insensitive. + */ + const CMD_SELECT='Select'; + const CMD_EDIT='Edit'; + const CMD_UPDATE='Update'; + const CMD_DELETE='Delete'; + const CMD_CANCEL='Cancel'; + + /** + * @var TDataListItemCollection item list + */ + private $_items=null; + /** + * @var Itemplate various item templates + */ + private $_itemTemplate=null; + private $_emptyTemplate=null; + private $_alternatingItemTemplate=null; + private $_selectedItemTemplate=null; + private $_editItemTemplate=null; + private $_headerTemplate=null; + private $_footerTemplate=null; + private $_separatorTemplate=null; + /** + * @var TControl header item + */ + private $_header=null; + /** + * @var TControl footer item + */ + private $_footer=null; + + /** + * @return TDataListItemCollection item list + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TDataListItemCollection; + return $this->_items; + } + + /** + * @return integer number of items + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * @return string the class name for datalist items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item renderer class. + * + * If not empty, the class will be used to instantiate as datalist items. + * This property takes precedence over {@link getItemTemplate ItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setItemTemplate + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for alternative datalist items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getAlternatingItemRenderer() + { + return $this->getViewState('AlternatingItemRenderer',''); + } + + /** + * Sets the alternative item renderer class. + * + * If not empty, the class will be used to instantiate as alternative datalist items. + * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setAlternatingItemTemplate + * @since 3.1.0 + */ + public function setAlternatingItemRenderer($value) + { + $this->setViewState('AlternatingItemRenderer',$value,''); + } + + /** + * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEditItemRenderer() + { + return $this->getViewState('EditItemRenderer',''); + } + + /** + * Sets the renderer class for the datalist item being editted. + * + * If not empty, the class will be used to instantiate as the datalist item. + * This property takes precedence over {@link getEditItemTemplate EditItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEditItemTemplate + * @since 3.1.0 + */ + public function setEditItemRenderer($value) + { + $this->setViewState('EditItemRenderer',$value,''); + } + + /** + * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSelectedItemRenderer() + { + return $this->getViewState('SelectedItemRenderer',''); + } + + /** + * Sets the renderer class for the datalist item being selected. + * + * If not empty, the class will be used to instantiate as the datalist item. + * This property takes precedence over {@link getSelectedItemTemplate SelectedItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSelectedItemTemplate + * @since 3.1.0 + */ + public function setSelectedItemRenderer($value) + { + $this->setViewState('SelectedItemRenderer',$value,''); + } + + /** + * @return string the class name for datalist item separators. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSeparatorRenderer() + { + return $this->getViewState('SeparatorRenderer',''); + } + + /** + * Sets the datalist item separator renderer class. + * + * If not empty, the class will be used to instantiate as datalist item separators. + * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSeparatorTemplate + * @since 3.1.0 + */ + public function setSeparatorRenderer($value) + { + $this->setViewState('SeparatorRenderer',$value,''); + } + + /** + * @return string the class name for datalist header item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the datalist header renderer class. + * + * If not empty, the class will be used to instantiate as datalist header item. + * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setHeaderTemplate + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @return string the class name for datalist footer item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the datalist footer renderer class. + * + * If not empty, the class will be used to instantiate as datalist footer item. + * This property takes precedence over {@link getFooterTemplate FooterTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setFooterTemplate + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @return string the class name for empty datalist item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEmptyRenderer() + { + return $this->getViewState('EmptyRenderer',''); + } + + /** + * Sets the datalist empty renderer class. + * + * The empty renderer is created as the child of the datalist + * if data bound to the datalist is empty. + * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEmptyTemplate + * @since 3.1.0 + */ + public function setEmptyRenderer($value) + { + $this->setViewState('EmptyRenderer',$value,''); + } + + /** + * @return ITemplate the template for item + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the template for item + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','ItemTemplate'); + } + + /** + * @return TTableItemStyle the style for item + */ + public function getItemStyle() + { + if(($style=$this->getViewState('ItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the template for each alternating item + */ + public function getAlternatingItemTemplate() + { + return $this->_alternatingItemTemplate; + } + + /** + * @param ITemplate the template for each alternating item + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setAlternatingItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_alternatingItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','AlternatingItemType'); + } + + /** + * @return TTableItemStyle the style for each alternating item + */ + public function getAlternatingItemStyle() + { + if(($style=$this->getViewState('AlternatingItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('AlternatingItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the selected item template + */ + public function getSelectedItemTemplate() + { + return $this->_selectedItemTemplate; + } + + /** + * @param ITemplate the selected item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSelectedItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_selectedItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','SelectedItemTemplate'); + } + + /** + * @return TTableItemStyle the style for selected item + */ + public function getSelectedItemStyle() + { + if(($style=$this->getViewState('SelectedItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SelectedItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the edit item template + */ + public function getEditItemTemplate() + { + return $this->_editItemTemplate; + } + + /** + * @param ITemplate the edit item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEditItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_editItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','EditItemTemplate'); + } + + /** + * @return TTableItemStyle the style for edit item + */ + public function getEditItemStyle() + { + if(($style=$this->getViewState('EditItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('EditItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','HeaderTemplate'); + } + + /** + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TControl the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','FooterTemplate'); + } + + /** + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle() + { + if(($style=$this->getViewState('FooterStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @return TControl the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return ITemplate the template applied when no data is bound to the datalist + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the datalist + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','EmptyTemplate'); + } + + /** + * @return ITemplate the separator template + */ + public function getSeparatorTemplate() + { + return $this->_separatorTemplate; + } + + /** + * @param ITemplate the separator template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSeparatorTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_separatorTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','SeparatorTemplate'); + } + + /** + * @return TTableItemStyle the style for separator + */ + public function getSeparatorStyle() + { + if(($style=$this->getViewState('SeparatorStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SeparatorStyle',$style,null); + } + return $style; + } + + /** + * @return integer the zero-based index of the selected item in {@link getItems Items}. + * A value -1 means no item selected. + */ + public function getSelectedItemIndex() + { + return $this->getViewState('SelectedItemIndex',-1); + } + + /** + * Selects an item by its index in {@link getItems Items}. + * Previously selected item will be un-selected. + * If the item to be selected is already in edit mode, it will remain in edit mode. + * If the index is less than 0, any existing selection will be cleared up. + * @param integer the selected item index + */ + public function setSelectedItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getSelectedItemIndex())!==$value) + { + $this->setViewState('SelectedItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + { + $item=$items->itemAt($current); + if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) + $item->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); + } + if($value>=0 && $value<$itemCount) + { + $item=$items->itemAt($value); + if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) + $item->setItemType(TListItemType::SelectedItem); + } + } + } + + /** + * @return TControl the selected item, null if no item is selected. + */ + public function getSelectedItem() + { + $index=$this->getSelectedItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return mixed the key value of the currently selected item + * @throws TInvalidOperationException if {@link getDataKeyField DataKeyField} is empty. + */ + public function getSelectedDataKey() + { + if($this->getDataKeyField()==='') + throw new TInvalidOperationException('datalist_datakeyfield_required'); + $index=$this->getSelectedItemIndex(); + $dataKeys=$this->getDataKeys(); + if($index>=0 && $index<$dataKeys->getCount()) + return $dataKeys->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the edit item in {@link getItems Items}. + * A value -1 means no item is in edit mode. + */ + public function getEditItemIndex() + { + return $this->getViewState('EditItemIndex',-1); + } + + /** + * Edits an item by its index in {@link getItems Items}. + * Previously editting item will change to normal item state. + * If the index is less than 0, any existing edit item will be cleared up. + * @param integer the edit item index + */ + public function setEditItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getEditItemIndex())!==$value) + { + $this->setViewState('EditItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); + if($value>=0 && $value<$itemCount) + $items->itemAt($value)->setItemType(TListItemType::EditItem); + } + } + + /** + * @return TControl the edit item + */ + public function getEditItem() + { + $index=$this->getEditItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return boolean whether the header should be shown. Defaults to true. + */ + public function getShowHeader() + { + return $this->getViewState('ShowHeader',true); + } + + /** + * @param boolean whether to show header + */ + public function setShowHeader($value) + { + $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the footer should be shown. Defaults to true. + */ + public function getShowFooter() + { + return $this->getViewState('ShowFooter',true); + } + + /** + * @param boolean whether to show footer + */ + public function setShowFooter($value) + { + $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TRepeatInfo repeat information (primarily used by control developers) + */ + protected function getRepeatInfo() + { + if(($repeatInfo=$this->getViewState('RepeatInfo',null))===null) + { + $repeatInfo=new TRepeatInfo; + $this->setViewState('RepeatInfo',$repeatInfo,null); + } + return $repeatInfo; + } + + /** + * @return string caption of the table layout + */ + public function getCaption() + { + return $this->getRepeatInfo()->getCaption(); + } + + /** + * @param string caption of the table layout + */ + public function setCaption($value) + { + $this->getRepeatInfo()->setCaption($value); + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getRepeatInfo()->getCaptionAlign(); + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. + */ + public function setCaptionAlign($value) + { + $this->getRepeatInfo()->setCaptionAlign($value); + } + + /** + * @return integer the number of columns that the list should be displayed with. Defaults to 0 meaning not set. + */ + public function getRepeatColumns() + { + return $this->getRepeatInfo()->getRepeatColumns(); + } + + /** + * @param integer the number of columns that the list should be displayed with. + */ + public function setRepeatColumns($value) + { + $this->getRepeatInfo()->setRepeatColumns($value); + } + + /** + * @return TRepeatDirection the direction of traversing the list, defaults to TRepeatDirection::Vertical + */ + public function getRepeatDirection() + { + return $this->getRepeatInfo()->getRepeatDirection(); + } + + /** + * @param TRepeatDirection the direction of traversing the list + */ + public function setRepeatDirection($value) + { + $this->getRepeatInfo()->setRepeatDirection($value); + } + + /** + * @return TRepeatLayout how the list should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. + */ + public function getRepeatLayout() + { + return $this->getRepeatInfo()->getRepeatLayout(); + } + + /** + * @param TRepeatLayout how the list should be displayed, using table or using line breaks + */ + public function setRepeatLayout($value) + { + $this->getRepeatInfo()->setRepeatLayout($value); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * datalist items and their child controls. + * If the event parameter is {@link TDataListCommandEventParameter} and + * the command name is a recognized one, which includes 'select', 'edit', + * 'delete', 'update', and 'cancel' (case-insensitive), then a + * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TDataListCommandEventParameter) + { + $this->onItemCommand($param); + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_SELECT)===0) + { + if(($item=$param->getItem()) instanceof IItemDataRenderer) + $this->setSelectedItemIndex($item->getItemIndex()); + $this->onSelectedIndexChanged($param); + return true; + } + else if(strcasecmp($command,self::CMD_EDIT)===0) + { + $this->onEditCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_DELETE)===0) + { + $this->onDeleteCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_UPDATE)===0) + { + $this->onUpdateCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelCommand($param); + return true; + } + } + return false; + } + + + /** + * Raises OnItemCreated event. + * This method is invoked after a data list item is created and instantiated with + * template, but before added to the page hierarchy. + * The datalist item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TDataListItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked right after an item is data bound. + * The datalist item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TDataListItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event. + * @param TDataListCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Raises OnEditCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'edit' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onEditCommand($param) + { + $this->raiseEvent('OnEditCommand',$this,$param); + } + + /** + * Raises OnDeleteCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'delete' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onDeleteCommand($param) + { + $this->raiseEvent('OnDeleteCommand',$this,$param); + } + + /** + * Raises OnUpdateCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'update' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onUpdateCommand($param) + { + $this->raiseEvent('OnUpdateCommand',$this,$param); + } + + /** + * Raises OnCancelCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'cancel' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onCancelCommand($param) + { + $this->raiseEvent('OnCancelCommand',$this,$param); + } + + /** + * Returns a value indicating whether this control contains header item. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean whether the datalist has header + */ + public function getHasHeader() + { + return ($this->getShowHeader() && ($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='')); + } + + /** + * Returns a value indicating whether this control contains footer item. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean whether the datalist has footer + */ + public function getHasFooter() + { + return ($this->getShowFooter() && ($this->_footerTemplate!==null || $this->getFooterRenderer()!=='')); + } + + /** + * Returns a value indicating whether this control contains separator items. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean always false. + */ + public function getHasSeparators() + { + return $this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + } + + /** + * Returns a style used for rendering items. + * This method is required by {@link IRepeatInfoUser} interface. + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer index of the item being rendered + * @return TStyle item style + */ + public function generateItemStyle($itemType,$index) + { + if(($item=$this->getItem($itemType,$index))!==null && ($item instanceof IStyleable) && $item->getHasStyle()) + { + $style=$item->getStyle(); + $item->clearStyle(); + return $style; + } + else + return null; + } + + /** + * Renders an item in the list. + * This method is required by {@link IRepeatInfoUser} interface. + * @param THtmlWriter writer for rendering purpose + * @param TRepeatInfo repeat information + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer zero-based index of the item in the item list + */ + public function renderItem($writer,$repeatInfo,$itemType,$index) + { + $item=$this->getItem($itemType,$index); + if($repeatInfo->getRepeatLayout()===TRepeatLayout::Raw && get_class($item)==='TDataListItem') + $item->setTagName('div'); + $item->renderControl($writer); + } + + /** + * @param TListItemType item type + * @param integer item index + * @return TControl data list item with the specified item type and index + */ + private function getItem($itemType,$index) + { + switch($itemType) + { + case TListItemType::Item: + case TListItemType::AlternatingItem: + case TListItemType::SelectedItem: + case TListItemType::EditItem: + return $this->getItems()->itemAt($index); + case TListItemType::Header: + return $this->getControls()->itemAt(0); + case TListItemType::Footer: + return $this->getControls()->itemAt($this->getControls()->getCount()-1); + case TListItemType::Separator: + $i=$index+$index+1; + if($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='') + $i++; + return $this->getControls()->itemAt($i); + } + return null; + } + + /** + * Creates a datalist item. + * This method invokes {@link createItem} to create a new datalist item. + * @param integer zero-based item index. + * @param TListItemType item type + * @return TControl the created item, null if item is not created + */ + private function createItemInternal($itemIndex,$itemType) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TDataListItemEventParameter($item); + $this->onItemCreated($param); + $this->getControls()->add($item); + return $item; + } + else + return null; + } + + /** + * Creates a datalist item and performs databinding. + * This method invokes {@link createItem} to create a new datalist item. + * @param integer zero-based item index. + * @param TListItemType item type + * @param mixed data to be associated with the item + * @return TControl the created item, null if item is not created + */ + private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TDataListItemEventParameter($item); + if($item instanceof IDataRenderer) + $item->setData($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + return $item; + } + else + return null; + } + + private function getAlternatingItemDisplay() + { + if(($classPath=$this->getAlternatingItemRenderer())==='' && $this->_alternatingItemTemplate===null) + return array($this->getItemRenderer(),$this->_itemTemplate); + else + return array($classPath,$this->_alternatingItemTemplate); + } + + private function getSelectedItemDisplay($itemIndex) + { + if(($classPath=$this->getSelectedItemRenderer())==='' && $this->_selectedItemTemplate===null) + { + if($itemIndex%2===0) + return array($this->getItemRenderer(),$this->_itemTemplate); + else + return $this->getAlternatingItemDisplay(); + } + else + return array($classPath,$this->_selectedItemTemplate); + } + + private function getEditItemDisplay($itemIndex) + { + if(($classPath=$this->getEditItemRenderer())==='' && $this->_editItemTemplate===null) + return $this->getSelectedItemDisplay($itemIndex); + else + return array($classPath,$this->_editItemTemplate); + } + + /** + * Creates a datalist item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TControl created datalist item + */ + protected function createItem($itemIndex,$itemType) + { + $template=null; + $classPath=null; + switch($itemType) + { + case TListItemType::Item : + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + break; + case TListItemType::AlternatingItem : + list($classPath,$template)=$this->getAlternatingItemDisplay(); + break; + case TListItemType::SelectedItem: + list($classPath,$template)=$this->getSelectedItemDisplay($itemIndex); + break; + case TListItemType::EditItem: + list($classPath,$template)=$this->getEditItemDisplay($itemIndex); + break; + case TListItemType::Header : + $classPath=$this->getHeaderRenderer(); + $template=$this->_headerTemplate; + break; + case TListItemType::Footer : + $classPath=$this->getFooterRenderer(); + $template=$this->_footerTemplate; + break; + case TListItemType::Separator : + $classPath=$this->getSeparatorRenderer(); + $template=$this->_separatorTemplate; + break; + default: + throw new TInvalidDataValueException('datalist_itemtype_unknown',$itemType); + } + if($classPath!=='') + { + $item=Prado::createComponent($classPath); + if($item instanceof IItemDataRenderer) + { + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + } + } + else if($template!==null) + { + $item=new TDataListItem; + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + $template->instantiateIn($item); + } + else + $item=null; + + return $item; + } + + /** + * Creates empty datalist content. + */ + protected function createEmptyContent() + { + if(($classPath=$this->getEmptyRenderer())!=='') + $this->getControls()->add(Prado::createComponent($classPath)); + else if($this->_emptyTemplate!==null) + $this->_emptyTemplate->instantiateIn($this); + } + + /** + * Applies styles to items, header, footer and separators. + * Item styles are applied in a hierarchical way. Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, + * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + */ + protected function applyItemStyles() + { + $itemStyle=$this->getViewState('ItemStyle',null); + + $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); + if($itemStyle!==null) + { + if($alternatingItemStyle===null) + $alternatingItemStyle=$itemStyle; + else + $alternatingItemStyle->mergeWith($itemStyle); + } + + $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); + + $editItemStyle=$this->getViewState('EditItemStyle',null); + if($selectedItemStyle!==null) + { + if($editItemStyle===null) + $editItemStyle=$selectedItemStyle; + else + $editItemStyle->mergeWith($selectedItemStyle); + } + + // apply header style if any + if($this->_header!==null && $this->_header instanceof IStyleable) + { + if($headerStyle=$this->getViewState('HeaderStyle',null)) + $this->_header->getStyle()->mergeWith($headerStyle); + } + + // apply footer style if any + if($this->_footer!==null && $this->_footer instanceof IStyleable) + { + if($footerStyle=$this->getViewState('FooterStyle',null)) + $this->_footer->getStyle()->mergeWith($footerStyle); + } + + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + + // apply item styles if any + foreach($this->getItems() as $index=>$item) + { + if($index===$editIndex) + $style=$editItemStyle; + else if($index===$selectedIndex) + $style=$selectedItemStyle; + else if($index%2===0) + $style=$itemStyle; + else + $style=$alternatingItemStyle; + if($style && $item instanceof IStyleable) + $item->getStyle()->mergeWith($style); + } + + // apply separator style if any + if(($separatorStyle=$this->getViewState('SeparatorStyle',null))!==null && $this->getHasSeparators()) + { + $controls=$this->getControls(); + $count=$controls->getCount(); + for($i=$this->_header?2:1;$i<$count;$i+=2) + { + if(($separator=$controls->itemAt($i)) instanceof IStyleable) + $separator->getStyle()->mergeWith($separatorStyle); + } + } + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getIsDataBound()) + $this->restoreItemsFromViewState(); + $this->clearViewState('ItemCount'); + } + + /** + * Clears up all items in the data list. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + } + + /** + * Creates data list items based on viewstate information. + */ + protected function restoreItemsFromViewState() + { + $this->reset(); + if(($itemCount=$this->getViewState('ItemCount',0))>0) + { + $items=$this->getItems(); + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $this->_header=$this->createItemInternal(-1,TListItemType::Header); + for($i=0;$i<$itemCount;++$i) + { + if($hasSeparator && $i>0) + $this->createItemInternal($i-1,TListItemType::Separator); + if($i===$editIndex) + $itemType=TListItemType::EditItem; + else if($i===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else + $itemType=$i%2?TListItemType::AlternatingItem : TListItemType::Item; + $items->add($this->createItemInternal($i,$itemType)); + } + $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); + } + else + $this->createEmptyContent(); + $this->clearChildState(); + } + + /** + * Performs databinding to populate data list items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $this->reset(); + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + $itemIndex=0; + $items=$this->getItems(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + foreach($data as $key=>$dataItem) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($dataItem,$keyField)); + else + $keys->add($key); + if($itemIndex===0) + $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); + if($hasSeparator && $itemIndex>0) + $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); + if($itemIndex===$editIndex) + $itemType=TListItemType::EditItem; + else if($itemIndex===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else + $itemType=$itemIndex%2?TListItemType::AlternatingItem : TListItemType::Item; + $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); + $itemIndex++; + } + if($itemIndex>0) + $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); + else + { + $this->createEmptyContent(); + $this->dataBindChildren(); + } + $this->setViewState('ItemCount',$itemIndex,0); + } + + /** + * Renders the data list control. + * This method overrides the parent implementation. + * @param THtmlWriter writer for rendering purpose. + */ + public function render($writer) + { + if($this->getHasControls()) + { + if($this->getItemCount()>0) + { + $this->applyItemStyles(); + $repeatInfo=$this->getRepeatInfo(); + $repeatInfo->renderRepeater($writer,$this); + } + else if($this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') + parent::render($writer); + } + } +} + + +/** + * TDataListItemEventParameter class + * + * TDataListItemEventParameter encapsulates the parameter data for + * {@link TDataList::onItemCreated ItemCreated} event of {@link TDataList} controls. + * The {@link getItem Item} property indicates the DataList item related with the event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItemEventParameter extends TEventParameter +{ + /** + * The datalist item control responsible for the event. + * @var TControl + */ + private $_item=null; + + /** + * Constructor. + * @param TControl DataList item related with the corresponding event + */ + public function __construct($item) + { + $this->_item=$item; + } + + /** + * @return TControl datalist item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TDataListCommandEventParameter class + * + * TDataListCommandEventParameter encapsulates the parameter data for + * {@link TDataList::onItemCommand ItemCommand} event of {@link TDataList} controls. + * + * The {@link getItem Item} property indicates the DataList item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TControl the datalist item control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the OnCommand event. + */ + private $_source=null; + + /** + * Constructor. + * @param TControl datalist item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TControl the datalist item control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TDataListItem class + * + * A TDataListItem control represents an item in the {@link TDataList} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItem extends TWebControl implements INamingContainer, IItemDataRenderer +{ + /** + * index of the data item in the Items collection of DataList + */ + private $_itemIndex; + /** + * type of the TDataListItem + * @var TListItemType + */ + private $_itemType; + /** + * value of the data associated with this item + * @var mixed + */ + private $_data; + + private $_tagName='span'; + + /** + * Returns the tag name used for this control. + * @return string tag name of the control to be rendered + */ + protected function getTagName() + { + return $this->_tagName; + } + + /** + * @param string tag name of the control to be rendered + */ + public function setTagName($value) + { + $this->_tagName=$value; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by a datalist item. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * @return integer zero-based index of the item in the item collection of datalist + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + +/** + * TDataListItemCollection class. + * + * TDataListItemCollection represents a collection of data list items. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TControl descendants. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TControl descendant. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('datalistitemcollection_datalistitem_required'); + } +} + diff --git a/framework/Web/UI/WebControls/TDataListItemRenderer.php b/framework/Web/UI/WebControls/TDataListItemRenderer.php index 7065dc09..53c48b6e 100644 --- a/framework/Web/UI/WebControls/TDataListItemRenderer.php +++ b/framework/Web/UI/WebControls/TDataListItemRenderer.php @@ -1,172 +1,172 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TDataList'); -Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); - -/** - * TDataListItemRenderer class - * - * TDataListItemRenderer can be used as a convenient base class to - * define an item renderer class specific for {@link TDataList}. - * - * TDataListItemRenderer extends {@link TItemDataRenderer} and implements - * the bubbling scheme for the OnCommand event of data list items. - * - * TDataListItemRenderer also implements the {@link IStyleable} interface, - * which allows TDataList to apply CSS styles to the renders. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.0 - */ -class TDataListItemRenderer extends TItemDataRenderer implements IStyleable -{ - /** - * Creates a style object to be used by the control. - * This method may be overriden by controls to provide customized style. - * @return TStyle - */ - protected function createStyle() - { - return new TTableItemStyle; - } - - /** - * @return boolean whether the control has defined any style information - */ - public function getHasStyle() - { - return $this->getViewState('Style',null)!==null; - } - - /** - * @return TStyle the object representing the css style of the control - */ - public function getStyle() - { - if($style=$this->getViewState('Style',null)) - return $style; - else - { - $style=$this->createStyle(); - $this->setViewState('Style',$style,null); - return $style; - } - } - - /** - * Removes all style data. - */ - public function clearStyle() - { - $this->clearViewState('Style'); - } - - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } - - /** - * Returns the tag name used for this control. - * By default, the tag name is 'span'. - * You can override this method to provide customized tag names. - * If the tag name is empty, the opening and closing tag will NOT be rendered. - * @return string tag name of the control to be rendered - */ - protected function getTagName() - { - return 'span'; - } - - /** - * Adds attribute name-value pairs to renderer. - * By default, this method renders the style string. - * The method can be overriden to provide customized attribute rendering. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - if($style=$this->getViewState('Style',null)) - $style->addAttributesToRender($writer); - } - - /** - * Renders the control. - * This method overrides the parent implementation by replacing it with - * the following sequence: - * - {@link renderBeginTag} - * - {@link renderContents} - * - {@link renderEndTag} - * If the {@link getTagName TagName} is empty, only {@link renderContents} is invoked. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - if($this->getTagName()!=='') - { - $this->renderBeginTag($writer); - $this->renderContents($writer); - $this->renderEndTag($writer); - } - else - $this->renderContents($writer); - } - - /** - * Renders the openning tag for the control (including attributes) - * This method is invoked when {@link getTagName TagName} is not empty. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderBeginTag($writer) - { - $this->addAttributesToRender($writer); - $writer->renderBeginTag($this->getTagName()); - } - - /** - * Renders the body content enclosed between the control tag. - * By default, child controls and text strings will be rendered. - * You can override this method to provide customized content rendering. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderContents($writer) - { - parent::renderChildren($writer); - } - - /** - * Renders the closing tag for the control - * This method is invoked when {@link getTagName TagName} is not empty. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderEndTag($writer) - { - $writer->renderEndTag(); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataList'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TDataListItemRenderer class + * + * TDataListItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TDataList}. + * + * TDataListItemRenderer extends {@link TItemDataRenderer} and implements + * the bubbling scheme for the OnCommand event of data list items. + * + * TDataListItemRenderer also implements the {@link IStyleable} interface, + * which allows TDataList to apply CSS styles to the renders. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TDataListItemRenderer extends TItemDataRenderer implements IStyleable +{ + /** + * Creates a style object to be used by the control. + * This method may be overriden by controls to provide customized style. + * @return TStyle + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return boolean whether the control has defined any style information + */ + public function getHasStyle() + { + return $this->getViewState('Style',null)!==null; + } + + /** + * @return TStyle the object representing the css style of the control + */ + public function getStyle() + { + if($style=$this->getViewState('Style',null)) + return $style; + else + { + $style=$this->createStyle(); + $this->setViewState('Style',$style,null); + return $style; + } + } + + /** + * Removes all style data. + */ + public function clearStyle() + { + $this->clearViewState('Style'); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } + + /** + * Returns the tag name used for this control. + * By default, the tag name is 'span'. + * You can override this method to provide customized tag names. + * If the tag name is empty, the opening and closing tag will NOT be rendered. + * @return string tag name of the control to be rendered + */ + protected function getTagName() + { + return 'span'; + } + + /** + * Adds attribute name-value pairs to renderer. + * By default, this method renders the style string. + * The method can be overriden to provide customized attribute rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($style=$this->getViewState('Style',null)) + $style->addAttributesToRender($writer); + } + + /** + * Renders the control. + * This method overrides the parent implementation by replacing it with + * the following sequence: + * - {@link renderBeginTag} + * - {@link renderContents} + * - {@link renderEndTag} + * If the {@link getTagName TagName} is empty, only {@link renderContents} is invoked. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->getTagName()!=='') + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + else + $this->renderContents($writer); + } + + /** + * Renders the openning tag for the control (including attributes) + * This method is invoked when {@link getTagName TagName} is not empty. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + } + + /** + * Renders the body content enclosed between the control tag. + * By default, child controls and text strings will be rendered. + * You can override this method to provide customized content rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + parent::renderChildren($writer); + } + + /** + * Renders the closing tag for the control + * This method is invoked when {@link getTagName TagName} is not empty. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderEndTag($writer) + { + $writer->renderEndTag(); + } +} + diff --git a/framework/Web/UI/WebControls/TDataRenderer.php b/framework/Web/UI/WebControls/TDataRenderer.php index ece9d974..44ab0b1d 100644 --- a/framework/Web/UI/WebControls/TDataRenderer.php +++ b/framework/Web/UI/WebControls/TDataRenderer.php @@ -1,52 +1,52 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.2 - */ - -/** - * TDataRenderer class - * - * TDataRenderer is the convenient base class for template-based renderer controls. - * It extends {@link TTemplateControl} and implements the methods required - * by the {@link IDataRenderer} interface. - * - * The following property is provided by TDataRenderer: - * - {@link getData Data}: data associated with this renderer. - - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.2 - */ -abstract class TDataRenderer extends TTemplateControl implements IDataRenderer -{ - /** - * @var mixed data associated with this renderer - */ - private $_data; - - /** - * @return mixed data associated with the item - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed data to be associated with the item - */ - public function setData($value) - { - $this->_data=$value; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ + +/** + * TDataRenderer class + * + * TDataRenderer is the convenient base class for template-based renderer controls. + * It extends {@link TTemplateControl} and implements the methods required + * by the {@link IDataRenderer} interface. + * + * The following property is provided by TDataRenderer: + * - {@link getData Data}: data associated with this renderer. + + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +abstract class TDataRenderer extends TTemplateControl implements IDataRenderer +{ + /** + * @var mixed data associated with this renderer + */ + private $_data; + + /** + * @return mixed data associated with the item + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + */ + public function setData($value) + { + $this->_data=$value; + } +} + diff --git a/framework/Web/UI/WebControls/TDataSourceControl.php b/framework/Web/UI/WebControls/TDataSourceControl.php index 0b07810a..f7a224af 100644 --- a/framework/Web/UI/WebControls/TDataSourceControl.php +++ b/framework/Web/UI/WebControls/TDataSourceControl.php @@ -1,118 +1,118 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * IDataSource class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -interface IDataSource -{ - public function getView($viewName); - public function getViewNames(); - public function onDataSourceChanged($param); -} - -/** - * TDataSourceControl class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TDataSourceControl extends TControl implements IDataSource -{ - public function getView($viewName) - { - return null; - } - - public function getViewNames() - { - return array(); - } - - public function onDataSourceChanged($param) - { - $this->raiseEvent('OnDataSourceChanged',$this,$param); - } - - public function focus() - { - throw new TNotSupportedException('datasourcecontrol_focus_unsupported'); - } - - public function getEnableTheming() - { - return false; - } - - public function setEnableTheming($value) - { - throw new TNotSupportedException('datasourcecontrol_enabletheming_unsupported'); - } - - public function getSkinID() - { - return ''; - } - - public function setSkinID($value) - { - throw new TNotSupportedException('datasourcecontrol_skinid_unsupported'); - } - - public function getVisible($checkParents=true) - { - return false; - } - - public function setVisible($value) - { - throw new TNotSupportedException('datasourcecontrol_visible_unsupported'); - } -} - -/** - * TDataSourceControl class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TReadOnlyDataSource extends TDataSourceControl -{ - private $_dataSource; - private $_dataMember; - - public function __construct($dataSource,$dataMember) - { - if(!is_array($dataSource) && !($dataSource instanceof IDataSource) && !($dataSource instanceof Traversable)) - throw new TInvalidDataTypeException('readonlydatasource_datasource_invalid'); - $this->_dataSource=$dataSource; - $this->_dataMember=$dataMember; - } - - public function getView($viewName) - { - if($this->_dataSource instanceof IDataSource) - return $this->_dataSource->getView($viewName); - else - return new TReadOnlyDataSourceView($this,$this->_dataMember,$this->_dataSource); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * IDataSource class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +interface IDataSource +{ + public function getView($viewName); + public function getViewNames(); + public function onDataSourceChanged($param); +} + +/** + * TDataSourceControl class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataSourceControl extends TControl implements IDataSource +{ + public function getView($viewName) + { + return null; + } + + public function getViewNames() + { + return array(); + } + + public function onDataSourceChanged($param) + { + $this->raiseEvent('OnDataSourceChanged',$this,$param); + } + + public function focus() + { + throw new TNotSupportedException('datasourcecontrol_focus_unsupported'); + } + + public function getEnableTheming() + { + return false; + } + + public function setEnableTheming($value) + { + throw new TNotSupportedException('datasourcecontrol_enabletheming_unsupported'); + } + + public function getSkinID() + { + return ''; + } + + public function setSkinID($value) + { + throw new TNotSupportedException('datasourcecontrol_skinid_unsupported'); + } + + public function getVisible($checkParents=true) + { + return false; + } + + public function setVisible($value) + { + throw new TNotSupportedException('datasourcecontrol_visible_unsupported'); + } +} + +/** + * TDataSourceControl class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TReadOnlyDataSource extends TDataSourceControl +{ + private $_dataSource; + private $_dataMember; + + public function __construct($dataSource,$dataMember) + { + if(!is_array($dataSource) && !($dataSource instanceof IDataSource) && !($dataSource instanceof Traversable)) + throw new TInvalidDataTypeException('readonlydatasource_datasource_invalid'); + $this->_dataSource=$dataSource; + $this->_dataMember=$dataMember; + } + + public function getView($viewName) + { + if($this->_dataSource instanceof IDataSource) + return $this->_dataSource->getView($viewName); + else + return new TReadOnlyDataSourceView($this,$this->_dataMember,$this->_dataSource); + } +} + diff --git a/framework/Web/UI/WebControls/TDataSourceView.php b/framework/Web/UI/WebControls/TDataSourceView.php index 9e7c0128..af817a32 100644 --- a/framework/Web/UI/WebControls/TDataSourceView.php +++ b/framework/Web/UI/WebControls/TDataSourceView.php @@ -1,206 +1,206 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TDataSourceSelectParameters class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataSourceSelectParameters extends TComponent -{ - private $_retrieveTotalRowCount=false; - private $_startRowIndex=0; - private $_totalRowCount=0; - private $_maximumRows=0; - - public function getStartRowIndex() - { - return $this->_startRowIndex; - } - - public function setStartRowIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=0; - $this->_startRowIndex=$value; - } - - public function getMaximumRows() - { - return $this->_maximumRows; - } - - public function setMaximumRows($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=0; - $this->_maximumRows=$value; - } - - public function getRetrieveTotalRowCount() - { - return $this->_retrieveTotalRowCount; - } - - public function setRetrieveTotalRowCount($value) - { - $this->_retrieveTotalRowCount=TPropertyValue::ensureBoolean($value); - } - - public function getTotalRowCount() - { - return $this->_totalRowCount; - } - - public function setTotalRowCount($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=0; - $this->_totalRowCount=$value; - } -} - -/** - * TDataSourceView class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TDataSourceView extends TComponent -{ - private $_owner; - private $_name; - - public function __construct(IDataSource $owner,$viewName) - { - $this->_owner=$owner; - $this->_name=$viewName; - } - - /** - * Performs DB selection based on specified parameters. - * @param ??? - * @return Traversable - */ - abstract public function select($parameters); - - /** - * Inserts a DB record. - * @param array|TMap - * @return integer affected rows - */ - public function insertAt($values) - { - throw new TNotSupportedException('datasourceview_insert_unsupported'); - } - - /** - * Updates DB record(s) with the specified keys and new values - * @param array|TMap keys for specifying the records to be updated - * @param array|TMap new values - * @return integer affected rows - */ - public function update($keys,$values) - { - throw new TNotSupportedException('datasourceview_update_unsupported'); - } - - /** - * Deletes DB row(s) with the specified keys. - * @param array|TMap keys for specifying the rows to be deleted - * @return integer affected rows - */ - public function delete($keys) - { - throw new TNotSupportedException('datasourceview_delete_unsupported'); - } - - public function getCanDelete() - { - return false; - } - - public function getCanInsert() - { - return false; - } - - public function getCanPage() - { - return false; - } - - public function getCanGetRowCount() - { - return false; - } - - public function getCanSort() - { - return false; - } - - public function getCanUpdate() - { - return false; - } - - public function getName() - { - return $this->_name; - } - - public function getDataSource() - { - return $this->_owner; - } - - public function onDataSourceViewChanged($param) - { - $this->raiseEvent('OnDataSourceViewChanged',$this,$param); - } -} - -/** - * TReadOnlyDataSourceView class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TReadOnlyDataSourceView extends TDataSourceView -{ - private $_dataSource=null; - - public function __construct(IDataSource $owner,$viewName,$dataSource) - { - parent::__construct($owner,$viewName); - if($dataSource===null || is_array($dataSource)) - $this->_dataSource=new TMap($dataSource); - else if($dataSource instanceof Traversable) - $this->_dataSource=$dataSource; - else - throw new TInvalidDataTypeException('readonlydatasourceview_datasource_invalid'); - } - - public function select($parameters) - { - return $this->_dataSource; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TDataSourceSelectParameters class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataSourceSelectParameters extends TComponent +{ + private $_retrieveTotalRowCount=false; + private $_startRowIndex=0; + private $_totalRowCount=0; + private $_maximumRows=0; + + public function getStartRowIndex() + { + return $this->_startRowIndex; + } + + public function setStartRowIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_startRowIndex=$value; + } + + public function getMaximumRows() + { + return $this->_maximumRows; + } + + public function setMaximumRows($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_maximumRows=$value; + } + + public function getRetrieveTotalRowCount() + { + return $this->_retrieveTotalRowCount; + } + + public function setRetrieveTotalRowCount($value) + { + $this->_retrieveTotalRowCount=TPropertyValue::ensureBoolean($value); + } + + public function getTotalRowCount() + { + return $this->_totalRowCount; + } + + public function setTotalRowCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_totalRowCount=$value; + } +} + +/** + * TDataSourceView class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataSourceView extends TComponent +{ + private $_owner; + private $_name; + + public function __construct(IDataSource $owner,$viewName) + { + $this->_owner=$owner; + $this->_name=$viewName; + } + + /** + * Performs DB selection based on specified parameters. + * @param ??? + * @return Traversable + */ + abstract public function select($parameters); + + /** + * Inserts a DB record. + * @param array|TMap + * @return integer affected rows + */ + public function insertAt($values) + { + throw new TNotSupportedException('datasourceview_insert_unsupported'); + } + + /** + * Updates DB record(s) with the specified keys and new values + * @param array|TMap keys for specifying the records to be updated + * @param array|TMap new values + * @return integer affected rows + */ + public function update($keys,$values) + { + throw new TNotSupportedException('datasourceview_update_unsupported'); + } + + /** + * Deletes DB row(s) with the specified keys. + * @param array|TMap keys for specifying the rows to be deleted + * @return integer affected rows + */ + public function delete($keys) + { + throw new TNotSupportedException('datasourceview_delete_unsupported'); + } + + public function getCanDelete() + { + return false; + } + + public function getCanInsert() + { + return false; + } + + public function getCanPage() + { + return false; + } + + public function getCanGetRowCount() + { + return false; + } + + public function getCanSort() + { + return false; + } + + public function getCanUpdate() + { + return false; + } + + public function getName() + { + return $this->_name; + } + + public function getDataSource() + { + return $this->_owner; + } + + public function onDataSourceViewChanged($param) + { + $this->raiseEvent('OnDataSourceViewChanged',$this,$param); + } +} + +/** + * TReadOnlyDataSourceView class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TReadOnlyDataSourceView extends TDataSourceView +{ + private $_dataSource=null; + + public function __construct(IDataSource $owner,$viewName,$dataSource) + { + parent::__construct($owner,$viewName); + if($dataSource===null || is_array($dataSource)) + $this->_dataSource=new TMap($dataSource); + else if($dataSource instanceof Traversable) + $this->_dataSource=$dataSource; + else + throw new TInvalidDataTypeException('readonlydatasourceview_datasource_invalid'); + } + + public function select($parameters) + { + return $this->_dataSource; + } +} + diff --git a/framework/Web/UI/WebControls/TDataTypeValidator.php b/framework/Web/UI/WebControls/TDataTypeValidator.php index 24372565..0e412e24 100644 --- a/framework/Web/UI/WebControls/TDataTypeValidator.php +++ b/framework/Web/UI/WebControls/TDataTypeValidator.php @@ -1,141 +1,141 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TBaseValidator class - */ -Prado::using('System.Web.UI.WebControls.TBaseValidator'); - -/** - * TDataTypeValidator class - * - * TDataTypeValidator verifies if the input data is of the type specified - * by {@link setDataType DataType}. - * The following data types are supported: - * - Integer A 32-bit signed integer data type. - * - Float A double-precision floating point number data type. - * - Date A date data type. - * - String A string data type. - * For Date type, the property {@link setDateFormat DateFormat} - * will be used to determine how to parse the date string. If it is not - * provided, the string will be assumed to be in GNU datetime format. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDataTypeValidator extends TBaseValidator -{ - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TDataTypeValidator'; - } - - /** - * @return TValidationDataType the data type that the values being compared are converted to before the comparison is made. Defaults to TValidationDataType::String. - */ - public function getDataType() - { - return $this->getViewState('DataType','String'); - } - - /** - * Sets the data type that the values being compared are converted to before the comparison is made. - * @param TValidationDataType the data type - */ - public function setDataType($value) - { - $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TValidationDataType'),TValidationDataType::String); - } - - /** - * Sets the date format for a date validation - * @param string the date format value - */ - public function setDateFormat($value) - { - $this->setViewState('DateFormat', $value, ''); - } - - /** - * @return string the date validation date format if any - */ - public function getDateFormat() - { - return $this->getViewState('DateFormat', ''); - } - - - /** - * Determine if the given value is of a particular type using RegExp. - * @param string value to check - * @return boolean true if value fits the type expression. - */ - protected function evaluateDataTypeCheck($value) - { - if($value=='') - return true; - - switch($this->getDataType()) - { - case TValidationDataType::Integer: - return preg_match('/^[-+]?[0-9]+$/',trim($value)); - case TValidationDataType::Float: - return preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value)); - case TValidationDataType::Date: - $dateFormat = $this->getDateFormat(); - if(strlen($dateFormat)) - { - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$dateFormat); - return $formatter->isValidDate($value); - } - else - return strtotime($value) > 0; - } - return true; - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options = parent::getClientScriptOptions(); - $options['DataType']=$this->getDataType(); - if(($dateFormat=$this->getDateFormat())!=='') - $options['DateFormat']=$dateFormat; - return $options; - } - - /** - * This method overrides the parent's implementation. - * The validation succeeds if the input data is of valid type. - * The validation always succeeds if ControlToValidate is not specified - * or the input data is empty. - * @return boolean whether the validation succeeds - */ - public function evaluateIsValid() - { - if(($value=$this->getValidationValue($this->getValidationTarget()))==='') - return true; - - return $this->evaluateDataTypeCheck($value); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TDataTypeValidator class + * + * TDataTypeValidator verifies if the input data is of the type specified + * by {@link setDataType DataType}. + * The following data types are supported: + * - Integer A 32-bit signed integer data type. + * - Float A double-precision floating point number data type. + * - Date A date data type. + * - String A string data type. + * For Date type, the property {@link setDateFormat DateFormat} + * will be used to determine how to parse the date string. If it is not + * provided, the string will be assumed to be in GNU datetime format. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataTypeValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TDataTypeValidator'; + } + + /** + * @return TValidationDataType the data type that the values being compared are converted to before the comparison is made. Defaults to TValidationDataType::String. + */ + public function getDataType() + { + return $this->getViewState('DataType','String'); + } + + /** + * Sets the data type that the values being compared are converted to before the comparison is made. + * @param TValidationDataType the data type + */ + public function setDataType($value) + { + $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TValidationDataType'),TValidationDataType::String); + } + + /** + * Sets the date format for a date validation + * @param string the date format value + */ + public function setDateFormat($value) + { + $this->setViewState('DateFormat', $value, ''); + } + + /** + * @return string the date validation date format if any + */ + public function getDateFormat() + { + return $this->getViewState('DateFormat', ''); + } + + + /** + * Determine if the given value is of a particular type using RegExp. + * @param string value to check + * @return boolean true if value fits the type expression. + */ + protected function evaluateDataTypeCheck($value) + { + if($value=='') + return true; + + switch($this->getDataType()) + { + case TValidationDataType::Integer: + return preg_match('/^[-+]?[0-9]+$/',trim($value)); + case TValidationDataType::Float: + return preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value)); + case TValidationDataType::Date: + $dateFormat = $this->getDateFormat(); + if(strlen($dateFormat)) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$dateFormat); + return $formatter->isValidDate($value); + } + else + return strtotime($value) > 0; + } + return true; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['DataType']=$this->getDataType(); + if(($dateFormat=$this->getDateFormat())!=='') + $options['DateFormat']=$dateFormat; + return $options; + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input data is of valid type. + * The validation always succeeds if ControlToValidate is not specified + * or the input data is empty. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + if(($value=$this->getValidationValue($this->getValidationTarget()))==='') + return true; + + return $this->evaluateDataTypeCheck($value); + } +} + diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index 8d1a811c..866bc585 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -1,993 +1,993 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTextBox class - */ -Prado::using('System.Web.UI.WebControls.TTextBox'); - -/** - * - * TDatePicker class. - * - * TDatePicker displays a text box for date input purpose. - * When the text box receives focus, a calendar will pop up and users can - * pick up from it a date that will be automatically entered into the text box. - * The format of the date string displayed in the text box is determined by - * the DateFormat property. Valid formats are the combination of the - * following tokens, - * - * - * Character Format Pattern (en-US) - * ----------------------------------------- - * d day digit - * dd padded day digit e.g. 01, 02 - * M month digit - * MM padded month digit - * MMMM localized month name, e.g. March, April - * yy 2 digit year - * yyyy 4 digit year - * ----------------------------------------- - * - * - * TDatePicker has four Mode to show the date picker popup. - * - * # Basic -- Only shows a text input, focusing on the input shows the - * date picker. This way you can access the popup using only - * the keyboard. Note that because of this, TAB-bing through - * this control will automatically select the current date if - * no previous date was selected. If you close the popup (eg. - * pressing the ESC key) you'll need to un-focus and re-focus - * the control again for the popup to reappear. - * # Clickable -- Only shows a text input, clicking on the input shows the - * date picker. This mode solves the two small problems of the - * Basic mode. It was first introduced in Prado 3.2. - * # Button -- Shows a button next to the text input, clicking on the - * button shows the date, button text can be by the - * ButtonText property - * # ImageButton -- Shows an image next to the text input, clicking on - * the image shows the date picker, image source can be - * change through the ButtonImageUrl property. - * - * The CssClass property can be used to override the css class name - * for the date picker panel. CalendarStyle property sets the packages - * styles available. E.g. default. - * - * The InputMode property can be set to "TextBox" or "DropDownList" with - * default as "TextBox". - * In DropDownList mode, in addition to the popup date picker, three - * drop down list (day, month and year) are presented to select the date . - * - * The PositionMode property can be set to "Top" or "Bottom" with default - * as "Bottom". It specifies the position of the calendar popup, relative to the - * input field. - * - * @author Wei Zhuo - * @author Carl G. Mathisen - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDatePicker extends TTextBox -{ - /** - * Script path relative to the TClientScriptManager::SCRIPT_PATH - */ - const SCRIPT_PATH = 'prado/datepicker'; - - /** - * @var TDatePickerClientScript validator client-script options. - */ - private $_clientScript; - /** - * AutoPostBack is not supported. - */ - public function setAutoPostBack($value) - { - throw new TNotSupportedException('tdatepicker_autopostback_unsupported', - get_class($this)); - } - - /** - * @return string the format of the date string - */ - public function getDateFormat() - { - return $this->getViewState('DateFormat','dd-MM-yyyy'); - } - - /** - * Sets the format of the date string. - * @param string the format of the date string - */ - public function setDateFormat($value) - { - $this->setViewState('DateFormat',$value,'dd-MM-yyyy'); - } - - /** - * @return boolean whether the calendar window should pop up when the control receives focus - */ - public function getShowCalendar() - { - return $this->getViewState('ShowCalendar',true); - } - - /** - * Sets whether to pop up the calendar window when the control receives focus - * @param boolean whether to show the calendar window - */ - public function setShowCalendar($value) - { - $this->setViewState('ShowCalendar',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Gets the current culture. - * @return string current culture, e.g. en_AU. - */ - public function getCulture() - { - return $this->getViewState('Culture', ''); - } - - /** - * Sets the culture/language for the date picker. - * @param string a culture string, e.g. en_AU. - */ - public function setCulture($value) - { - $this->setViewState('Culture', $value, ''); - } - - /** - * @param TDatePickerInputMode input method of date values - */ - public function setInputMode($value) - { - $this->setViewState('InputMode', TPropertyValue::ensureEnum($value, 'TDatePickerInputMode'), TDatePickerInputMode::TextBox); - } - - /** - * @return TDatePickerInputMode input method of date values. Defaults to TDatePickerInputMode::TextBox. - */ - public function getInputMode() - { - return $this->getViewState('InputMode', TDatePickerInputMode::TextBox); - } - - /** - * @param TDatePickerMode calendar UI mode - */ - public function setMode($value) - { - $this->setViewState('Mode', TPropertyValue::ensureEnum($value, 'TDatePickerMode'), TDatePickerMode::Basic); - } - - /** - * @return TDatePickerMode current calendar UI mode. - */ - public function getMode() - { - return $this->getViewState('Mode', TDatePickerMode::Basic); - } - /** - * @param string the image url for "Image" UI mode. - */ - public function setButtonImageUrl($value) - { - $this->setViewState('ImageUrl', $value, ''); - } - - /** - * @return string the image url for "Image" UI mode. - */ - public function getButtonImageUrl() - { - return $this->getViewState('ImageUrl', ''); - } - - /** - * @param string set the calendar style - */ - public function setCalendarStyle($value) - { - $this->setViewState('CalendarStyle', $value, 'default'); - } - - /** - * @return string current calendar style - */ - public function getCalendarStyle() - { - return $this->getViewState('CalendarStyle', 'default'); - } - - /** - * Set the first day of week, with 0 as Sunday, 1 as Monday, etc. - * @param integer 0 for Sunday, 1 for Monday, 2 for Tuesday, etc. - */ - public function setFirstDayOfWeek($value) - { - $this->setViewState('FirstDayOfWeek', TPropertyValue::ensureInteger($value), 1); - } - - /** - * @return integer first day of the week - */ - public function getFirstDayOfWeek() - { - return $this->getViewState('FirstDayOfWeek', 1); - } - - /** - * @return string text for the date picker button. Default is "...". - */ - public function getButtonText() - { - return $this->getViewState('ButtonText', '...'); - } - - /** - * @param string text for the date picker button - */ - public function setButtonText($value) - { - $this->setViewState('ButtonText', $value, '...'); - } - - /** - * @param integer date picker starting year, default is 2000. - */ - public function setFromYear($value) - { - $this->setViewState('FromYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))-5); - } - - /** - * @return integer date picker starting year, default is -5 years - */ - public function getFromYear() - { - return $this->getViewState('FromYear', intval(@date('Y'))-5); - } - - /** - * @param integer date picker ending year, default +10 years - */ - public function setUpToYear($value) - { - $this->setViewState('UpToYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))+10); - } - - /** - * @return integer date picker ending year, default +10 years - */ - public function getUpToYear() - { - return $this->getViewState('UpToYear', intval(@date('Y'))+10); - } - - /** - * @param TDatePickerPositionMode calendar UI position - */ - public function setPositionMode($value) - { - $this->setViewState('PositionMode', TPropertyValue::ensureEnum($value, 'TDatePickerPositionMode'), TDatePickerPositionMode::Bottom); - } - - /** - * @return TDatePickerPositionMode current calendar UI position. - */ - public function getPositionMode() - { - return $this->getViewState('PositionMode', TDatePickerPositionMode::Bottom); - } - - /** - * @return integer current selected date from the date picker as timestamp, NULL if timestamp is not set previously. - */ - public function getTimeStamp() - { - if(trim($this->getText())==='') - return null; - else - return $this->getTimeStampFromText(); - } - - /** - * Sets the date for the date picker using timestamp. - * @param float time stamp for the date picker - */ - public function setTimeStamp($value) - { - if($value===null || (is_string($value) && trim($value)==='')) - $this->setText(''); - else - { - $date = TPropertyValue::ensureFloat($value); - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$this->getDateFormat()); - $this->setText($formatter->format($date)); - } - } - - /** - * Returns the timestamp selected by the user. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getTimeStamp()}. - * @return integer the timestamp of the TDatePicker control. - * @see getTimeStamp - * @since 3.1.2 - */ - public function getData() - { - return $this->getTimeStamp(); - } - - /** - * Sets the timestamp represented by this control. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setTimeStamp()}. - * @param integer the timestamp of the TDatePicker control. - * @see setTimeStamp - * @since 3.1.2 - */ - public function setData($value) - { - $this->setTimeStamp($value); - } - - /** - * @return string the date string. - */ - public function getDate() - { - return $this->getText(); - } - - /** - * @param string date string - */ - public function setDate($value) - { - $this->setText($value); - } - - /** - * Gets the TDatePickerClientScript to set the TDatePicker event handlers. - * - * The date picker on the client-side supports the following events. - * # OnDateChanged -- raised when the date is changed. - * - * You can attach custom javascript code to each of these events - * - * @return TDatePickerClientScript javascript validator event options. - */ - public function getClientSide() - { - if($this->_clientScript===null) - $this->_clientScript = $this->createClientScript(); - return $this->_clientScript; - } - - /** - * @return TDatePickerClientScript javascript validator event options. - */ - protected function createClientScript() - { - return new TDatePickerClientScript; - } - - /** - * Returns the value to be validated. - * This methid is required by IValidatable interface. - * @return integer the interger timestamp if valid, otherwise the original text. - */ - public function getValidationPropertyValue() - { - if(($text = $this->getText()) === '') - return ''; - $date = $this->getTimeStamp(); - return $date == null ? $text : $date; - } - - /** - * Publish the date picker Css asset files. - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if($this->getInputMode() === TDatePickerInputMode::DropDownList) - { - $page = $this->getPage(); - $uniqueID = $this->getUniqueID(); - $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'day'); - $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'month'); - $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'year'); - } - $this->publishCalendarStyle(); - $this->registerCalendarClientScriptPre(); - } - - /** - * Renders body content. - * This method overrides parent implementation by adding - * additional date picker button if Mode is Button or ImageButton. - * @param THtmlWriter writer - */ - public function render($writer) - { - if($this->getInputMode() == TDatePickerInputMode::TextBox) - { - parent::render($writer); - $this->renderDatePickerButtons($writer); - } - else - { - $this->renderDropDownListCalendar($writer); - if($this->hasDayPattern()) - { - $this->registerCalendarClientScriptPost(); - $this->renderDatePickerButtons($writer); - } - } - } - - /** - * Renders the date picker popup buttons. - */ - protected function renderDatePickerButtons($writer) - { - if($this->getShowCalendar()) - { - switch ($this->getMode()) - { - case TDatePickerMode::Button: - $this->renderButtonDatePicker($writer); - break; - case TDatePickerMode::ImageButton : - $this->renderImageButtonDatePicker($writer); - break; - } - } - } - - /** - * Loads user input data. Override parent implementation, when InputMode - * is DropDownList call getDateFromPostData to get date data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - if($this->getInputMode() == TDatePickerInputMode::TextBox) - return parent::loadPostData($key, $values); - $value = $this->getDateFromPostData($key, $values); - if(!$this->getReadOnly() && $this->getText()!==$value) - { - $this->setText($value); - return true; - } - else - return false; - } - - /** - * Loads date from drop down list data. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return array the date selected - */ - protected function getDateFromPostData($key, $values) - { - $date = @getdate(); - - if(isset($values[$key.'$day'])) - $day = intval($values[$key.'$day']); - else - $day = $date['mday']; - - if(isset($values[$key.'$month'])) - $month = intval($values[$key.'$month']) + 1; - else - $month = $date['mon']; - - if(isset($values[$key.'$year'])) - $year = intval($values[$key.'$year']); - else - $year = $date['year']; - - $s = Prado::createComponent('System.Util.TDateTimeStamp'); - $date = $s->getTimeStamp(0, 0, 0, $month, $day, $year); - //$date = @mktime(0, 0, 0, $month, $day, $year); - - $pattern = $this->getDateFormat(); - $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $pattern); - return $formatter->format($date); - } - - /** - * Get javascript date picker options. - * @return array date picker client-side options - */ - protected function getDatePickerOptions() - { - $options['ID'] = $this->getClientID(); - $options['InputMode'] = $this->getInputMode(); - $options['Format'] = $this->getDateFormat(); - $options['FirstDayOfWeek'] = $this->getFirstDayOfWeek(); - if(($cssClass=$this->getCssClass())!=='') - $options['ClassName'] = $cssClass; - $options['CalendarStyle'] = $this->getCalendarStyle(); - $options['FromYear'] = $this->getFromYear(); - $options['UpToYear'] = $this->getUpToYear(); - switch($this->getMode()) - { - case TDatePickerMode::Basic: - break; - case TDatePickerMode::Clickable: - $options['TriggerEvent'] = "click"; - break; - default: - $options['Trigger'] = $this->getDatePickerButtonID(); - break; - } - $options['PositionMode'] = $this->getPositionMode(); - - $options = array_merge($options, $this->getCulturalOptions()); - if($this->_clientScript!==null) - $options = array_merge($options, - $this->_clientScript->getOptions()->toArray()); - return $options; - } - - /** - * Get javascript localization options, e.g. month and weekday names. - * @return array localization options. - */ - protected function getCulturalOptions() - { - if($this->getCurrentCulture() == 'en') - return array(); - - $date = $this->getLocalizedCalendarInfo(); - $options['MonthNames'] = $date->getMonthNames(); - $options['AbbreviatedMonthNames'] = $date->getAbbreviatedMonthNames(); - $options['ShortWeekDayNames'] = $date->getAbbreviatedDayNames(); - - return $options; - } - - /** - * @return string the current culture, falls back to application if culture is not set. - */ - protected function getCurrentCulture() - { - $app = $this->getApplication()->getGlobalization(false); - return $this->getCulture() == '' ? - ($app ? $app->getCulture() : 'en') : $this->getCulture(); - } - - /** - * @return DateTimeFormatInfo date time format information for the current culture. - */ - protected function getLocalizedCalendarInfo() - { - //expensive operations - $culture = $this->getCurrentCulture(); - Prado::using('System.I18N.core.DateTimeFormatInfo'); - $info = Prado::createComponent('System.I18N.core.CultureInfo', $culture); - return $info->getDateTimeFormat(); - } - - /** - * Renders the drop down list date picker. - */ - protected function renderDropDownListCalendar($writer) - { - if($this->getMode() == TDatePickerMode::Basic) - $this->setMode(TDatePickerMode::ImageButton); - parent::addAttributesToRender($writer); - $writer->removeAttribute('name'); - $writer->removeAttribute('type'); - $writer->addAttribute('id', $this->getClientID()); - - if(strlen($class = $this->getCssClass()) > 0) - $writer->addAttribute('class', $class); - $writer->renderBeginTag('span'); - - $s = Prado::createComponent('System.Util.TDateTimeStamp'); - $date = $s->getDate($this->getTimeStampFromText()); - //$date = @getdate($this->getTimeStampFromText()); - - $this->renderCalendarSelections($writer, $date); - - //render a hidden input field - $writer->addAttribute('name', $this->getUniqueID()); - $writer->addAttribute('type', 'hidden'); - $writer->addAttribute('value', $this->getText()); - $writer->renderBeginTag('input'); - - $writer->renderEndTag(); - $writer->renderEndTag(); - } - - protected function hasDayPattern() - { - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', - $this->getDateFormat()); - return ($formatter->getDayPattern()!==null); - } - - /** - * Renders the calendar drop down list depending on the DateFormat pattern. - * @param THtmlWriter the Html writer to render the drop down lists. - * @param array the current selected date - */ - protected function renderCalendarSelections($writer, $date) - { - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', - $this->getDateFormat()); - - foreach($formatter->getDayMonthYearOrdering() as $type) - { - if($type == 'day') - $this->renderCalendarDayOptions($writer,$date['mday']); - elseif($type == 'month') - $this->renderCalendarMonthOptions($writer,$date['mon']); - elseif($type == 'year') - $this->renderCalendarYearOptions($writer,$date['year']); - } - } - - /** - * Gets the date from the text input using TSimpleDateFormatter - * @return integer current selected date timestamp - */ - protected function getTimeStampFromText() - { - $pattern = $this->getDateFormat(); - $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$pattern); - return $formatter->parse($this->getText()); - } - - /** - * Renders a drop down lists. - * @param THtmlWriter the writer used for the rendering purpose - * @param array list of selection options - * @param mixed selected key. - */ - private function renderDropDownListOptions($writer,$options,$selected=null) - { - foreach($options as $k => $v) - { - $writer->addAttribute('value', $k); - if($k == $selected) - $writer->addAttribute('selected', 'selected'); - $writer->renderBeginTag('option'); - $writer->write($v); - $writer->renderEndTag(); - } - } - - /** - * Renders the day drop down list options. - * @param THtmlWriter the writer used for the rendering purpose - * @param mixed selected day. - */ - protected function renderCalendarDayOptions($writer, $selected=null) - { - $days = $this->getDropDownDayOptions(); - $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'day'); - $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'day'); - $writer->addAttribute('class', 'datepicker_day_options'); - if($this->getReadOnly() || !$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); - $writer->renderBeginTag('select'); - $this->renderDropDownListOptions($writer, $days, $selected); - $writer->renderEndTag(); - } - - /** - * @return array list of day options for a drop down list. - */ - protected function getDropDownDayOptions() - { - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', - $this->getDateFormat()); - $days = array(); - $requiresPadding = $formatter->getDayPattern() === 'dd'; - for($i=1;$i<=31;$i++) - { - $days[$i] = $requiresPadding ? str_pad($i, 2, '0', STR_PAD_LEFT) : $i; - } - return $days; - } - - /** - * Renders the month drop down list options. - * @param THtmlWriter the writer used for the rendering purpose - * @param mixed selected month. - */ - protected function renderCalendarMonthOptions($writer, $selected=null) - { - $info = $this->getLocalizedCalendarInfo(); - $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'month'); - $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'month'); - $writer->addAttribute('class', 'datepicker_month_options'); - if($this->getReadOnly() || !$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); - $writer->renderBeginTag('select'); - $this->renderDropDownListOptions($writer, - $this->getLocalizedMonthNames($info), $selected-1); - $writer->renderEndTag(); - } - - /** - * Returns the localized month names that depends on the month format pattern. - * "MMMM" will return the month names, "MM" or "MMM" return abbr. month names - * and "M" return month digits. - * @param DateTimeFormatInfo localized date format information. - * @return array localized month names. - */ - protected function getLocalizedMonthNames($info) - { - $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', - $this->getDateFormat()); - switch($formatter->getMonthPattern()) - { - case 'MMM': return $info->getAbbreviatedMonthNames(); - case 'MM': - $array = array(); - for($i=1;$i<=12;$i++) - $array[$i-1] = $i < 10 ? '0'.$i : $i; - return $array; - case 'M': - $array = array(); for($i=1;$i<=12;$i++) $array[$i-1] = $i; - return $array; - default : return $info->getMonthNames(); - } - } - - /** - * Renders the year drop down list options. - * @param THtmlWriter the writer used for the rendering purpose - * @param mixed selected year. - */ - protected function renderCalendarYearOptions($writer, $selected=null) - { - $years = array(); - for($i = $this->getFromYear(); $i <= $this->getUpToYear(); $i++) - $years[$i] = $i; - $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'year'); - $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'year'); - if($this->getReadOnly() || !$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); - $writer->renderBeginTag('select'); - $writer->addAttribute('class', 'datepicker_year_options'); - $this->renderDropDownListOptions($writer, $years, $selected); - $writer->renderEndTag(); - } - - /** - * Gets the ID for the date picker trigger button. - * @return string unique button ID - */ - protected function getDatePickerButtonID() - { - return $this->getClientID().'button'; - } - - /** - * Adds an additional button such that when clicked it shows the date picker. - * @return THtmlWriter writer - */ - protected function renderButtonDatePicker($writer) - { - $writer->addAttribute('id', $this->getDatePickerButtonID()); - $writer->addAttribute('type', 'button'); - $writer->addAttribute('class', $this->getCssClass().' TDatePickerButton'); - $writer->addAttribute('value',$this->getButtonText()); - if(!$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); - $writer->renderBeginTag("input"); - $writer->renderEndTag(); - } - - /** - * Adds an additional image button such that when clicked it shows the date picker. - * @return THtmlWriter writer - */ - protected function renderImageButtonDatePicker($writer) - { - $url = $this->getButtonImageUrl(); - $url = empty($url) ? $this->getAssetUrl('calendar.png') : $url; - $writer->addAttribute('id', $this->getDatePickerButtonID()); - $writer->addAttribute('src', $url); - $writer->addAttribute('alt', ' '); - $writer->addAttribute('class', $this->getCssClass().' TDatePickerImageButton'); - if(!$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); - $writer->addAttribute('type', 'image'); - $writer->addAttribute('onclick', 'return false;'); - $writer->renderBeginTag('input'); - $writer->renderEndTag(); - } - - /** - * @param string date picker asset file in the self::SCRIPT_PATH directory. - * @return string date picker asset url. - */ - protected function getAssetUrl($file='') - { - $base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl(); - return $base.'/'.self::SCRIPT_PATH.'/'.$file; - } - - /** - * Publish the calendar style Css asset file. - * @return string Css file url. - */ - protected function publishCalendarStyle() - { - $url = $this->getAssetUrl($this->getCalendarStyle().'.css'); - $cs = $this->getPage()->getClientScript(); - if(!$cs->isStyleSheetFileRegistered($url)) - $cs->registerStyleSheetFile($url, $url); - return $url; - } - - /** - * Add the client id to the input textbox, and register the client scripts. - * @param THtmlWriter writer - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - $writer->addAttribute('id',$this->getClientID()); - $this->registerCalendarClientScriptPost(); - } - - - /** - * Registers the javascript code to initialize the date picker. - */ - protected function registerCalendarClientScriptPre() - { - if($this->getShowCalendar()) - { - $cs = $this->getPage()->getClientScript(); - $cs->registerPradoScript("datepicker"); - } - } - - protected function registerCalendarClientScriptPost() - { - if($this->getShowCalendar()) - { - $cs = $this->getPage()->getClientScript(); - if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) - { - $spacer = $this->getAssetUrl('spacer.gif'); - $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; - $cs->registerEndScript('TDatePicker.spacer', $code); - } - - $options = TJavaScript::encode($this->getDatePickerOptions()); - $code = "new Prado.WebUI.TDatePicker($options);"; - $cs->registerEndScript("prado:".$this->getClientID(), $code); - } - } -} - -/** - * TDatePickerClientScript class. - * - * Client-side date picker event {@link setOnDateChanged OnDateChanged} - * can be modified through the {@link TDatePicker::getClientSide ClientSide} - * property of a date picker. - * - * The OnDateChanged event is raise when the date picker's date - * is changed. - * The formatted date according to {@link TDatePicker::getDateFormat DateFormat} is sent - * as parameter to this event - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDatePickerClientScript extends TClientSideOptions -{ - /** - * Javascript code to execute when the date picker's date is changed. - * @param string javascript code - */ - public function setOnDateChanged($javascript) - { - $this->setFunction('OnDateChanged', $javascript); - } - - /** - * @return string javascript code to execute when the date picker's date is changed. - */ - public function getOnDateChanged() - { - return $this->getOption('OnDateChanged'); - } -} - - -/** - * TDatePickerInputMode class. - * TDatePickerInputMode defines the enumerable type for the possible datepicker input methods. - * - * The following enumerable values are defined: - * - TextBox: text boxes are used to input date values - * - DropDownList: dropdown lists are used to pick up date values - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDatePickerInputMode extends TEnumerable -{ - const TextBox='TextBox'; - const DropDownList='DropDownList'; -} - -/** - * TDatePickerMode class. - * TDatePickerMode defines the enumerable type for the possible UI mode - * that a {@link TDatePicker} control can take. - * - * The following enumerable values are defined: - * - Basic: Only shows a text input, focusing on the input shows the date picker - * - Clickable: Only shows a text input, clicking on the input shows the date picker (since 3.2) - * - Button: Shows a button next to the text input, clicking on the button shows the date, button text can be by the - * - ImageButton: Shows an image next to the text input, clicking on the image shows the date picker, - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDatePickerMode extends TEnumerable -{ - const Basic='Basic'; - const Clickable='Clickable'; - const Button='Button'; - const ImageButton='ImageButton'; -} - -/** - * TDatePickerPositionMode class. - * TDatePickerPositionMode defines the positions available for the calendar popup, relative to the corresponding input. - * - * The following enumerable values are defined: - * - Top: the date picker is placed above the input field - * - Bottom: the date picker is placed below the input field - * - * @author Carl G. Mathisen - * @package System.Web.UI.WebControls - * @since 3.1.4 - */ -class TDatePickerPositionMode extends TEnumerable -{ - const Top='Top'; - const Bottom='Bottom'; -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTextBox class + */ +Prado::using('System.Web.UI.WebControls.TTextBox'); + +/** + * + * TDatePicker class. + * + * TDatePicker displays a text box for date input purpose. + * When the text box receives focus, a calendar will pop up and users can + * pick up from it a date that will be automatically entered into the text box. + * The format of the date string displayed in the text box is determined by + * the DateFormat property. Valid formats are the combination of the + * following tokens, + * + * + * Character Format Pattern (en-US) + * ----------------------------------------- + * d day digit + * dd padded day digit e.g. 01, 02 + * M month digit + * MM padded month digit + * MMMM localized month name, e.g. March, April + * yy 2 digit year + * yyyy 4 digit year + * ----------------------------------------- + * + * + * TDatePicker has four Mode to show the date picker popup. + * + * # Basic -- Only shows a text input, focusing on the input shows the + * date picker. This way you can access the popup using only + * the keyboard. Note that because of this, TAB-bing through + * this control will automatically select the current date if + * no previous date was selected. If you close the popup (eg. + * pressing the ESC key) you'll need to un-focus and re-focus + * the control again for the popup to reappear. + * # Clickable -- Only shows a text input, clicking on the input shows the + * date picker. This mode solves the two small problems of the + * Basic mode. It was first introduced in Prado 3.2. + * # Button -- Shows a button next to the text input, clicking on the + * button shows the date, button text can be by the + * ButtonText property + * # ImageButton -- Shows an image next to the text input, clicking on + * the image shows the date picker, image source can be + * change through the ButtonImageUrl property. + * + * The CssClass property can be used to override the css class name + * for the date picker panel. CalendarStyle property sets the packages + * styles available. E.g. default. + * + * The InputMode property can be set to "TextBox" or "DropDownList" with + * default as "TextBox". + * In DropDownList mode, in addition to the popup date picker, three + * drop down list (day, month and year) are presented to select the date . + * + * The PositionMode property can be set to "Top" or "Bottom" with default + * as "Bottom". It specifies the position of the calendar popup, relative to the + * input field. + * + * @author Wei Zhuo + * @author Carl G. Mathisen + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDatePicker extends TTextBox +{ + /** + * Script path relative to the TClientScriptManager::SCRIPT_PATH + */ + const SCRIPT_PATH = 'prado/datepicker'; + + /** + * @var TDatePickerClientScript validator client-script options. + */ + private $_clientScript; + /** + * AutoPostBack is not supported. + */ + public function setAutoPostBack($value) + { + throw new TNotSupportedException('tdatepicker_autopostback_unsupported', + get_class($this)); + } + + /** + * @return string the format of the date string + */ + public function getDateFormat() + { + return $this->getViewState('DateFormat','dd-MM-yyyy'); + } + + /** + * Sets the format of the date string. + * @param string the format of the date string + */ + public function setDateFormat($value) + { + $this->setViewState('DateFormat',$value,'dd-MM-yyyy'); + } + + /** + * @return boolean whether the calendar window should pop up when the control receives focus + */ + public function getShowCalendar() + { + return $this->getViewState('ShowCalendar',true); + } + + /** + * Sets whether to pop up the calendar window when the control receives focus + * @param boolean whether to show the calendar window + */ + public function setShowCalendar($value) + { + $this->setViewState('ShowCalendar',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Gets the current culture. + * @return string current culture, e.g. en_AU. + */ + public function getCulture() + { + return $this->getViewState('Culture', ''); + } + + /** + * Sets the culture/language for the date picker. + * @param string a culture string, e.g. en_AU. + */ + public function setCulture($value) + { + $this->setViewState('Culture', $value, ''); + } + + /** + * @param TDatePickerInputMode input method of date values + */ + public function setInputMode($value) + { + $this->setViewState('InputMode', TPropertyValue::ensureEnum($value, 'TDatePickerInputMode'), TDatePickerInputMode::TextBox); + } + + /** + * @return TDatePickerInputMode input method of date values. Defaults to TDatePickerInputMode::TextBox. + */ + public function getInputMode() + { + return $this->getViewState('InputMode', TDatePickerInputMode::TextBox); + } + + /** + * @param TDatePickerMode calendar UI mode + */ + public function setMode($value) + { + $this->setViewState('Mode', TPropertyValue::ensureEnum($value, 'TDatePickerMode'), TDatePickerMode::Basic); + } + + /** + * @return TDatePickerMode current calendar UI mode. + */ + public function getMode() + { + return $this->getViewState('Mode', TDatePickerMode::Basic); + } + /** + * @param string the image url for "Image" UI mode. + */ + public function setButtonImageUrl($value) + { + $this->setViewState('ImageUrl', $value, ''); + } + + /** + * @return string the image url for "Image" UI mode. + */ + public function getButtonImageUrl() + { + return $this->getViewState('ImageUrl', ''); + } + + /** + * @param string set the calendar style + */ + public function setCalendarStyle($value) + { + $this->setViewState('CalendarStyle', $value, 'default'); + } + + /** + * @return string current calendar style + */ + public function getCalendarStyle() + { + return $this->getViewState('CalendarStyle', 'default'); + } + + /** + * Set the first day of week, with 0 as Sunday, 1 as Monday, etc. + * @param integer 0 for Sunday, 1 for Monday, 2 for Tuesday, etc. + */ + public function setFirstDayOfWeek($value) + { + $this->setViewState('FirstDayOfWeek', TPropertyValue::ensureInteger($value), 1); + } + + /** + * @return integer first day of the week + */ + public function getFirstDayOfWeek() + { + return $this->getViewState('FirstDayOfWeek', 1); + } + + /** + * @return string text for the date picker button. Default is "...". + */ + public function getButtonText() + { + return $this->getViewState('ButtonText', '...'); + } + + /** + * @param string text for the date picker button + */ + public function setButtonText($value) + { + $this->setViewState('ButtonText', $value, '...'); + } + + /** + * @param integer date picker starting year, default is 2000. + */ + public function setFromYear($value) + { + $this->setViewState('FromYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))-5); + } + + /** + * @return integer date picker starting year, default is -5 years + */ + public function getFromYear() + { + return $this->getViewState('FromYear', intval(@date('Y'))-5); + } + + /** + * @param integer date picker ending year, default +10 years + */ + public function setUpToYear($value) + { + $this->setViewState('UpToYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))+10); + } + + /** + * @return integer date picker ending year, default +10 years + */ + public function getUpToYear() + { + return $this->getViewState('UpToYear', intval(@date('Y'))+10); + } + + /** + * @param TDatePickerPositionMode calendar UI position + */ + public function setPositionMode($value) + { + $this->setViewState('PositionMode', TPropertyValue::ensureEnum($value, 'TDatePickerPositionMode'), TDatePickerPositionMode::Bottom); + } + + /** + * @return TDatePickerPositionMode current calendar UI position. + */ + public function getPositionMode() + { + return $this->getViewState('PositionMode', TDatePickerPositionMode::Bottom); + } + + /** + * @return integer current selected date from the date picker as timestamp, NULL if timestamp is not set previously. + */ + public function getTimeStamp() + { + if(trim($this->getText())==='') + return null; + else + return $this->getTimeStampFromText(); + } + + /** + * Sets the date for the date picker using timestamp. + * @param float time stamp for the date picker + */ + public function setTimeStamp($value) + { + if($value===null || (is_string($value) && trim($value)==='')) + $this->setText(''); + else + { + $date = TPropertyValue::ensureFloat($value); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$this->getDateFormat()); + $this->setText($formatter->format($date)); + } + } + + /** + * Returns the timestamp selected by the user. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getTimeStamp()}. + * @return integer the timestamp of the TDatePicker control. + * @see getTimeStamp + * @since 3.1.2 + */ + public function getData() + { + return $this->getTimeStamp(); + } + + /** + * Sets the timestamp represented by this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setTimeStamp()}. + * @param integer the timestamp of the TDatePicker control. + * @see setTimeStamp + * @since 3.1.2 + */ + public function setData($value) + { + $this->setTimeStamp($value); + } + + /** + * @return string the date string. + */ + public function getDate() + { + return $this->getText(); + } + + /** + * @param string date string + */ + public function setDate($value) + { + $this->setText($value); + } + + /** + * Gets the TDatePickerClientScript to set the TDatePicker event handlers. + * + * The date picker on the client-side supports the following events. + * # OnDateChanged -- raised when the date is changed. + * + * You can attach custom javascript code to each of these events + * + * @return TDatePickerClientScript javascript validator event options. + */ + public function getClientSide() + { + if($this->_clientScript===null) + $this->_clientScript = $this->createClientScript(); + return $this->_clientScript; + } + + /** + * @return TDatePickerClientScript javascript validator event options. + */ + protected function createClientScript() + { + return new TDatePickerClientScript; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return integer the interger timestamp if valid, otherwise the original text. + */ + public function getValidationPropertyValue() + { + if(($text = $this->getText()) === '') + return ''; + $date = $this->getTimeStamp(); + return $date == null ? $text : $date; + } + + /** + * Publish the date picker Css asset files. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getInputMode() === TDatePickerInputMode::DropDownList) + { + $page = $this->getPage(); + $uniqueID = $this->getUniqueID(); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'day'); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'month'); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'year'); + } + $this->publishCalendarStyle(); + $this->registerCalendarClientScriptPre(); + } + + /** + * Renders body content. + * This method overrides parent implementation by adding + * additional date picker button if Mode is Button or ImageButton. + * @param THtmlWriter writer + */ + public function render($writer) + { + if($this->getInputMode() == TDatePickerInputMode::TextBox) + { + parent::render($writer); + $this->renderDatePickerButtons($writer); + } + else + { + $this->renderDropDownListCalendar($writer); + if($this->hasDayPattern()) + { + $this->registerCalendarClientScriptPost(); + $this->renderDatePickerButtons($writer); + } + } + } + + /** + * Renders the date picker popup buttons. + */ + protected function renderDatePickerButtons($writer) + { + if($this->getShowCalendar()) + { + switch ($this->getMode()) + { + case TDatePickerMode::Button: + $this->renderButtonDatePicker($writer); + break; + case TDatePickerMode::ImageButton : + $this->renderImageButtonDatePicker($writer); + break; + } + } + } + + /** + * Loads user input data. Override parent implementation, when InputMode + * is DropDownList call getDateFromPostData to get date data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if($this->getInputMode() == TDatePickerInputMode::TextBox) + return parent::loadPostData($key, $values); + $value = $this->getDateFromPostData($key, $values); + if(!$this->getReadOnly() && $this->getText()!==$value) + { + $this->setText($value); + return true; + } + else + return false; + } + + /** + * Loads date from drop down list data. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return array the date selected + */ + protected function getDateFromPostData($key, $values) + { + $date = @getdate(); + + if(isset($values[$key.'$day'])) + $day = intval($values[$key.'$day']); + else + $day = $date['mday']; + + if(isset($values[$key.'$month'])) + $month = intval($values[$key.'$month']) + 1; + else + $month = $date['mon']; + + if(isset($values[$key.'$year'])) + $year = intval($values[$key.'$year']); + else + $year = $date['year']; + + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getTimeStamp(0, 0, 0, $month, $day, $year); + //$date = @mktime(0, 0, 0, $month, $day, $year); + + $pattern = $this->getDateFormat(); + $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $pattern); + return $formatter->format($date); + } + + /** + * Get javascript date picker options. + * @return array date picker client-side options + */ + protected function getDatePickerOptions() + { + $options['ID'] = $this->getClientID(); + $options['InputMode'] = $this->getInputMode(); + $options['Format'] = $this->getDateFormat(); + $options['FirstDayOfWeek'] = $this->getFirstDayOfWeek(); + if(($cssClass=$this->getCssClass())!=='') + $options['ClassName'] = $cssClass; + $options['CalendarStyle'] = $this->getCalendarStyle(); + $options['FromYear'] = $this->getFromYear(); + $options['UpToYear'] = $this->getUpToYear(); + switch($this->getMode()) + { + case TDatePickerMode::Basic: + break; + case TDatePickerMode::Clickable: + $options['TriggerEvent'] = "click"; + break; + default: + $options['Trigger'] = $this->getDatePickerButtonID(); + break; + } + $options['PositionMode'] = $this->getPositionMode(); + + $options = array_merge($options, $this->getCulturalOptions()); + if($this->_clientScript!==null) + $options = array_merge($options, + $this->_clientScript->getOptions()->toArray()); + return $options; + } + + /** + * Get javascript localization options, e.g. month and weekday names. + * @return array localization options. + */ + protected function getCulturalOptions() + { + if($this->getCurrentCulture() == 'en') + return array(); + + $date = $this->getLocalizedCalendarInfo(); + $options['MonthNames'] = $date->getMonthNames(); + $options['AbbreviatedMonthNames'] = $date->getAbbreviatedMonthNames(); + $options['ShortWeekDayNames'] = $date->getAbbreviatedDayNames(); + + return $options; + } + + /** + * @return string the current culture, falls back to application if culture is not set. + */ + protected function getCurrentCulture() + { + $app = $this->getApplication()->getGlobalization(false); + return $this->getCulture() == '' ? + ($app ? $app->getCulture() : 'en') : $this->getCulture(); + } + + /** + * @return DateTimeFormatInfo date time format information for the current culture. + */ + protected function getLocalizedCalendarInfo() + { + //expensive operations + $culture = $this->getCurrentCulture(); + Prado::using('System.I18N.core.DateTimeFormatInfo'); + $info = Prado::createComponent('System.I18N.core.CultureInfo', $culture); + return $info->getDateTimeFormat(); + } + + /** + * Renders the drop down list date picker. + */ + protected function renderDropDownListCalendar($writer) + { + if($this->getMode() == TDatePickerMode::Basic) + $this->setMode(TDatePickerMode::ImageButton); + parent::addAttributesToRender($writer); + $writer->removeAttribute('name'); + $writer->removeAttribute('type'); + $writer->addAttribute('id', $this->getClientID()); + + if(strlen($class = $this->getCssClass()) > 0) + $writer->addAttribute('class', $class); + $writer->renderBeginTag('span'); + + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getDate($this->getTimeStampFromText()); + //$date = @getdate($this->getTimeStampFromText()); + + $this->renderCalendarSelections($writer, $date); + + //render a hidden input field + $writer->addAttribute('name', $this->getUniqueID()); + $writer->addAttribute('type', 'hidden'); + $writer->addAttribute('value', $this->getText()); + $writer->renderBeginTag('input'); + + $writer->renderEndTag(); + $writer->renderEndTag(); + } + + protected function hasDayPattern() + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + return ($formatter->getDayPattern()!==null); + } + + /** + * Renders the calendar drop down list depending on the DateFormat pattern. + * @param THtmlWriter the Html writer to render the drop down lists. + * @param array the current selected date + */ + protected function renderCalendarSelections($writer, $date) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + + foreach($formatter->getDayMonthYearOrdering() as $type) + { + if($type == 'day') + $this->renderCalendarDayOptions($writer,$date['mday']); + elseif($type == 'month') + $this->renderCalendarMonthOptions($writer,$date['mon']); + elseif($type == 'year') + $this->renderCalendarYearOptions($writer,$date['year']); + } + } + + /** + * Gets the date from the text input using TSimpleDateFormatter + * @return integer current selected date timestamp + */ + protected function getTimeStampFromText() + { + $pattern = $this->getDateFormat(); + $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$pattern); + return $formatter->parse($this->getText()); + } + + /** + * Renders a drop down lists. + * @param THtmlWriter the writer used for the rendering purpose + * @param array list of selection options + * @param mixed selected key. + */ + private function renderDropDownListOptions($writer,$options,$selected=null) + { + foreach($options as $k => $v) + { + $writer->addAttribute('value', $k); + if($k == $selected) + $writer->addAttribute('selected', 'selected'); + $writer->renderBeginTag('option'); + $writer->write($v); + $writer->renderEndTag(); + } + } + + /** + * Renders the day drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected day. + */ + protected function renderCalendarDayOptions($writer, $selected=null) + { + $days = $this->getDropDownDayOptions(); + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'day'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'day'); + $writer->addAttribute('class', 'datepicker_day_options'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $this->renderDropDownListOptions($writer, $days, $selected); + $writer->renderEndTag(); + } + + /** + * @return array list of day options for a drop down list. + */ + protected function getDropDownDayOptions() + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + $days = array(); + $requiresPadding = $formatter->getDayPattern() === 'dd'; + for($i=1;$i<=31;$i++) + { + $days[$i] = $requiresPadding ? str_pad($i, 2, '0', STR_PAD_LEFT) : $i; + } + return $days; + } + + /** + * Renders the month drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected month. + */ + protected function renderCalendarMonthOptions($writer, $selected=null) + { + $info = $this->getLocalizedCalendarInfo(); + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'month'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'month'); + $writer->addAttribute('class', 'datepicker_month_options'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $this->renderDropDownListOptions($writer, + $this->getLocalizedMonthNames($info), $selected-1); + $writer->renderEndTag(); + } + + /** + * Returns the localized month names that depends on the month format pattern. + * "MMMM" will return the month names, "MM" or "MMM" return abbr. month names + * and "M" return month digits. + * @param DateTimeFormatInfo localized date format information. + * @return array localized month names. + */ + protected function getLocalizedMonthNames($info) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + switch($formatter->getMonthPattern()) + { + case 'MMM': return $info->getAbbreviatedMonthNames(); + case 'MM': + $array = array(); + for($i=1;$i<=12;$i++) + $array[$i-1] = $i < 10 ? '0'.$i : $i; + return $array; + case 'M': + $array = array(); for($i=1;$i<=12;$i++) $array[$i-1] = $i; + return $array; + default : return $info->getMonthNames(); + } + } + + /** + * Renders the year drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected year. + */ + protected function renderCalendarYearOptions($writer, $selected=null) + { + $years = array(); + for($i = $this->getFromYear(); $i <= $this->getUpToYear(); $i++) + $years[$i] = $i; + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'year'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'year'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $writer->addAttribute('class', 'datepicker_year_options'); + $this->renderDropDownListOptions($writer, $years, $selected); + $writer->renderEndTag(); + } + + /** + * Gets the ID for the date picker trigger button. + * @return string unique button ID + */ + protected function getDatePickerButtonID() + { + return $this->getClientID().'button'; + } + + /** + * Adds an additional button such that when clicked it shows the date picker. + * @return THtmlWriter writer + */ + protected function renderButtonDatePicker($writer) + { + $writer->addAttribute('id', $this->getDatePickerButtonID()); + $writer->addAttribute('type', 'button'); + $writer->addAttribute('class', $this->getCssClass().' TDatePickerButton'); + $writer->addAttribute('value',$this->getButtonText()); + if(!$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag("input"); + $writer->renderEndTag(); + } + + /** + * Adds an additional image button such that when clicked it shows the date picker. + * @return THtmlWriter writer + */ + protected function renderImageButtonDatePicker($writer) + { + $url = $this->getButtonImageUrl(); + $url = empty($url) ? $this->getAssetUrl('calendar.png') : $url; + $writer->addAttribute('id', $this->getDatePickerButtonID()); + $writer->addAttribute('src', $url); + $writer->addAttribute('alt', ' '); + $writer->addAttribute('class', $this->getCssClass().' TDatePickerImageButton'); + if(!$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->addAttribute('type', 'image'); + $writer->addAttribute('onclick', 'return false;'); + $writer->renderBeginTag('input'); + $writer->renderEndTag(); + } + + /** + * @param string date picker asset file in the self::SCRIPT_PATH directory. + * @return string date picker asset url. + */ + protected function getAssetUrl($file='') + { + $base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl(); + return $base.'/'.self::SCRIPT_PATH.'/'.$file; + } + + /** + * Publish the calendar style Css asset file. + * @return string Css file url. + */ + protected function publishCalendarStyle() + { + $url = $this->getAssetUrl($this->getCalendarStyle().'.css'); + $cs = $this->getPage()->getClientScript(); + if(!$cs->isStyleSheetFileRegistered($url)) + $cs->registerStyleSheetFile($url, $url); + return $url; + } + + /** + * Add the client id to the input textbox, and register the client scripts. + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + $this->registerCalendarClientScriptPost(); + } + + + /** + * Registers the javascript code to initialize the date picker. + */ + protected function registerCalendarClientScriptPre() + { + if($this->getShowCalendar()) + { + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript("datepicker"); + } + } + + protected function registerCalendarClientScriptPost() + { + if($this->getShowCalendar()) + { + $cs = $this->getPage()->getClientScript(); + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) + { + $spacer = $this->getAssetUrl('spacer.gif'); + $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; + $cs->registerEndScript('TDatePicker.spacer', $code); + } + + $options = TJavaScript::encode($this->getDatePickerOptions()); + $code = "new Prado.WebUI.TDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); + } + } +} + +/** + * TDatePickerClientScript class. + * + * Client-side date picker event {@link setOnDateChanged OnDateChanged} + * can be modified through the {@link TDatePicker::getClientSide ClientSide} + * property of a date picker. + * + * The OnDateChanged event is raise when the date picker's date + * is changed. + * The formatted date according to {@link TDatePicker::getDateFormat DateFormat} is sent + * as parameter to this event + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerClientScript extends TClientSideOptions +{ + /** + * Javascript code to execute when the date picker's date is changed. + * @param string javascript code + */ + public function setOnDateChanged($javascript) + { + $this->setFunction('OnDateChanged', $javascript); + } + + /** + * @return string javascript code to execute when the date picker's date is changed. + */ + public function getOnDateChanged() + { + return $this->getOption('OnDateChanged'); + } +} + + +/** + * TDatePickerInputMode class. + * TDatePickerInputMode defines the enumerable type for the possible datepicker input methods. + * + * The following enumerable values are defined: + * - TextBox: text boxes are used to input date values + * - DropDownList: dropdown lists are used to pick up date values + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerInputMode extends TEnumerable +{ + const TextBox='TextBox'; + const DropDownList='DropDownList'; +} + +/** + * TDatePickerMode class. + * TDatePickerMode defines the enumerable type for the possible UI mode + * that a {@link TDatePicker} control can take. + * + * The following enumerable values are defined: + * - Basic: Only shows a text input, focusing on the input shows the date picker + * - Clickable: Only shows a text input, clicking on the input shows the date picker (since 3.2) + * - Button: Shows a button next to the text input, clicking on the button shows the date, button text can be by the + * - ImageButton: Shows an image next to the text input, clicking on the image shows the date picker, + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerMode extends TEnumerable +{ + const Basic='Basic'; + const Clickable='Clickable'; + const Button='Button'; + const ImageButton='ImageButton'; +} + +/** + * TDatePickerPositionMode class. + * TDatePickerPositionMode defines the positions available for the calendar popup, relative to the corresponding input. + * + * The following enumerable values are defined: + * - Top: the date picker is placed above the input field + * - Bottom: the date picker is placed below the input field + * + * @author Carl G. Mathisen + * @package System.Web.UI.WebControls + * @since 3.1.4 + */ +class TDatePickerPositionMode extends TEnumerable +{ + const Top='Top'; + const Bottom='Bottom'; +} diff --git a/framework/Web/UI/WebControls/TDropDownList.php b/framework/Web/UI/WebControls/TDropDownList.php index 0eba6285..57f1f165 100644 --- a/framework/Web/UI/WebControls/TDropDownList.php +++ b/framework/Web/UI/WebControls/TDropDownList.php @@ -1,154 +1,154 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TListControl class - */ -Prado::using('System.Web.UI.WebControls.TListControl'); - -/** - * TDropDownList class - * - * TDropDownList displays a dropdown list on a Web page. - * It inherits all properties and events from {@link TListControl}. - * - * Since v3.0.3, TDropDownList starts to support optgroup. To specify an option group for - * a list item, set a Group attribute with it, - * - * $listitem->Attributes->Group="Group Name"; - * // or in template - * - * - * Since v3.1.1, TDropDownList starts to support prompt text. That is, a prompt item can be - * displayed as the first list item by specifying either {@link setPromptText PromptText} or - * {@link setPromptValue PromptValue}, or both. Choosing the prompt item will unselect the TDropDownList. - * - * When a prompt item is set, its index in the list is set to -1. So, the {@link getSelectedIndex SelectedIndex} - * property is not affected by a prompt item: the items list will still be zero-based. - * - * The {@link clearSelection clearSelection} method will select the prompt item if existing, otherway the first - * available item in the dropdown list will be selected. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TDropDownList extends TListControl implements IPostBackDataHandler, IValidatable -{ - private $_dataChanged=false; - private $_isValid=true; - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - $writer->addAttribute('name',$this->getUniqueID()); - parent::addAttributesToRender($writer); - } - - /** - * 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.TDropDownList'; - } - - /** - * Loads user input data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - if(!$this->getEnabled(true)) - return false; - $this->ensureDataBound(); - $selection=isset($values[$key])?$values[$key]:null; - if($selection!==null) - { - $index=$this->getItems()->findIndexByValue($selection,false); - if($this->getSelectedIndex()!==$index) - { - $this->setSelectedIndex($index); - return $this->_dataChanged=true; - } - } - return false; - } - - /** - * Raises postdata changed event. - * This method is required by {@link IPostBackDataHandler} interface. - * It is invoked by the framework when {@link getSelectedIndex SelectedIndex} property - * is changed on postback. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - if($this->getAutoPostBack() && $this->getCausesValidation()) - $this->getPage()->validate($this->getValidationGroup()); - $this->onSelectedIndexChanged(null); - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * @throws TNotSupportedException if this method is invoked - */ - public function setSelectedIndices($indices) - { - throw new TNotSupportedException('dropdownlist_selectedindices_unsupported'); - } - - /** - * Returns the value to be validated. - * This methid is required by IValidatable interface. - * @return mixed the value of the property to be validated. - */ - public function getValidationPropertyValue() - { - return $this->getSelectedValue(); - } - - /** - * Returns true if this control validated successfully. - * Defaults to true. - * @return bool wether this control validated successfully. - */ - public function getIsValid() - { - return $this->_isValid; - } - /** - * @param bool wether this control is valid. - */ - public function setIsValid($value) - { - $this->_isValid=TPropertyValue::ensureBoolean($value); - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TListControl class + */ +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * TDropDownList class + * + * TDropDownList displays a dropdown list on a Web page. + * It inherits all properties and events from {@link TListControl}. + * + * Since v3.0.3, TDropDownList starts to support optgroup. To specify an option group for + * a list item, set a Group attribute with it, + * + * $listitem->Attributes->Group="Group Name"; + * // or in template + * + * + * Since v3.1.1, TDropDownList starts to support prompt text. That is, a prompt item can be + * displayed as the first list item by specifying either {@link setPromptText PromptText} or + * {@link setPromptValue PromptValue}, or both. Choosing the prompt item will unselect the TDropDownList. + * + * When a prompt item is set, its index in the list is set to -1. So, the {@link getSelectedIndex SelectedIndex} + * property is not affected by a prompt item: the items list will still be zero-based. + * + * The {@link clearSelection clearSelection} method will select the prompt item if existing, otherway the first + * available item in the dropdown list will be selected. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDropDownList extends TListControl implements IPostBackDataHandler, IValidatable +{ + private $_dataChanged=false; + private $_isValid=true; + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('name',$this->getUniqueID()); + parent::addAttributesToRender($writer); + } + + /** + * 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.TDropDownList'; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if(!$this->getEnabled(true)) + return false; + $this->ensureDataBound(); + $selection=isset($values[$key])?$values[$key]:null; + if($selection!==null) + { + $index=$this->getItems()->findIndexByValue($selection,false); + if($this->getSelectedIndex()!==$index) + { + $this->setSelectedIndex($index); + return $this->_dataChanged=true; + } + } + return false; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getSelectedIndex SelectedIndex} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onSelectedIndexChanged(null); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @throws TNotSupportedException if this method is invoked + */ + public function setSelectedIndices($indices) + { + throw new TNotSupportedException('dropdownlist_selectedindices_unsupported'); + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getSelectedValue(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } +} diff --git a/framework/Web/UI/WebControls/TDropDownListColumn.php b/framework/Web/UI/WebControls/TDropDownListColumn.php index ffbe8f70..941a9be1 100644 --- a/framework/Web/UI/WebControls/TDropDownListColumn.php +++ b/framework/Web/UI/WebControls/TDropDownListColumn.php @@ -1,321 +1,321 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TDataGridColumn'); -Prado::using('System.Web.UI.WebControls.TDropDownList'); - -/** - * TDropDownListColumn class - * - * TDropDownListColumn represents a column that is bound to a field in a data source. - * The cells in the column will be displayed using the data indexed by - * {@link setDataTextField DataTextField}. You can customize the display by - * setting {@link setDataTextFormatString DataTextFormatString}. - * - * If {@link setReadOnly ReadOnly} is false, TDropDownListColumn will display cells in edit mode - * with dropdown lists. Otherwise, a static text is displayed. - * The currently selected dropndown list item is specified by the data indexed with - * {@link setDataValueField DataValueField}. - * - * There are two approaches to specify the list items available for selection. - * The first approach uses template syntax as follows, - * - * - * - * - * - * - * - * The second approach specifies a data source to be bound to the dropdown lists - * by setting {@link setListDataSource ListDataSource}. Like generic list controls, - * you may also want to specify which data fields are used for item values and texts - * by setting {@link setListValueField ListValueField} and - * {@link setListTextField ListTextField}, respectively. - * Furthermore, the item texts may be formatted by using {@link setListTextFormatString ListTextFormatString}. - * Note, if you specify {@link setListDataSource ListDataSource}, do it before - * calling the datagrid's dataBind(). - * - * The dropdown list control in the TDropDownListColumn can be accessed by one of - * the following two methods: - * - * $datagridItem->DropDownListColumnID->DropDownList - * $datagridItem->DropDownListColumnID->Controls[0] - * - * The second method is possible because the dropdown list control created within the - * datagrid cell is the first child. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TDropDownListColumn extends TDataGridColumn -{ - private $_stateLoaded=false; - private $_dataBound=false; - private $_listControl=null; - - public function __construct() - { - $this->_listControl=new TDropDownList; - } - - /** - * Loads items from viewstate. - * This method overrides the parent implementation by loading list items - * @param mixed state values - */ - public function loadState($state) - { - parent::loadState($state); - $this->_stateLoaded=true; - if(!$this->_dataBound) - $this->_listControl->getItems()->loadState($this->getViewState('Items',null)); - } - - /** - * Saves items into viewstate. - * This method overrides the parent implementation by saving list items - */ - public function saveState() - { - $this->setViewState('Items',$this->_listControl->getItems()->saveState(),null); - return parent::saveState(); - } - - /** - * Adds object parsed from template to the control. - * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. - * All other objects are ignored. - * @param mixed object parsed from template - */ - public function addParsedObject($object) - { - // Do not add items from template if items are loaded from viewstate - if(!$this->_stateLoaded && ($object instanceof TListItem)) - { - $object->setSelected(false); - $index=$this->_listControl->getItems()->add($object); - } - } - - /** - * @return string the field of the data source that provides the text content of the column. - */ - public function getDataTextField() - { - return $this->getViewState('DataTextField',''); - } - - /** - * Sets the field of the data source that provides the text content of the column. - * If this is not set, the data specified via {@link getDataValueField DataValueField} - * will be displayed in the column. - * @param string the field of the data source that provides the text content of the column. - */ - public function setDataTextField($value) - { - $this->setViewState('DataTextField',$value,''); - } - - /** - * @return string the formatting string used to control how the bound data will be displayed. - */ - public function getDataTextFormatString() - { - return $this->getViewState('DataTextFormatString',''); - } - - /** - * @param string the formatting string used to control how the bound data will be displayed. - */ - public function setDataTextFormatString($value) - { - $this->setViewState('DataTextFormatString',$value,''); - } - - /** - * @return string the field of the data source that provides the key selecting an item in dropdown list. - */ - public function getDataValueField() - { - return $this->getViewState('DataValueField',''); - } - - /** - * Sets the field of the data source that provides the key selecting an item in dropdown list. - * If this is not present, the data specified via {@link getDataTextField DataTextField} (without - * applying the formatting string) will be used for selection, instead. - * @param string the field of the data source that provides the key selecting an item in dropdown list. - */ - public function setDataValueField($value) - { - $this->setViewState('DataValueField',$value,''); - } - - /** - * @return boolean whether the items in the column can be edited. Defaults to false. - */ - public function getReadOnly() - { - return $this->getViewState('ReadOnly',false); - } - - /** - * @param boolean whether the items in the column can be edited - */ - public function setReadOnly($value) - { - $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return Traversable data source to be bound to the dropdown list boxes. - */ - public function getListDataSource() - { - return $this->_listControl->getDataSource(); - } - - /** - * @param Traversable|array|string data source to be bound to the dropdown list boxes. - */ - public function setListDataSource($value) - { - $this->_listControl->setDataSource($value); - } - - /** - * @return string the data field used to populate the values of the dropdown list items. Defaults to empty. - */ - public function getListValueField() - { - return $this->getViewState('ListValueField',''); - } - - /** - * @param string the data field used to populate the values of the dropdown list items - */ - public function setListValueField($value) - { - $this->setViewState('ListValueField',$value,''); - } - - /** - * @return string the data field used to populate the texts of the dropdown list items. Defaults to empty. - */ - public function getListTextField() - { - return $this->getViewState('ListTextField',''); - } - - /** - * @param string the data field used to populate the texts of the dropdown list items - */ - public function setListTextField($value) - { - $this->setViewState('ListTextField',$value,''); - } - - /** - * @return string the formatting string used to control how the list item texts will be displayed. - */ - public function getListTextFormatString() - { - return $this->getViewState('ListTextFormatString',''); - } - - /** - * @param string the formatting string used to control how the list item texts will be displayed. - */ - public function setListTextFormatString($value) - { - $this->setViewState('ListTextFormatString',$value,''); - } - - /** - * Initializes the specified cell to its initial values. - * This method overrides the parent implementation. - * It creates a textbox for item in edit mode and the column is not read-only. - * Otherwise it displays a static text. - * The caption of the button and the static text are retrieved - * from the datasource. - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if(!$this->_dataBound && $this->_listControl->getDataSource()!==null) - { - $this->_listControl->setDataTextField($this->getListTextField()); - $this->_listControl->setDataValueField($this->getListValueField()); - $this->_listControl->setDataTextFormatString($this->getListTextFormatString()); - $this->_listControl->dataBind(); - $this->_dataBound=true; - } - switch($itemType) - { - case TListItemType::EditItem: - if(!$this->getReadOnly()) - { - $listControl=clone $this->_listControl; - $cell->getControls()->add($listControl); - $cell->registerObject('DropDownList',$listControl); - $control=$listControl; - } - else - $control=$cell; - $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - break; - case TListItemType::Item: - case TListItemType::AlternatingItem: - case TListItemType::SelectedItem: - if($this->getDataTextField()!=='' || $this->getDataValueField()!=='') - $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - break; - default: - parent::initializeCell($cell,$columnIndex,$itemType); - break; - } - } - - /** - * Databinds a cell in the column. - * This method is invoked when datagrid performs databinding. - * It populates the content of the cell with the relevant data from data source. - */ - public function dataBindColumn($sender,$param) - { - $item=$sender->getNamingContainer(); - $data=$item->getData(); - if(($valueField=$this->getDataValueField())!=='') - $value=$this->getDataFieldValue($data,$valueField); - else - $value=''; - if(($textField=$this->getDataTextField())!=='') - { - $text=$this->getDataFieldValue($data,$textField); - if($valueField==='') - $value=$text; - $formatString=$this->getDataTextFormatString(); - $text=$this->formatDataValue($formatString,$text); - } - else - $text=$value; - if($sender instanceof TTableCell) - $sender->setText($text); - else if($sender instanceof TDropDownList) - $sender->setSelectedValue($value); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); +Prado::using('System.Web.UI.WebControls.TDropDownList'); + +/** + * TDropDownListColumn class + * + * TDropDownListColumn represents a column that is bound to a field in a data source. + * The cells in the column will be displayed using the data indexed by + * {@link setDataTextField DataTextField}. You can customize the display by + * setting {@link setDataTextFormatString DataTextFormatString}. + * + * If {@link setReadOnly ReadOnly} is false, TDropDownListColumn will display cells in edit mode + * with dropdown lists. Otherwise, a static text is displayed. + * The currently selected dropndown list item is specified by the data indexed with + * {@link setDataValueField DataValueField}. + * + * There are two approaches to specify the list items available for selection. + * The first approach uses template syntax as follows, + * + * + * + * + * + * + * + * The second approach specifies a data source to be bound to the dropdown lists + * by setting {@link setListDataSource ListDataSource}. Like generic list controls, + * you may also want to specify which data fields are used for item values and texts + * by setting {@link setListValueField ListValueField} and + * {@link setListTextField ListTextField}, respectively. + * Furthermore, the item texts may be formatted by using {@link setListTextFormatString ListTextFormatString}. + * Note, if you specify {@link setListDataSource ListDataSource}, do it before + * calling the datagrid's dataBind(). + * + * The dropdown list control in the TDropDownListColumn can be accessed by one of + * the following two methods: + * + * $datagridItem->DropDownListColumnID->DropDownList + * $datagridItem->DropDownListColumnID->Controls[0] + * + * The second method is possible because the dropdown list control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDropDownListColumn extends TDataGridColumn +{ + private $_stateLoaded=false; + private $_dataBound=false; + private $_listControl=null; + + public function __construct() + { + $this->_listControl=new TDropDownList; + } + + /** + * Loads items from viewstate. + * This method overrides the parent implementation by loading list items + * @param mixed state values + */ + public function loadState($state) + { + parent::loadState($state); + $this->_stateLoaded=true; + if(!$this->_dataBound) + $this->_listControl->getItems()->loadState($this->getViewState('Items',null)); + } + + /** + * Saves items into viewstate. + * This method overrides the parent implementation by saving list items + */ + public function saveState() + { + $this->setViewState('Items',$this->_listControl->getItems()->saveState(),null); + return parent::saveState(); + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + // Do not add items from template if items are loaded from viewstate + if(!$this->_stateLoaded && ($object instanceof TListItem)) + { + $object->setSelected(false); + $index=$this->_listControl->getItems()->add($object); + } + } + + /** + * @return string the field of the data source that provides the text content of the column. + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * Sets the field of the data source that provides the text content of the column. + * If this is not set, the data specified via {@link getDataValueField DataValueField} + * will be displayed in the column. + * @param string the field of the data source that provides the text content of the column. + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the bound data will be displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the bound data will be displayed. + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string the field of the data source that provides the key selecting an item in dropdown list. + */ + public function getDataValueField() + { + return $this->getViewState('DataValueField',''); + } + + /** + * Sets the field of the data source that provides the key selecting an item in dropdown list. + * If this is not present, the data specified via {@link getDataTextField DataTextField} (without + * applying the formatting string) will be used for selection, instead. + * @param string the field of the data source that provides the key selecting an item in dropdown list. + */ + public function setDataValueField($value) + { + $this->setViewState('DataValueField',$value,''); + } + + /** + * @return boolean whether the items in the column can be edited. Defaults to false. + */ + public function getReadOnly() + { + return $this->getViewState('ReadOnly',false); + } + + /** + * @param boolean whether the items in the column can be edited + */ + public function setReadOnly($value) + { + $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return Traversable data source to be bound to the dropdown list boxes. + */ + public function getListDataSource() + { + return $this->_listControl->getDataSource(); + } + + /** + * @param Traversable|array|string data source to be bound to the dropdown list boxes. + */ + public function setListDataSource($value) + { + $this->_listControl->setDataSource($value); + } + + /** + * @return string the data field used to populate the values of the dropdown list items. Defaults to empty. + */ + public function getListValueField() + { + return $this->getViewState('ListValueField',''); + } + + /** + * @param string the data field used to populate the values of the dropdown list items + */ + public function setListValueField($value) + { + $this->setViewState('ListValueField',$value,''); + } + + /** + * @return string the data field used to populate the texts of the dropdown list items. Defaults to empty. + */ + public function getListTextField() + { + return $this->getViewState('ListTextField',''); + } + + /** + * @param string the data field used to populate the texts of the dropdown list items + */ + public function setListTextField($value) + { + $this->setViewState('ListTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the list item texts will be displayed. + */ + public function getListTextFormatString() + { + return $this->getViewState('ListTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the list item texts will be displayed. + */ + public function setListTextFormatString($value) + { + $this->setViewState('ListTextFormatString',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates a textbox for item in edit mode and the column is not read-only. + * Otherwise it displays a static text. + * The caption of the button and the static text are retrieved + * from the datasource. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if(!$this->_dataBound && $this->_listControl->getDataSource()!==null) + { + $this->_listControl->setDataTextField($this->getListTextField()); + $this->_listControl->setDataValueField($this->getListValueField()); + $this->_listControl->setDataTextFormatString($this->getListTextFormatString()); + $this->_listControl->dataBind(); + $this->_dataBound=true; + } + switch($itemType) + { + case TListItemType::EditItem: + if(!$this->getReadOnly()) + { + $listControl=clone $this->_listControl; + $cell->getControls()->add($listControl); + $cell->registerObject('DropDownList',$listControl); + $control=$listControl; + } + else + $control=$cell; + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + break; + case TListItemType::Item: + case TListItemType::AlternatingItem: + case TListItemType::SelectedItem: + if($this->getDataTextField()!=='' || $this->getDataValueField()!=='') + $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + break; + default: + parent::initializeCell($cell,$columnIndex,$itemType); + break; + } + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + if(($valueField=$this->getDataValueField())!=='') + $value=$this->getDataFieldValue($data,$valueField); + else + $value=''; + if(($textField=$this->getDataTextField())!=='') + { + $text=$this->getDataFieldValue($data,$textField); + if($valueField==='') + $value=$text; + $formatString=$this->getDataTextFormatString(); + $text=$this->formatDataValue($formatString,$text); + } + else + $text=$value; + if($sender instanceof TTableCell) + $sender->setText($text); + else if($sender instanceof TDropDownList) + $sender->setSelectedValue($value); + } +} + diff --git a/framework/Web/UI/WebControls/TEditCommandColumn.php b/framework/Web/UI/WebControls/TEditCommandColumn.php index 44004807..b10c6880 100644 --- a/framework/Web/UI/WebControls/TEditCommandColumn.php +++ b/framework/Web/UI/WebControls/TEditCommandColumn.php @@ -1,265 +1,265 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TDataGridColumn class file - */ -Prado::using('System.Web.UI.WebControls.TDataGridColumn'); - -/** - * TEditCommandColumn class - * - * TEditCommandColumn contains the Edit command buttons for editing data items in each row. - * - * TEditCommandColumn will create an edit button if a cell is not in edit mode. - * Otherwise an update button and a cancel button will be created within the cell. - * The button captions are specified using {@link setEditText EditText}, - * {@link setUpdateText UpdateText}, and {@link setCancelText CancelText}. - * - * The buttons in the column can be set to display as hyperlinks, push or image buttons - * by setting the {@link setButtonType ButtonType} property. - * - * When an edit button is clicked, the datagrid will generate an - * {@link onEditCommand OnEditCommand} event. When an update/cancel button - * is clicked, the datagrid will generate an - * {@link onUpdateCommand OnUpdateCommand} or an {@link onCancelCommand OnCancelCommand} - * You can write these event handlers to change the state of specific datagrid item. - * - * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} - * properties affect the corresponding properties of the edit and update buttons. - * The cancel button does not cause validation by default. - * - * The command buttons in the column can be accessed by one of the following methods: - * - * $datagridItem->ButtonColumnID->EditButton (or UpdateButton, CancelButton) - * $datagridItem->ButtonColumnID->Controls[0] - * - * The second method is possible because the button control created within the - * datagrid cell is the first child. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TEditCommandColumn extends TDataGridColumn -{ - /** - * @return TButtonColumnType the type of command button. Defaults to TButtonColumnType::LinkButton. - */ - public function getButtonType() - { - return $this->getViewState('ButtonType',TButtonColumnType::LinkButton); - } - - /** - * @param TButtonColumnType the type of command button. - */ - public function setButtonType($value) - { - $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TButtonColumnType'),TButtonColumnType::LinkButton); - } - - /** - * @return string the caption of the edit button. Defaults to 'Edit'. - */ - public function getEditText() - { - return $this->getViewState('EditText','Edit'); - } - - /** - * @param string the caption of the edit button - */ - public function setEditText($value) - { - $this->setViewState('EditText',$value,'Edit'); - } - - /** - * @return string the URL of the image file for edit image buttons - */ - public function getEditImageUrl() - { - return $this->getViewState('EditImageUrl',''); - } - - /** - * @param string the URL of the image file for edit image buttons - */ - public function setEditImageUrl($value) - { - $this->setViewState('EditImageUrl',$value,''); - } - - /** - * @return string the caption of the update button. Defaults to 'Update'. - */ - public function getUpdateText() - { - return $this->getViewState('UpdateText','Update'); - } - - /** - * @param string the caption of the update button - */ - public function setUpdateText($value) - { - $this->setViewState('UpdateText',$value,'Update'); - } - - /** - * @return string the URL of the image file for update image buttons - */ - public function getUpdateImageUrl() - { - return $this->getViewState('UpdateImageUrl',''); - } - - /** - * @param string the URL of the image file for update image buttons - */ - public function setUpdateImageUrl($value) - { - $this->setViewState('UpdateImageUrl',$value,''); - } - - /** - * @return string the caption of the cancel button. Defaults to 'Cancel'. - */ - public function getCancelText() - { - return $this->getViewState('CancelText','Cancel'); - } - - /** - * @param string the caption of the cancel button - */ - public function setCancelText($value) - { - $this->setViewState('CancelText',$value,'Cancel'); - } - - /** - * @return string the URL of the image file for cancel image buttons - */ - public function getCancelImageUrl() - { - return $this->getViewState('CancelImageUrl',''); - } - - /** - * @param string the URL of the image file for cancel image buttons - */ - public function setCancelImageUrl($value) - { - $this->setViewState('CancelImageUrl',$value,''); - } - - /** - * @return boolean whether postback event trigger by edit or update button will cause input validation, default is true - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * @param boolean whether postback event trigger by edit or update button will cause input validation - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the group of validators which the edit or update button causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the edit or update button causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * Initializes the specified cell to its initial values. - * This method overrides the parent implementation. - * It creates an update and a cancel button for cell in edit mode. - * Otherwise it creates an edit button. - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) - { - $button=$this->createButton('Edit',$this->getEditText(),false,''); - $cell->getControls()->add($button); - $cell->registerObject('EditButton',$button); - } - else if($itemType===TListItemType::EditItem) - { - $controls=$cell->getControls(); - $button=$this->createButton('Update',$this->getUpdateText(),$this->getCausesValidation(),$this->getValidationGroup()); - $controls->add($button); - $cell->registerObject('UpdateButton',$button); - $controls->add(' '); - $button=$this->createButton('Cancel',$this->getCancelText(),false,''); - $controls->add($button); - $cell->registerObject('CancelButton',$button); - } - else - parent::initializeCell($cell,$columnIndex,$itemType); - } - - /** - * Creates a button and initializes its properties. - * The button type is determined by {@link getButtonType ButtonType}. - * @param string command name associated with the button - * @param string button caption - * @param boolean whether the button should cause validation - * @param string the validation group that the button belongs to - * @return mixed the newly created button. - */ - protected function createButton($commandName,$text,$causesValidation,$validationGroup) - { - if($this->getButtonType()===TButtonColumnType::LinkButton) - $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); - else if($this->getButtonType()===TButtonColumnType::PushButton) - $button=Prado::createComponent('System.Web.UI.WebControls.TButton'); - else // image buttons - { - $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); - if(strcasecmp($commandName,'Update')===0) - $url=$this->getUpdateImageUrl(); - else if(strcasecmp($commandName,'Cancel')===0) - $url=$this->getCancelImageUrl(); - else - $url=$this->getEditImageUrl(); - $button->setImageUrl($url); - } - $button->setText($text); - $button->setCommandName($commandName); - $button->setCausesValidation($causesValidation); - $button->setValidationGroup($validationGroup); - return $button; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TEditCommandColumn class + * + * TEditCommandColumn contains the Edit command buttons for editing data items in each row. + * + * TEditCommandColumn will create an edit button if a cell is not in edit mode. + * Otherwise an update button and a cancel button will be created within the cell. + * The button captions are specified using {@link setEditText EditText}, + * {@link setUpdateText UpdateText}, and {@link setCancelText CancelText}. + * + * The buttons in the column can be set to display as hyperlinks, push or image buttons + * by setting the {@link setButtonType ButtonType} property. + * + * When an edit button is clicked, the datagrid will generate an + * {@link onEditCommand OnEditCommand} event. When an update/cancel button + * is clicked, the datagrid will generate an + * {@link onUpdateCommand OnUpdateCommand} or an {@link onCancelCommand OnCancelCommand} + * You can write these event handlers to change the state of specific datagrid item. + * + * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} + * properties affect the corresponding properties of the edit and update buttons. + * The cancel button does not cause validation by default. + * + * The command buttons in the column can be accessed by one of the following methods: + * + * $datagridItem->ButtonColumnID->EditButton (or UpdateButton, CancelButton) + * $datagridItem->ButtonColumnID->Controls[0] + * + * The second method is possible because the button control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TEditCommandColumn extends TDataGridColumn +{ + /** + * @return TButtonColumnType the type of command button. Defaults to TButtonColumnType::LinkButton. + */ + public function getButtonType() + { + return $this->getViewState('ButtonType',TButtonColumnType::LinkButton); + } + + /** + * @param TButtonColumnType the type of command button. + */ + public function setButtonType($value) + { + $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TButtonColumnType'),TButtonColumnType::LinkButton); + } + + /** + * @return string the caption of the edit button. Defaults to 'Edit'. + */ + public function getEditText() + { + return $this->getViewState('EditText','Edit'); + } + + /** + * @param string the caption of the edit button + */ + public function setEditText($value) + { + $this->setViewState('EditText',$value,'Edit'); + } + + /** + * @return string the URL of the image file for edit image buttons + */ + public function getEditImageUrl() + { + return $this->getViewState('EditImageUrl',''); + } + + /** + * @param string the URL of the image file for edit image buttons + */ + public function setEditImageUrl($value) + { + $this->setViewState('EditImageUrl',$value,''); + } + + /** + * @return string the caption of the update button. Defaults to 'Update'. + */ + public function getUpdateText() + { + return $this->getViewState('UpdateText','Update'); + } + + /** + * @param string the caption of the update button + */ + public function setUpdateText($value) + { + $this->setViewState('UpdateText',$value,'Update'); + } + + /** + * @return string the URL of the image file for update image buttons + */ + public function getUpdateImageUrl() + { + return $this->getViewState('UpdateImageUrl',''); + } + + /** + * @param string the URL of the image file for update image buttons + */ + public function setUpdateImageUrl($value) + { + $this->setViewState('UpdateImageUrl',$value,''); + } + + /** + * @return string the caption of the cancel button. Defaults to 'Cancel'. + */ + public function getCancelText() + { + return $this->getViewState('CancelText','Cancel'); + } + + /** + * @param string the caption of the cancel button + */ + public function setCancelText($value) + { + $this->setViewState('CancelText',$value,'Cancel'); + } + + /** + * @return string the URL of the image file for cancel image buttons + */ + public function getCancelImageUrl() + { + return $this->getViewState('CancelImageUrl',''); + } + + /** + * @param string the URL of the image file for cancel image buttons + */ + public function setCancelImageUrl($value) + { + $this->setViewState('CancelImageUrl',$value,''); + } + + /** + * @return boolean whether postback event trigger by edit or update button will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by edit or update button will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the edit or update button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the edit or update button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates an update and a cancel button for cell in edit mode. + * Otherwise it creates an edit button. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) + { + $button=$this->createButton('Edit',$this->getEditText(),false,''); + $cell->getControls()->add($button); + $cell->registerObject('EditButton',$button); + } + else if($itemType===TListItemType::EditItem) + { + $controls=$cell->getControls(); + $button=$this->createButton('Update',$this->getUpdateText(),$this->getCausesValidation(),$this->getValidationGroup()); + $controls->add($button); + $cell->registerObject('UpdateButton',$button); + $controls->add(' '); + $button=$this->createButton('Cancel',$this->getCancelText(),false,''); + $controls->add($button); + $cell->registerObject('CancelButton',$button); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Creates a button and initializes its properties. + * The button type is determined by {@link getButtonType ButtonType}. + * @param string command name associated with the button + * @param string button caption + * @param boolean whether the button should cause validation + * @param string the validation group that the button belongs to + * @return mixed the newly created button. + */ + protected function createButton($commandName,$text,$causesValidation,$validationGroup) + { + if($this->getButtonType()===TButtonColumnType::LinkButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); + else if($this->getButtonType()===TButtonColumnType::PushButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TButton'); + else // image buttons + { + $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); + if(strcasecmp($commandName,'Update')===0) + $url=$this->getUpdateImageUrl(); + else if(strcasecmp($commandName,'Cancel')===0) + $url=$this->getCancelImageUrl(); + else + $url=$this->getEditImageUrl(); + $button->setImageUrl($url); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCausesValidation($causesValidation); + $button->setValidationGroup($validationGroup); + return $button; + } +} + diff --git a/framework/Web/UI/WebControls/TEmailAddressValidator.php b/framework/Web/UI/WebControls/TEmailAddressValidator.php index a198ffc4..b0b51208 100644 --- a/framework/Web/UI/WebControls/TEmailAddressValidator.php +++ b/framework/Web/UI/WebControls/TEmailAddressValidator.php @@ -1,97 +1,97 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TRegularExpressionValidator class - */ -Prado::using('System.Web.UI.WebControls.TRegularExpressionValidator'); - -/** - * TEmailAddressValidator class - * - * TEmailAddressValidator validates whether the value of an associated - * input component is a valid email address. If {@link getCheckMXRecord CheckMXRecord} - * is true, it will check MX record for the email adress, provided - * checkdnsrr() is available in the installed PHP. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TEmailAddressValidator extends TRegularExpressionValidator -{ - /** - * Regular expression used to validate the email address - */ - const EMAIL_REGEXP="\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"; - - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TEmailAddressValidator'; - } - - /** - * @return string the regular expression that determines the pattern used to validate a field. - */ - public function getRegularExpression() - { - $regex=parent::getRegularExpression(); - return $regex===''?self::EMAIL_REGEXP:$regex; - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - public function evaluateIsValid() - { - $valid=parent::evaluateIsValid(); - if($valid && $this->getCheckMXRecord() && function_exists('checkdnsrr')) - { - if(($value=$this->getValidationValue($this->getValidationTarget()))!=='') - { - if(($pos=strpos($value,'@'))!==false) - { - $domain=substr($value,$pos+1); - return $domain===''?false:checkdnsrr($domain,'MX'); - } - else - return false; - } - } - return $valid; - } - - /** - * @return boolean whether to check MX record for the email address being validated. Defaults to true. - */ - public function getCheckMXRecord() - { - return $this->getViewState('CheckMXRecord',true); - } - - /** - * @param boolean whether to check MX record for the email address being validated. - * Note, if {@link checkdnsrr} is not available, this check will not be performed. - */ - public function setCheckMXRecord($value) - { - $this->setViewState('CheckMXRecord',TPropertyValue::ensureBoolean($value),true); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TRegularExpressionValidator class + */ +Prado::using('System.Web.UI.WebControls.TRegularExpressionValidator'); + +/** + * TEmailAddressValidator class + * + * TEmailAddressValidator validates whether the value of an associated + * input component is a valid email address. If {@link getCheckMXRecord CheckMXRecord} + * is true, it will check MX record for the email adress, provided + * checkdnsrr() is available in the installed PHP. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TEmailAddressValidator extends TRegularExpressionValidator +{ + /** + * Regular expression used to validate the email address + */ + const EMAIL_REGEXP="\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"; + + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TEmailAddressValidator'; + } + + /** + * @return string the regular expression that determines the pattern used to validate a field. + */ + public function getRegularExpression() + { + $regex=parent::getRegularExpression(); + return $regex===''?self::EMAIL_REGEXP:$regex; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + public function evaluateIsValid() + { + $valid=parent::evaluateIsValid(); + if($valid && $this->getCheckMXRecord() && function_exists('checkdnsrr')) + { + if(($value=$this->getValidationValue($this->getValidationTarget()))!=='') + { + if(($pos=strpos($value,'@'))!==false) + { + $domain=substr($value,$pos+1); + return $domain===''?false:checkdnsrr($domain,'MX'); + } + else + return false; + } + } + return $valid; + } + + /** + * @return boolean whether to check MX record for the email address being validated. Defaults to true. + */ + public function getCheckMXRecord() + { + return $this->getViewState('CheckMXRecord',true); + } + + /** + * @param boolean whether to check MX record for the email address being validated. + * Note, if {@link checkdnsrr} is not available, this check will not be performed. + */ + public function setCheckMXRecord($value) + { + $this->setViewState('CheckMXRecord',TPropertyValue::ensureBoolean($value),true); + } +} + diff --git a/framework/Web/UI/WebControls/TExpression.php b/framework/Web/UI/WebControls/TExpression.php index 9b8eb7e7..cf38df70 100644 --- a/framework/Web/UI/WebControls/TExpression.php +++ b/framework/Web/UI/WebControls/TExpression.php @@ -1,62 +1,62 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TExpression class - * - * TExpression evaluates a PHP expression and renders the result. - * The expression is evaluated during the rendering stage. The expression being - * evaluated can be set via the property {@link setExpression Expression}. - * The context of the expression evaluated is the TExpression object itself. - * - * Note, since TExpression allows evaluation of arbitrary PHP expression, - * make sure {@link setExpression Expression} does not come directly from user input. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TExpression extends TControl -{ - /** - * @var string PHP expression to be evaluated - */ - private $_e=''; - - /** - * @return string the expression to be evaluated - */ - public function getExpression() - { - return $this->_e; - } - - /** - * @param string the expression to be evaluated - */ - public function setExpression($value) - { - $this->_e=$value; - } - - /** - * Renders the evaluation result of the expression. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - if($this->_e!=='') - $writer->write($this->evaluateExpression($this->_e)); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TExpression class + * + * TExpression evaluates a PHP expression and renders the result. + * The expression is evaluated during the rendering stage. The expression being + * evaluated can be set via the property {@link setExpression Expression}. + * The context of the expression evaluated is the TExpression object itself. + * + * Note, since TExpression allows evaluation of arbitrary PHP expression, + * make sure {@link setExpression Expression} does not come directly from user input. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TExpression extends TControl +{ + /** + * @var string PHP expression to be evaluated + */ + private $_e=''; + + /** + * @return string the expression to be evaluated + */ + public function getExpression() + { + return $this->_e; + } + + /** + * @param string the expression to be evaluated + */ + public function setExpression($value) + { + $this->_e=$value; + } + + /** + * Renders the evaluation result of the expression. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->_e!=='') + $writer->write($this->evaluateExpression($this->_e)); + } +} + diff --git a/framework/Web/UI/WebControls/TFileUpload.php b/framework/Web/UI/WebControls/TFileUpload.php index 0f7d226d..051e3e0b 100644 --- a/framework/Web/UI/WebControls/TFileUpload.php +++ b/framework/Web/UI/WebControls/TFileUpload.php @@ -1,281 +1,281 @@ -, Qiang Xue - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TFileUpload class - * - * TFileUpload displays a file upload field on a page. Upon postback, - * the text entered into the field will be treated as the name of the file - * that will be uploaded to the server. The property {@link getHasFile HasFile} - * indicates whether the file upload is successful. If successful, the file - * may be obtained by calling {@link saveAs} to save it at a specified place. - * You can use {@link getFileName FileName}, {@link getFileType FileType}, - * {@link getFileSize FileSize} to get the original client-side file name, - * the file mime type, and the file size information. If the upload is not - * successful, {@link getErrorCode ErrorCode} contains the error code - * describing the cause of failure. - * - * TFileUpload raises {@link onFileUpload OnFileUpload} event if a file is uploaded - * (whether it succeeds or not). - * - * @author Marcus Nyeholt , Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TFileUpload extends TWebControl implements IPostBackDataHandler, IValidatable -{ - /** - * Maximum file size (in bytes) allowed to be uploaded, defaults to 1MB. - */ - const MAX_FILE_SIZE=1048576; - /** - * @var integer the size of the uploaded file (in bytes) - */ - private $_fileSize=0; - /** - * @var string The original name of the file on the client machine - */ - private $_fileName=''; - /** - * @var string the name of the temporary file storing the uploaded file - */ - private $_localName=''; - /** - * @var string the uploaded file mime type - */ - private $_fileType=''; - /** - * @var integer error code of the current file upload - */ - protected $_errorCode=UPLOAD_ERR_NO_FILE; - private $_dataChanged=false; - private $_isValid=true; - - /** - * @return string tag name of the file upload control - */ - protected function getTagName() - { - return 'input'; - } - - /** - * Sets name attribute to the unique ID of the control. - * This method overrides the parent implementation with additional file update control specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $this->getPage()->ensureRenderInForm($this); - parent::addAttributesToRender($writer); - $writer->addAttribute('type','file'); - $writer->addAttribute('name',$this->getUniqueID()); - $isEnabled=$this->getEnabled(true); - if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' - $writer->addAttribute('disabled','disabled'); - } - - /** - * Sets Enctype of the form on the page. - * This method overrides the parent implementation and is invoked before render. - * @param mixed event parameter - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if(($form=$this->getPage()->getForm())!==null) - $form->setEnctype('multipart/form-data'); - $this->getPage()->getClientScript()->registerHiddenField('MAX_FILE_SIZE',$this->getMaxFileSize()); - if($this->getEnabled(true)) - $this->getPage()->registerRequiresPostData($this); - } - - /** - * @return integer the maximum file size, defaults to 1MB (1048576 bytes). - * @see setMaxFileSize - */ - public function getMaxFileSize() - { - return $this->getViewState('MaxFileSize',self::MAX_FILE_SIZE); - } - - /** - * Sets the maximum size that a file can be uploaded. - * Note, this is an advisory value to the browser. Sets this property with - * a reasonably large size to save users the trouble of waiting - * for a big file being transferred only to find that it was too big - * and the transfer failed. - * @param int the maximum upload size allowed for a file. - */ - public function setMaxFileSize($size) - { - $this->setViewState('MaxFileSize',TPropertyValue::ensureInteger($size),self::MAX_FILE_SIZE); - } - - /** - * @return string the original full path name of the file on the client machine - */ - public function getFileName() - { - return $this->_fileName; - } - - /** - * @return integer the actual size of the uploaded file in bytes - */ - public function getFileSize() - { - return $this->_fileSize; - } - - /** - * @return string the MIME-type of the uploaded file (such as "image/gif"). - * This mime type is not checked on the server side and do not take its value for granted. - */ - public function getFileType() - { - return $this->_fileType; - } - - /** - * @return string the local name of the file (where it is after being uploaded). - * Note, PHP will delete this file automatically after finishing this round of request. - */ - public function getLocalName() - { - return $this->_localName; - } - - /** - * Returns an error code describing the status of this file uploading. - * @return integer the error code - * @see http://www.php.net/manual/en/features.file-upload.errors.php - */ - public function getErrorCode() - { - return $this->_errorCode; - } - - /** - * @return boolean whether the file is uploaded successfully - */ - public function getHasFile() - { - return $this->_errorCode===UPLOAD_ERR_OK; - } - - /** - * Saves the uploaded file. - * @param string the file name used to save the uploaded file - * @param boolean whether to delete the temporary file after saving. - * If true, you will not be able to save the uploaded file again. - * @return boolean true if the file saving is successful - */ - public function saveAs($fileName,$deleteTempFile=true) - { - if($this->_errorCode===UPLOAD_ERR_OK) - { - if($deleteTempFile) - return move_uploaded_file($this->_localName,$fileName); - else if(is_uploaded_file($this->_localName)) - return file_put_contents($fileName,file_get_contents($this->_localName))!==false; - else - return false; - } - else - return false; - } - - /** - * Loads user input data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the control has been changed - */ - public function loadPostData($key,$values) - { - if(isset($_FILES[$key])) - { - $this->_fileName=$_FILES[$key]['name']; - $this->_fileSize=$_FILES[$key]['size']; - $this->_fileType=$_FILES[$key]['type']; - $this->_errorCode=$_FILES[$key]['error']; - $this->_localName=$_FILES[$key]['tmp_name']; - return $this->_dataChanged=true; - } - else - return false; - } - - /** - * Raises postdata changed event. - * This method calls {@link onFileUpload} method. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - $this->onFileUpload(null); - } - - /** - * This method is invoked when a file is uploaded during a postback. - * The method raises OnFileUpload event to fire up the event handler. - * If you override this method, be sure to call the parent implementation - * so that the event delegates can be invoked. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onFileUpload($param) - { - $this->raiseEvent('OnFileUpload',$this,$param); - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * Returns the original file name as the property value to be validated. - * This method is required by IValidatable property. - * @return mixed the property value to be validated - */ - public function getValidationPropertyValue() - { - return $this->getFileName(); - } - - /** - * Returns true if this control validated successfully. - * Defaults to true. - * @return bool wether this control validated successfully. - */ - public function getIsValid() - { - return $this->_isValid; - } - /** - * @param bool wether this control is valid. - */ - public function setIsValid($value) - { - $this->_isValid=TPropertyValue::ensureBoolean($value); - } - -} - +, Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TFileUpload class + * + * TFileUpload displays a file upload field on a page. Upon postback, + * the text entered into the field will be treated as the name of the file + * that will be uploaded to the server. The property {@link getHasFile HasFile} + * indicates whether the file upload is successful. If successful, the file + * may be obtained by calling {@link saveAs} to save it at a specified place. + * You can use {@link getFileName FileName}, {@link getFileType FileType}, + * {@link getFileSize FileSize} to get the original client-side file name, + * the file mime type, and the file size information. If the upload is not + * successful, {@link getErrorCode ErrorCode} contains the error code + * describing the cause of failure. + * + * TFileUpload raises {@link onFileUpload OnFileUpload} event if a file is uploaded + * (whether it succeeds or not). + * + * @author Marcus Nyeholt , Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TFileUpload extends TWebControl implements IPostBackDataHandler, IValidatable +{ + /** + * Maximum file size (in bytes) allowed to be uploaded, defaults to 1MB. + */ + const MAX_FILE_SIZE=1048576; + /** + * @var integer the size of the uploaded file (in bytes) + */ + private $_fileSize=0; + /** + * @var string The original name of the file on the client machine + */ + private $_fileName=''; + /** + * @var string the name of the temporary file storing the uploaded file + */ + private $_localName=''; + /** + * @var string the uploaded file mime type + */ + private $_fileType=''; + /** + * @var integer error code of the current file upload + */ + protected $_errorCode=UPLOAD_ERR_NO_FILE; + private $_dataChanged=false; + private $_isValid=true; + + /** + * @return string tag name of the file upload control + */ + protected function getTagName() + { + return 'input'; + } + + /** + * Sets name attribute to the unique ID of the control. + * This method overrides the parent implementation with additional file update control specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $this->getPage()->ensureRenderInForm($this); + parent::addAttributesToRender($writer); + $writer->addAttribute('type','file'); + $writer->addAttribute('name',$this->getUniqueID()); + $isEnabled=$this->getEnabled(true); + if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + } + + /** + * Sets Enctype of the form on the page. + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if(($form=$this->getPage()->getForm())!==null) + $form->setEnctype('multipart/form-data'); + $this->getPage()->getClientScript()->registerHiddenField('MAX_FILE_SIZE',$this->getMaxFileSize()); + if($this->getEnabled(true)) + $this->getPage()->registerRequiresPostData($this); + } + + /** + * @return integer the maximum file size, defaults to 1MB (1048576 bytes). + * @see setMaxFileSize + */ + public function getMaxFileSize() + { + return $this->getViewState('MaxFileSize',self::MAX_FILE_SIZE); + } + + /** + * Sets the maximum size that a file can be uploaded. + * Note, this is an advisory value to the browser. Sets this property with + * a reasonably large size to save users the trouble of waiting + * for a big file being transferred only to find that it was too big + * and the transfer failed. + * @param int the maximum upload size allowed for a file. + */ + public function setMaxFileSize($size) + { + $this->setViewState('MaxFileSize',TPropertyValue::ensureInteger($size),self::MAX_FILE_SIZE); + } + + /** + * @return string the original full path name of the file on the client machine + */ + public function getFileName() + { + return $this->_fileName; + } + + /** + * @return integer the actual size of the uploaded file in bytes + */ + public function getFileSize() + { + return $this->_fileSize; + } + + /** + * @return string the MIME-type of the uploaded file (such as "image/gif"). + * This mime type is not checked on the server side and do not take its value for granted. + */ + public function getFileType() + { + return $this->_fileType; + } + + /** + * @return string the local name of the file (where it is after being uploaded). + * Note, PHP will delete this file automatically after finishing this round of request. + */ + public function getLocalName() + { + return $this->_localName; + } + + /** + * Returns an error code describing the status of this file uploading. + * @return integer the error code + * @see http://www.php.net/manual/en/features.file-upload.errors.php + */ + public function getErrorCode() + { + return $this->_errorCode; + } + + /** + * @return boolean whether the file is uploaded successfully + */ + public function getHasFile() + { + return $this->_errorCode===UPLOAD_ERR_OK; + } + + /** + * Saves the uploaded file. + * @param string the file name used to save the uploaded file + * @param boolean whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again. + * @return boolean true if the file saving is successful + */ + public function saveAs($fileName,$deleteTempFile=true) + { + if($this->_errorCode===UPLOAD_ERR_OK) + { + if($deleteTempFile) + return move_uploaded_file($this->_localName,$fileName); + else if(is_uploaded_file($this->_localName)) + return file_put_contents($fileName,file_get_contents($this->_localName))!==false; + else + return false; + } + else + return false; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the control has been changed + */ + public function loadPostData($key,$values) + { + if(isset($_FILES[$key])) + { + $this->_fileName=$_FILES[$key]['name']; + $this->_fileSize=$_FILES[$key]['size']; + $this->_fileType=$_FILES[$key]['type']; + $this->_errorCode=$_FILES[$key]['error']; + $this->_localName=$_FILES[$key]['tmp_name']; + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Raises postdata changed event. + * This method calls {@link onFileUpload} method. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onFileUpload(null); + } + + /** + * This method is invoked when a file is uploaded during a postback. + * The method raises OnFileUpload event to fire up the event handler. + * If you override this method, be sure to call the parent implementation + * so that the event delegates can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onFileUpload($param) + { + $this->raiseEvent('OnFileUpload',$this,$param); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the original file name as the property value to be validated. + * This method is required by IValidatable property. + * @return mixed the property value to be validated + */ + public function getValidationPropertyValue() + { + return $this->getFileName(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + +} + diff --git a/framework/Web/UI/WebControls/TFlushOutput.php b/framework/Web/UI/WebControls/TFlushOutput.php index cc55646d..0ea9b389 100644 --- a/framework/Web/UI/WebControls/TFlushOutput.php +++ b/framework/Web/UI/WebControls/TFlushOutput.php @@ -1,86 +1,86 @@ - - * @link http://www.pradosoft.com/ - * @license http://www.pradosoft.com/license/ - * @version $Id: TFlushOutput.php $ - * @package System.Web.UI.WebControls - */ - -/** - * TFlushOutput class. - * - * TFlushOutput enables forced flushing of the current output buffer - * at (a) certain point(s) in the page, after rendering of all previous - * controls has been completed. - * - * To use TFlushOutput, simply place it in a template where you want - * the have the output buffered between the start of the page or the - * last TFlushOutput to be sent to the client immediately - * - * - * - * - * You can specify whether you want to keep buffering of the output - * (if it was enabled) till the next occourence of a - * or the end of the page rendering, or stop buffering, by using the - * {@link setContinueBuffering ContinueBuffering}. - * - * @author Berczi Gabor - * @version $Id: TFlushOutput.php $ - * @package System.Web.UI.WebControls - * @since 3.1 - */ -class TFlushOutput extends TControl -{ - /** - * @var boolean whether to continue buffering of output - */ - private $_continueBuffering=true; - - - /** - * Constructor. - */ - public function __construct() - { - parent::__construct(); - $this->EnableViewState = false; - } - - /** - * @return Tells whether buffering of output can continue after this point - */ - public function getContinueBuffering() - { - return $this->_continueBuffering; - } - - /** - * @param boolean sets whether buffering of output can continue after this point - */ - public function setContinueBuffering($value) - { - $this->_continueBuffering = TPropertyValue::ensureBoolean($value); - } - - /** - * Flushes the output of all completely rendered controls to the client. - * @param THtmlWriter writer for the rendering purpose - */ - public function render($writer) - { -//$writer->write(''); - // ajax responses can't be parsed by the client side before loaded and returned completely, - // so don't bother with flushing output somewhere mid-page if refreshing in a callback - if (!$this->Page->IsCallback) - { - $this->Page->flushWriter(); -// $this->Application->flushOutput($this->ContinueBuffering); - } - } -} - + + * @link http://www.pradosoft.com/ + * @license http://www.pradosoft.com/license/ + * @version $Id: TFlushOutput.php $ + * @package System.Web.UI.WebControls + */ + +/** + * TFlushOutput class. + * + * TFlushOutput enables forced flushing of the current output buffer + * at (a) certain point(s) in the page, after rendering of all previous + * controls has been completed. + * + * To use TFlushOutput, simply place it in a template where you want + * the have the output buffered between the start of the page or the + * last TFlushOutput to be sent to the client immediately + * + * + * + * + * You can specify whether you want to keep buffering of the output + * (if it was enabled) till the next occourence of a + * or the end of the page rendering, or stop buffering, by using the + * {@link setContinueBuffering ContinueBuffering}. + * + * @author Berczi Gabor + * @version $Id: TFlushOutput.php $ + * @package System.Web.UI.WebControls + * @since 3.1 + */ +class TFlushOutput extends TControl +{ + /** + * @var boolean whether to continue buffering of output + */ + private $_continueBuffering=true; + + + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(); + $this->EnableViewState = false; + } + + /** + * @return Tells whether buffering of output can continue after this point + */ + public function getContinueBuffering() + { + return $this->_continueBuffering; + } + + /** + * @param boolean sets whether buffering of output can continue after this point + */ + public function setContinueBuffering($value) + { + $this->_continueBuffering = TPropertyValue::ensureBoolean($value); + } + + /** + * Flushes the output of all completely rendered controls to the client. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { +//$writer->write(''); + // ajax responses can't be parsed by the client side before loaded and returned completely, + // so don't bother with flushing output somewhere mid-page if refreshing in a callback + if (!$this->Page->IsCallback) + { + $this->Page->flushWriter(); +// $this->Application->flushOutput($this->ContinueBuffering); + } + } +} + ?> \ No newline at end of file diff --git a/framework/Web/UI/WebControls/TFont.php b/framework/Web/UI/WebControls/TFont.php index 4da42508..771b6a4e 100644 --- a/framework/Web/UI/WebControls/TFont.php +++ b/framework/Web/UI/WebControls/TFont.php @@ -1,318 +1,318 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TFont class - * - * TFont encapsulates the CSS style fields related with font settings. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TFont extends TComponent -{ - /** - * Bits indicating the font states. - */ - const IS_BOLD=0x01; - const IS_ITALIC=0x02; - const IS_OVERLINE=0x04; - const IS_STRIKEOUT=0x08; - const IS_UNDERLINE=0x10; - - /** - * Bits indicating whether particular font states are changed. - */ - const IS_SET_BOLD=0x01000; - const IS_SET_ITALIC=0x02000; - const IS_SET_OVERLINE=0x04000; - const IS_SET_STRIKEOUT=0x08000; - const IS_SET_UNDERLINE=0x10000; - const IS_SET_SIZE=0x20000; - const IS_SET_NAME=0x40000; - - /** - * @var integer bits representing various states - */ - private $_flags=0; - /** - * @var string font name - */ - private $_name=''; - /** - * @var string font size - */ - private $_size=''; - - /** - * @return boolean whether the font is in bold face. Defaults to false. - */ - public function getBold() - { - return ($this->_flags & self::IS_BOLD)!==0; - } - - /** - * @param boolean whether the font is in bold face - */ - public function setBold($value) - { - $this->_flags |= self::IS_SET_BOLD; - if(TPropertyValue::ensureBoolean($value)) - $this->_flags |= self::IS_BOLD; - else - $this->_flags &= ~self::IS_BOLD; - } - - /** - * @return boolean whether the font is in italic face. Defaults to false. - */ - public function getItalic() - { - return ($this->_flags & self::IS_ITALIC)!==0; - } - - /** - * @param boolean whether the font is italic - */ - public function setItalic($value) - { - $this->_flags |= self::IS_SET_ITALIC; - if(TPropertyValue::ensureBoolean($value)) - $this->_flags |= self::IS_ITALIC; - else - $this->_flags &= ~self::IS_ITALIC; - } - - /** - * @return boolean whether the font is overlined. Defaults to false. - */ - public function getOverline() - { - return ($this->_flags & self::IS_OVERLINE)!==0; - } - - /** - * @param boolean whether the font is overlined - */ - public function setOverline($value) - { - $this->_flags |= self::IS_SET_OVERLINE; - if(TPropertyValue::ensureBoolean($value)) - $this->_flags |= self::IS_OVERLINE; - else - $this->_flags &= ~self::IS_OVERLINE; - } - - /** - * @return string the font size - */ - public function getSize() - { - return $this->_size; - } - - /** - * @param string the font size - */ - public function setSize($value) - { - $this->_flags |= self::IS_SET_SIZE; - $this->_size=$value; - } - - /** - * @return boolean whether the font is strikeout. Defaults to false. - */ - public function getStrikeout() - { - return ($this->_flags & self::IS_STRIKEOUT)!==0; - } - - /** - * @param boolean whether the font is strikeout - */ - public function setStrikeout($value) - { - $this->_flags |= self::IS_SET_STRIKEOUT; - if(TPropertyValue::ensureBoolean($value)) - $this->_flags |= self::IS_STRIKEOUT; - else - $this->_flags &= ~self::IS_STRIKEOUT; - } - - /** - * @return boolean whether the font is underlined. Defaults to false. - */ - public function getUnderline() - { - return ($this->_flags & self::IS_UNDERLINE)!==0; - } - - /** - * @param boolean whether the font is underlined - */ - public function setUnderline($value) - { - $this->_flags |= self::IS_SET_UNDERLINE; - if(TPropertyValue::ensureBoolean($value)) - $this->_flags |= self::IS_UNDERLINE; - else - $this->_flags &= ~self::IS_UNDERLINE; - } - - /** - * @return string the font name (family) - */ - public function getName() - { - return $this->_name; - } - - /** - * @param string the font name (family) - */ - public function setName($value) - { - $this->_flags |= self::IS_SET_NAME; - $this->_name=$value; - } - - /** - * @return boolean whether the font is empty - */ - public function getIsEmpty() - { - return !$this->_flags; - } - - /** - * Clears up the font. - */ - public function reset() - { - $this->_flags=0; - $this->_name=''; - $this->_size=''; - } - - /** - * Merges the font with a new one. - * If a font field is not set in the font, it will be overwritten with - * the new one. - * @param TFont the new font - */ - public function mergeWith($font) - { - if($font===null || $font->_flags===0) - return; - if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD)) - $this->setBold($font->getBold()); - if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC)) - $this->setItalic($font->getItalic()); - if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE)) - $this->setOverline($font->getOverline()); - if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT)) - $this->setStrikeout($font->getStrikeout()); - if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE)) - $this->setUnderline($font->getUnderline()); - if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE)) - $this->setSize($font->getSize()); - if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME)) - $this->setName($font->getName()); - } - - /** - * Copies the fields in a new font to this font. - * If a font field is set in the new font, the corresponding field - * in this font will be overwritten. - * @param TFont the new font - */ - public function copyFrom($font) - { - if($font===null || $font->_flags===0) - return; - if($font->_flags & self::IS_SET_BOLD) - $this->setBold($font->getBold()); - if($font->_flags & self::IS_SET_ITALIC) - $this->setItalic($font->getItalic()); - if($font->_flags & self::IS_SET_OVERLINE) - $this->setOverline($font->getOverline()); - if($font->_flags & self::IS_SET_STRIKEOUT) - $this->setStrikeout($font->getStrikeout()); - if($font->_flags & self::IS_SET_UNDERLINE) - $this->setUnderline($font->getUnderline()); - if($font->_flags & self::IS_SET_SIZE) - $this->setSize($font->getSize()); - if($font->_flags & self::IS_SET_NAME) - $this->setName($font->getName()); - } - - /** - * @return string the font in a css style string representation. - */ - public function toString() - { - if($this->_flags===0) - return ''; - $str=''; - if($this->_flags & self::IS_SET_BOLD) - $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;'); - if($this->_flags & self::IS_SET_ITALIC) - $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;'); - $textDec=''; - if($this->_flags & self::IS_UNDERLINE) - $textDec.='underline'; - if($this->_flags & self::IS_OVERLINE) - $textDec.=' overline'; - if($this->_flags & self::IS_STRIKEOUT) - $textDec.=' line-through'; - $textDec=ltrim($textDec); - if($textDec!=='') - $str.='text-decoration:'.$textDec.';'; - if($this->_size!=='') - $str.='font-size:'.$this->_size.';'; - if($this->_name!=='') - $str.='font-family:'.$this->_name.';'; - return $str; - } - - /** - * Adds attributes related to CSS styles to renderer. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function addAttributesToRender($writer) - { - if($this->_flags===0) - return; - if($this->_flags & self::IS_SET_BOLD) - $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal')); - if($this->_flags & self::IS_SET_ITALIC) - $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal')); - $textDec=''; - if($this->_flags & self::IS_UNDERLINE) - $textDec.='underline'; - if($this->_flags & self::IS_OVERLINE) - $textDec.=' overline'; - if($this->_flags & self::IS_STRIKEOUT) - $textDec.=' line-through'; - $textDec=ltrim($textDec); - if($textDec!=='') - $writer->addStyleAttribute('text-decoration',$textDec); - if($this->_size!=='') - $writer->addStyleAttribute('font-size',$this->_size); - if($this->_name!=='') - $writer->addStyleAttribute('font-family',$this->_name); - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TFont class + * + * TFont encapsulates the CSS style fields related with font settings. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TFont extends TComponent +{ + /** + * Bits indicating the font states. + */ + const IS_BOLD=0x01; + const IS_ITALIC=0x02; + const IS_OVERLINE=0x04; + const IS_STRIKEOUT=0x08; + const IS_UNDERLINE=0x10; + + /** + * Bits indicating whether particular font states are changed. + */ + const IS_SET_BOLD=0x01000; + const IS_SET_ITALIC=0x02000; + const IS_SET_OVERLINE=0x04000; + const IS_SET_STRIKEOUT=0x08000; + const IS_SET_UNDERLINE=0x10000; + const IS_SET_SIZE=0x20000; + const IS_SET_NAME=0x40000; + + /** + * @var integer bits representing various states + */ + private $_flags=0; + /** + * @var string font name + */ + private $_name=''; + /** + * @var string font size + */ + private $_size=''; + + /** + * @return boolean whether the font is in bold face. Defaults to false. + */ + public function getBold() + { + return ($this->_flags & self::IS_BOLD)!==0; + } + + /** + * @param boolean whether the font is in bold face + */ + public function setBold($value) + { + $this->_flags |= self::IS_SET_BOLD; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_BOLD; + else + $this->_flags &= ~self::IS_BOLD; + } + + /** + * @return boolean whether the font is in italic face. Defaults to false. + */ + public function getItalic() + { + return ($this->_flags & self::IS_ITALIC)!==0; + } + + /** + * @param boolean whether the font is italic + */ + public function setItalic($value) + { + $this->_flags |= self::IS_SET_ITALIC; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_ITALIC; + else + $this->_flags &= ~self::IS_ITALIC; + } + + /** + * @return boolean whether the font is overlined. Defaults to false. + */ + public function getOverline() + { + return ($this->_flags & self::IS_OVERLINE)!==0; + } + + /** + * @param boolean whether the font is overlined + */ + public function setOverline($value) + { + $this->_flags |= self::IS_SET_OVERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_OVERLINE; + else + $this->_flags &= ~self::IS_OVERLINE; + } + + /** + * @return string the font size + */ + public function getSize() + { + return $this->_size; + } + + /** + * @param string the font size + */ + public function setSize($value) + { + $this->_flags |= self::IS_SET_SIZE; + $this->_size=$value; + } + + /** + * @return boolean whether the font is strikeout. Defaults to false. + */ + public function getStrikeout() + { + return ($this->_flags & self::IS_STRIKEOUT)!==0; + } + + /** + * @param boolean whether the font is strikeout + */ + public function setStrikeout($value) + { + $this->_flags |= self::IS_SET_STRIKEOUT; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_STRIKEOUT; + else + $this->_flags &= ~self::IS_STRIKEOUT; + } + + /** + * @return boolean whether the font is underlined. Defaults to false. + */ + public function getUnderline() + { + return ($this->_flags & self::IS_UNDERLINE)!==0; + } + + /** + * @param boolean whether the font is underlined + */ + public function setUnderline($value) + { + $this->_flags |= self::IS_SET_UNDERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_UNDERLINE; + else + $this->_flags &= ~self::IS_UNDERLINE; + } + + /** + * @return string the font name (family) + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string the font name (family) + */ + public function setName($value) + { + $this->_flags |= self::IS_SET_NAME; + $this->_name=$value; + } + + /** + * @return boolean whether the font is empty + */ + public function getIsEmpty() + { + return !$this->_flags; + } + + /** + * Clears up the font. + */ + public function reset() + { + $this->_flags=0; + $this->_name=''; + $this->_size=''; + } + + /** + * Merges the font with a new one. + * If a font field is not set in the font, it will be overwritten with + * the new one. + * @param TFont the new font + */ + public function mergeWith($font) + { + if($font===null || $font->_flags===0) + return; + if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD)) + $this->setBold($font->getBold()); + if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC)) + $this->setItalic($font->getItalic()); + if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE)) + $this->setOverline($font->getOverline()); + if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT)) + $this->setStrikeout($font->getStrikeout()); + if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE)) + $this->setUnderline($font->getUnderline()); + if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE)) + $this->setSize($font->getSize()); + if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME)) + $this->setName($font->getName()); + } + + /** + * Copies the fields in a new font to this font. + * If a font field is set in the new font, the corresponding field + * in this font will be overwritten. + * @param TFont the new font + */ + public function copyFrom($font) + { + if($font===null || $font->_flags===0) + return; + if($font->_flags & self::IS_SET_BOLD) + $this->setBold($font->getBold()); + if($font->_flags & self::IS_SET_ITALIC) + $this->setItalic($font->getItalic()); + if($font->_flags & self::IS_SET_OVERLINE) + $this->setOverline($font->getOverline()); + if($font->_flags & self::IS_SET_STRIKEOUT) + $this->setStrikeout($font->getStrikeout()); + if($font->_flags & self::IS_SET_UNDERLINE) + $this->setUnderline($font->getUnderline()); + if($font->_flags & self::IS_SET_SIZE) + $this->setSize($font->getSize()); + if($font->_flags & self::IS_SET_NAME) + $this->setName($font->getName()); + } + + /** + * @return string the font in a css style string representation. + */ + public function toString() + { + if($this->_flags===0) + return ''; + $str=''; + if($this->_flags & self::IS_SET_BOLD) + $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;'); + if($this->_flags & self::IS_SET_ITALIC) + $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;'); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $str.='text-decoration:'.$textDec.';'; + if($this->_size!=='') + $str.='font-size:'.$this->_size.';'; + if($this->_name!=='') + $str.='font-family:'.$this->_name.';'; + return $str; + } + + /** + * Adds attributes related to CSS styles to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if($this->_flags===0) + return; + if($this->_flags & self::IS_SET_BOLD) + $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal')); + if($this->_flags & self::IS_SET_ITALIC) + $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal')); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $writer->addStyleAttribute('text-decoration',$textDec); + if($this->_size!=='') + $writer->addStyleAttribute('font-size',$this->_size); + if($this->_name!=='') + $writer->addStyleAttribute('font-family',$this->_name); + } +} diff --git a/framework/Web/UI/WebControls/THead.php b/framework/Web/UI/WebControls/THead.php index c0042c22..7966f2d3 100644 --- a/framework/Web/UI/WebControls/THead.php +++ b/framework/Web/UI/WebControls/THead.php @@ -1,377 +1,377 @@ - and Qiang Xue - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI - */ - -/** - * THead class - * - * THead displays a head element on a page. It displays the content - * enclosed in its body and the page title set by the - * {@link setTitle Title} property. In addition, stylesheets and JavaScripts registered via - * {@link TClientScriptManager::registerStyleSheet}, {@link TClientScriptManager::registerStyleSheetFile} - * {@link TClientScriptManager::registerHeadJavaScript}, and - * {@link TClientScriptManager::registerHeadJavaScriptFile} will also be displayed - * in the head. - * THead also manages and displays meta tags through its {@link getMetaTags MetaTags} - * property. You can add a meta object to the collection in code dynamically, - * or add it in template using the following syntax, - * - * - * - * - * - * - * - * Note, {@link TPage} has a property {@link TPage::getHead Head} that refers to - * the THead control currently on the page. A page can have at most one THead - * control. Although not required, it is recommended to place a THead on your page. - * Without a THead on the page, stylesheets and javascripts in the current page - * theme will not be rendered. - * - * @author Marcus Nyeholt and Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0 - */ -class THead extends TControl -{ - /** - * @var TList list of meta name tags to be loaded by {@link THead} - */ - private $_metaTags=null; - - /** - * Registers the head control with the current page. - * This method is invoked when the control enters 'Init' stage. - * The method raises 'Init' event. - * 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 - */ - public function onInit($param) - { - parent::onInit($param); - $this->getPage()->setHead($this); - } - - /** - * Processes an object that is created during parsing template. - * This method adds TMetaTag components into the {@link getMetaTags MetaTags} - * collection of the head control. - * @param string|TComponent text string or component parsed and instantiated in template - * @see createdOnTemplate - */ - public function addParsedObject($object) - { - if($object instanceof TMetaTag) - $this->getMetaTags()->add($object); - else - parent::addParsedObject($object); - } - - /** - * @return string the page title. - */ - public function getTitle() - { - return $this->getViewState('Title',''); - } - - /** - * Sets the page title. - * This title will be rendered only if the {@link TPage::getTitle Title} property - * of the page is empty. - * @param string the page title. - */ - public function setTitle($value) - { - $this->setViewState('Title',$value,''); - } - - /** - * @return string base URL of the page. This URL is rendered as the 'href' attribute of tag. Defaults to ''. - */ - public function getBaseUrl() - { - return $this->getViewState('BaseUrl',''); - } - - /** - * @param string base URL of the page. This URL is rendered as the 'href' attribute of tag. - */ - public function setBaseUrl($url) - { - $this->setViewState('BaseUrl',$url,''); - } - - /** - * @return string the URL for the shortcut icon of the page. Defaults to ''. - */ - public function getShortcutIcon() - { - return $this->getViewState('ShortcutIcon',''); - } - - /** - * @param string the URL for the shortcut icon of the page. - */ - public function setShortcutIcon($url) - { - $this->setViewState('ShortcutIcon',$url,''); - } - - /** - * @return TMetaTagCollection meta tag collection - */ - public function getMetaTags() - { - if(($metaTags=$this->getViewState('MetaTags',null))===null) - { - $metaTags=new TMetaTagCollection; - $this->setViewState('MetaTags',$metaTags,null); - } - return $metaTags; - } - - /** - * Renders the head control. - * @param THtmlWriter the writer for rendering purpose. - */ - public function render($writer) - { - $page=$this->getPage(); - $title=$this->getTitle(); - $writer->write("\n".THttpUtility::htmlEncode($title)."\n"); - if(($baseUrl=$this->getBaseUrl())!=='') - $writer->write('\n"); - if(($icon=$this->getShortcutIcon())!=='') - $writer->write('\n"); - - if(($metaTags=$this->getMetaTags())!==null) - { - foreach($metaTags as $metaTag) - { - $metaTag->render($writer); - $writer->writeLine(); - } - } - $cs=$page->getClientScript(); - $cs->renderStyleSheetFiles($writer); - $cs->renderStyleSheets($writer); - if($page->getClientSupportsJavaScript()) - { - $cs->renderHeadScriptFiles($writer); - $cs->renderHeadScripts($writer); - } - parent::render($writer); - $writer->write("\n"); - } -} - -/** - * TMetaTag class. - * - * TMetaTag represents a meta tag appearing in a page head section. - * You can set its {@link setID ID}, {@link setHttpEquiv HttpEquiv}, - * {@link setName Name}, {@link setContent Content}, {@link setScheme Scheme} - * properties, which correspond to id, http-equiv, name, content, and scheme - * attributes for a meta tag, respectively. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TMetaTag extends TComponent -{ - /** - * @var string id of the meta tag - */ - private $_id=''; - /** - * @var string http-equiv attribute of the meta tag - */ - private $_httpEquiv=''; - /** - * @var string name attribute of the meta tag - */ - private $_name=''; - /** - * @var string content attribute of the meta tag - */ - private $_content=''; - /** - * @var string scheme attribute of the meta tag - */ - private $_scheme=''; - - /** - * @return string id of the meta tag - */ - public function getID() - { - return $this->_id; - } - - /** - * @param string id of the meta tag - */ - public function setID($value) - { - $this->_id=$value; - } - - /** - * @return string http-equiv attribute of the meta tag - */ - public function getHttpEquiv() - { - return $this->_httpEquiv; - } - - /** - * @param string http-equiv attribute of the meta tag - */ - public function setHttpEquiv($value) - { - $this->_httpEquiv=$value; - } - - /** - * @return string name attribute of the meta tag - */ - public function getName() - { - return $this->_name; - } - - /** - * @param string name attribute of the meta tag - */ - public function setName($value) - { - $this->_name=$value; - } - - /** - * @return string content attribute of the meta tag - */ - public function getContent() - { - return $this->_content; - } - - /** - * @param string content attribute of the meta tag - */ - public function setContent($value) - { - $this->_content=$value; - } - - /** - * @return string scheme attribute of the meta tag - */ - public function getScheme() - { - return $this->_scheme; - } - - /** - * @param string scheme attribute of the meta tag - */ - public function setScheme($value) - { - $this->_scheme=$value; - } - - /** - * Renders the meta tag. - * @param THtmlWriter writer for the rendering purpose - */ - public function render($writer) - { - if($this->_id!=='') - $writer->addAttribute('id',$this->_id); - if($this->_name!=='') - $writer->addAttribute('name',$this->_name); - if($this->_httpEquiv!=='') - $writer->addAttribute('http-equiv',$this->_httpEquiv); - if($this->_scheme!=='') - $writer->addAttribute('scheme',$this->_scheme); - $writer->addAttribute('content',$this->_content); - $writer->renderBeginTag('meta'); - $writer->renderEndTag(); - } -} - - -/** - * TMetaTagCollection class - * - * TMetaTagCollection represents a collection of meta tags - * contained in a {@link THead} control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TMetaTagCollection extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing type - * check on the item being added. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TMetaTag} - */ - public function insertAt($index,$item) - { - if($item instanceof TMetaTag) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('metatagcollection_metatag_invalid'); - } - - /** - * Finds the lowest cardinal index of the meta tag whose id is the one being looked for. - * @param string the ID of the meta tag to be looked for - * @return integer the index of the meta tag found, -1 if not found. - */ - public function findIndexByID($id) - { - $index=0; - foreach($this as $item) - { - if($item->getID()===$id) - return $index; - $index++; - } - return -1; - } - - /** - * Finds the item whose value is the one being looked for. - * @param string the id of the meta tag to be looked for - * @return TMetaTag the meta tag found, null if not found. - */ - public function findMetaTagByID($id) - { - if(($index=$this->findIndexByID($id))>=0) - return $this->itemAt($index); - else - return null; - } -} - -?> + and Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +/** + * THead class + * + * THead displays a head element on a page. It displays the content + * enclosed in its body and the page title set by the + * {@link setTitle Title} property. In addition, stylesheets and JavaScripts registered via + * {@link TClientScriptManager::registerStyleSheet}, {@link TClientScriptManager::registerStyleSheetFile} + * {@link TClientScriptManager::registerHeadJavaScript}, and + * {@link TClientScriptManager::registerHeadJavaScriptFile} will also be displayed + * in the head. + * THead also manages and displays meta tags through its {@link getMetaTags MetaTags} + * property. You can add a meta object to the collection in code dynamically, + * or add it in template using the following syntax, + * + * + * + * + * + * + * + * Note, {@link TPage} has a property {@link TPage::getHead Head} that refers to + * the THead control currently on the page. A page can have at most one THead + * control. Although not required, it is recommended to place a THead on your page. + * Without a THead on the page, stylesheets and javascripts in the current page + * theme will not be rendered. + * + * @author Marcus Nyeholt and Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0 + */ +class THead extends TControl +{ + /** + * @var TList list of meta name tags to be loaded by {@link THead} + */ + private $_metaTags=null; + + /** + * Registers the head control with the current page. + * This method is invoked when the control enters 'Init' stage. + * The method raises 'Init' event. + * 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 + */ + public function onInit($param) + { + parent::onInit($param); + $this->getPage()->setHead($this); + } + + /** + * Processes an object that is created during parsing template. + * This method adds TMetaTag components into the {@link getMetaTags MetaTags} + * collection of the head control. + * @param string|TComponent text string or component parsed and instantiated in template + * @see createdOnTemplate + */ + public function addParsedObject($object) + { + if($object instanceof TMetaTag) + $this->getMetaTags()->add($object); + else + parent::addParsedObject($object); + } + + /** + * @return string the page title. + */ + public function getTitle() + { + return $this->getViewState('Title',''); + } + + /** + * Sets the page title. + * This title will be rendered only if the {@link TPage::getTitle Title} property + * of the page is empty. + * @param string the page title. + */ + public function setTitle($value) + { + $this->setViewState('Title',$value,''); + } + + /** + * @return string base URL of the page. This URL is rendered as the 'href' attribute of tag. Defaults to ''. + */ + public function getBaseUrl() + { + return $this->getViewState('BaseUrl',''); + } + + /** + * @param string base URL of the page. This URL is rendered as the 'href' attribute of tag. + */ + public function setBaseUrl($url) + { + $this->setViewState('BaseUrl',$url,''); + } + + /** + * @return string the URL for the shortcut icon of the page. Defaults to ''. + */ + public function getShortcutIcon() + { + return $this->getViewState('ShortcutIcon',''); + } + + /** + * @param string the URL for the shortcut icon of the page. + */ + public function setShortcutIcon($url) + { + $this->setViewState('ShortcutIcon',$url,''); + } + + /** + * @return TMetaTagCollection meta tag collection + */ + public function getMetaTags() + { + if(($metaTags=$this->getViewState('MetaTags',null))===null) + { + $metaTags=new TMetaTagCollection; + $this->setViewState('MetaTags',$metaTags,null); + } + return $metaTags; + } + + /** + * Renders the head control. + * @param THtmlWriter the writer for rendering purpose. + */ + public function render($writer) + { + $page=$this->getPage(); + $title=$this->getTitle(); + $writer->write("\n".THttpUtility::htmlEncode($title)."\n"); + if(($baseUrl=$this->getBaseUrl())!=='') + $writer->write('\n"); + if(($icon=$this->getShortcutIcon())!=='') + $writer->write('\n"); + + if(($metaTags=$this->getMetaTags())!==null) + { + foreach($metaTags as $metaTag) + { + $metaTag->render($writer); + $writer->writeLine(); + } + } + $cs=$page->getClientScript(); + $cs->renderStyleSheetFiles($writer); + $cs->renderStyleSheets($writer); + if($page->getClientSupportsJavaScript()) + { + $cs->renderHeadScriptFiles($writer); + $cs->renderHeadScripts($writer); + } + parent::render($writer); + $writer->write("\n"); + } +} + +/** + * TMetaTag class. + * + * TMetaTag represents a meta tag appearing in a page head section. + * You can set its {@link setID ID}, {@link setHttpEquiv HttpEquiv}, + * {@link setName Name}, {@link setContent Content}, {@link setScheme Scheme} + * properties, which correspond to id, http-equiv, name, content, and scheme + * attributes for a meta tag, respectively. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TMetaTag extends TComponent +{ + /** + * @var string id of the meta tag + */ + private $_id=''; + /** + * @var string http-equiv attribute of the meta tag + */ + private $_httpEquiv=''; + /** + * @var string name attribute of the meta tag + */ + private $_name=''; + /** + * @var string content attribute of the meta tag + */ + private $_content=''; + /** + * @var string scheme attribute of the meta tag + */ + private $_scheme=''; + + /** + * @return string id of the meta tag + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of the meta tag + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return string http-equiv attribute of the meta tag + */ + public function getHttpEquiv() + { + return $this->_httpEquiv; + } + + /** + * @param string http-equiv attribute of the meta tag + */ + public function setHttpEquiv($value) + { + $this->_httpEquiv=$value; + } + + /** + * @return string name attribute of the meta tag + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string name attribute of the meta tag + */ + public function setName($value) + { + $this->_name=$value; + } + + /** + * @return string content attribute of the meta tag + */ + public function getContent() + { + return $this->_content; + } + + /** + * @param string content attribute of the meta tag + */ + public function setContent($value) + { + $this->_content=$value; + } + + /** + * @return string scheme attribute of the meta tag + */ + public function getScheme() + { + return $this->_scheme; + } + + /** + * @param string scheme attribute of the meta tag + */ + public function setScheme($value) + { + $this->_scheme=$value; + } + + /** + * Renders the meta tag. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->_id!=='') + $writer->addAttribute('id',$this->_id); + if($this->_name!=='') + $writer->addAttribute('name',$this->_name); + if($this->_httpEquiv!=='') + $writer->addAttribute('http-equiv',$this->_httpEquiv); + if($this->_scheme!=='') + $writer->addAttribute('scheme',$this->_scheme); + $writer->addAttribute('content',$this->_content); + $writer->renderBeginTag('meta'); + $writer->renderEndTag(); + } +} + + +/** + * TMetaTagCollection class + * + * TMetaTagCollection represents a collection of meta tags + * contained in a {@link THead} control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TMetaTagCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing type + * check on the item being added. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TMetaTag} + */ + public function insertAt($index,$item) + { + if($item instanceof TMetaTag) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('metatagcollection_metatag_invalid'); + } + + /** + * Finds the lowest cardinal index of the meta tag whose id is the one being looked for. + * @param string the ID of the meta tag to be looked for + * @return integer the index of the meta tag found, -1 if not found. + */ + public function findIndexByID($id) + { + $index=0; + foreach($this as $item) + { + if($item->getID()===$id) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the item whose value is the one being looked for. + * @param string the id of the meta tag to be looked for + * @return TMetaTag the meta tag found, null if not found. + */ + public function findMetaTagByID($id) + { + if(($index=$this->findIndexByID($id))>=0) + return $this->itemAt($index); + else + return null; + } +} + +?> diff --git a/framework/Web/UI/WebControls/THiddenField.php b/framework/Web/UI/WebControls/THiddenField.php index ac8ddfff..ec330d54 100644 --- a/framework/Web/UI/WebControls/THiddenField.php +++ b/framework/Web/UI/WebControls/THiddenField.php @@ -1,117 +1,117 @@ - - * @link http://www.xisc.com/ + + * @link http://www.xisc.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * THiddenField class - * - * THiddenField displays a hidden input field on a Web page. - * The value of the input field can be accessed via {@link getValue Value} property. - * If upon postback the value is changed, a {@link onValueChanged OnValueChanged} - * event will be raised. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class THiddenField extends TControl implements IPostBackDataHandler, IValidatable, IDataRenderer -{ - private $_dataChanged=false; + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * THiddenField class + * + * THiddenField displays a hidden input field on a Web page. + * The value of the input field can be accessed via {@link getValue Value} property. + * If upon postback the value is changed, a {@link onValueChanged OnValueChanged} + * event will be raised. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THiddenField extends TControl implements IPostBackDataHandler, IValidatable, IDataRenderer +{ + private $_dataChanged=false; private $_isValid=true; - - /** - * @return string tag name of the hidden field. - */ - protected function getTagName() - { - return 'input'; - } - - /** - * Sets focus to this control. - * This method overrides the parent implementation by forbidding setting focus to this control. - */ - public function focus() - { - throw new TNotSupportedException('hiddenfield_focus_unsupported'); - } - - /** - * Renders the control. - * This method overrides the parent implementation by rendering - * the hidden field input element. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - $uniqueID=$this->getUniqueID(); - $this->getPage()->ensureRenderInForm($this); - $writer->addAttribute('type','hidden'); - if($uniqueID!=='') - $writer->addAttribute('name',$uniqueID); - if($this->getID()!=='') - $writer->addAttribute('id',$this->getClientID()); - if(($value=$this->getValue())!=='') - $writer->addAttribute('value',$value); - - if($this->getHasAttributes()) - { - foreach($this->getAttributes() as $name=>$value) - $writer->addAttribute($name,$value); - } - - $writer->renderBeginTag('input'); - $writer->renderEndTag(); - } - - /** - * Loads hidden field data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - $value=$values[$key]; - if($value===$this->getValue()) - return false; - else - { - $this->setValue($value); - return $this->_dataChanged=true; - } - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * Returns the value to be validated. - * This methid is required by IValidatable interface. - * @return mixed the value of the property to be validated. - */ - public function getValidationPropertyValue() - { - return $this->getValue(); - } - + + /** + * @return string tag name of the hidden field. + */ + protected function getTagName() + { + return 'input'; + } + + /** + * Sets focus to this control. + * This method overrides the parent implementation by forbidding setting focus to this control. + */ + public function focus() + { + throw new TNotSupportedException('hiddenfield_focus_unsupported'); + } + + /** + * Renders the control. + * This method overrides the parent implementation by rendering + * the hidden field input element. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + $uniqueID=$this->getUniqueID(); + $this->getPage()->ensureRenderInForm($this); + $writer->addAttribute('type','hidden'); + if($uniqueID!=='') + $writer->addAttribute('name',$uniqueID); + if($this->getID()!=='') + $writer->addAttribute('id',$this->getClientID()); + if(($value=$this->getValue())!=='') + $writer->addAttribute('value',$value); + + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + + $writer->renderBeginTag('input'); + $writer->renderEndTag(); + } + + /** + * Loads hidden field data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=$values[$key]; + if($value===$this->getValue()) + return false; + else + { + $this->setValue($value); + return $this->_dataChanged=true; + } + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getValue(); + } + /** * Returns true if this control validated successfully. * Defaults to true. @@ -129,96 +129,96 @@ class THiddenField extends TControl implements IPostBackDataHandler, IValidatabl $this->_isValid=TPropertyValue::ensureBoolean($value); } - /** - * Raises postdata changed event. - * This method calls {@link onValueChanged} method. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - $this->onValueChanged(null); - } - - /** - * This method is invoked when the value of the {@link getValue Value} property changes between posts to the server. - * The method raises 'OnValueChanged' event to fire up the event delegates. - * If you override this method, be sure to call the parent implementation - * so that the attached event handlers can be invoked. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onValueChanged($param) - { - $this->raiseEvent('OnValueChanged',$this,$param); - } - - /** - * @return string the value of the THiddenField - */ - public function getValue() - { - return $this->getViewState('Value',''); - } - - /** - * Sets the value of the THiddenField - * @param string the value to be set - */ - public function setValue($value) - { - $this->setViewState('Value',$value,''); - } - - /** - * Returns the value of the hidden field. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getValue()}. - * @return string value of the hidden field - * @see getValue - * @since 3.1.0 - */ - public function getData() - { - return $this->getValue(); - } - - /** - * Sets the value of the hidden field. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setValue()}. - * @param string value of the hidden field - * @see setValue - * @since 3.1.0 - */ - public function setData($value) - { - $this->setValue($value); - } - - - /** - * @return boolean whether theming is enabled for this control. Defaults to false. - */ - public function getEnableTheming() - { - return false; - } - - /** - * @param boolean whether theming is enabled for this control. - * @throws TNotSupportedException This method is always thrown when calling this method. - */ - public function setEnableTheming($value) - { - throw new TNotSupportedException('hiddenfield_theming_unsupported'); - } - - /** - * @param string Skin ID - * @throws TNotSupportedException This method is always thrown when calling this method. - */ - public function setSkinID($value) - { - throw new TNotSupportedException('hiddenfield_skinid_unsupported'); - } -} - + /** + * Raises postdata changed event. + * This method calls {@link onValueChanged} method. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onValueChanged(null); + } + + /** + * This method is invoked when the value of the {@link getValue Value} property changes between posts to the server. + * The method raises 'OnValueChanged' event to fire up the event delegates. + * If you override this method, be sure to call the parent implementation + * so that the attached event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onValueChanged($param) + { + $this->raiseEvent('OnValueChanged',$this,$param); + } + + /** + * @return string the value of the THiddenField + */ + public function getValue() + { + return $this->getViewState('Value',''); + } + + /** + * Sets the value of the THiddenField + * @param string the value to be set + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Returns the value of the hidden field. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string value of the hidden field + * @see getValue + * @since 3.1.0 + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Sets the value of the hidden field. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string value of the hidden field + * @see setValue + * @since 3.1.0 + */ + public function setData($value) + { + $this->setValue($value); + } + + + /** + * @return boolean whether theming is enabled for this control. Defaults to false. + */ + public function getEnableTheming() + { + return false; + } + + /** + * @param boolean whether theming is enabled for this control. + * @throws TNotSupportedException This method is always thrown when calling this method. + */ + public function setEnableTheming($value) + { + throw new TNotSupportedException('hiddenfield_theming_unsupported'); + } + + /** + * @param string Skin ID + * @throws TNotSupportedException This method is always thrown when calling this method. + */ + public function setSkinID($value) + { + throw new TNotSupportedException('hiddenfield_skinid_unsupported'); + } +} + diff --git a/framework/Web/UI/WebControls/THtmlElement.php b/framework/Web/UI/WebControls/THtmlElement.php index 3889ee50..29cd0057 100644 --- a/framework/Web/UI/WebControls/THtmlElement.php +++ b/framework/Web/UI/WebControls/THtmlElement.php @@ -1,68 +1,68 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TWebControl'); - -/** - * THtmlElement class. - * - * THtmlElement represents a generic HTML element whose tag name is specified - * via {@link setTagName TagName} property. Because THtmlElement extends from - * {@link TWebControl}, it enjoys all its functionalities. - * - * To change the default tag your subclass should override {@link getDefaultTagName} - * - * @author Qiang Xue - * @author Brad Anderson - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.2 - */ -class THtmlElement extends TWebControl -{ - /** - * @var the tag of this element - */ - private $_tagName=null; - - /** - * @return string the tag name of this control. Defaults to 'span'. - */ - public function getTagName() - { - return ($this->_tagName !== null) ? $this->_tagName : ($this->_tagName = $this->getDefaultTagName()); - } - - /** - * @param string the tag name of this control. - */ - public function setTagName($value) - { - $this->_tagName=TPropertyValue::ensureString($value); - } - - /** - * This is the default tag when no other is specified - * @return string the default tag - */ - public function getDefaultTagName() { - return 'span'; - } - - /** - * This tells you if this TagName has deviated from the original - * @return boolean true if TagName has deviated from the default. - */ - public function getIsMutated() { - return $this->_tagName !== null && $this->_tagName != $this->getDefaultTagName(); - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TWebControl'); + +/** + * THtmlElement class. + * + * THtmlElement represents a generic HTML element whose tag name is specified + * via {@link setTagName TagName} property. Because THtmlElement extends from + * {@link TWebControl}, it enjoys all its functionalities. + * + * To change the default tag your subclass should override {@link getDefaultTagName} + * + * @author Qiang Xue + * @author Brad Anderson + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +class THtmlElement extends TWebControl +{ + /** + * @var the tag of this element + */ + private $_tagName=null; + + /** + * @return string the tag name of this control. Defaults to 'span'. + */ + public function getTagName() + { + return ($this->_tagName !== null) ? $this->_tagName : ($this->_tagName = $this->getDefaultTagName()); + } + + /** + * @param string the tag name of this control. + */ + public function setTagName($value) + { + $this->_tagName=TPropertyValue::ensureString($value); + } + + /** + * This is the default tag when no other is specified + * @return string the default tag + */ + public function getDefaultTagName() { + return 'span'; + } + + /** + * This tells you if this TagName has deviated from the original + * @return boolean true if TagName has deviated from the default. + */ + public function getIsMutated() { + return $this->_tagName !== null && $this->_tagName != $this->getDefaultTagName(); + } +} diff --git a/framework/Web/UI/WebControls/THyperLink.php b/framework/Web/UI/WebControls/THyperLink.php index 1e32d6c9..b745f7b0 100644 --- a/framework/Web/UI/WebControls/THyperLink.php +++ b/framework/Web/UI/WebControls/THyperLink.php @@ -1,227 +1,227 @@ - - * @link http://www.xisc.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * THyperLink class - * - * THyperLink displays a hyperlink on a page. The hyperlink URL is specified - * via the {@link setNavigateUrl NavigateUrl} property, and link text is via - * the {@link setText Text} property. It is also possible to display an image - * by setting the {@link setImageUrl ImageUrl} property. In this case, - * {@link getText Text} is displayed as the alternate text of the image. - * The link target is specified via the {@link setTarget Target} property. - * If both {@link getImageUrl ImageUrl} and {@link getText Text} are empty, - * the content enclosed within the control tag will be rendered. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class THyperLink extends TWebControl implements IDataRenderer -{ - /** - * @return string tag name of the hyperlink - */ - protected function getTagName() - { - return 'a'; - } - - /** - * Adds attributes related to a hyperlink element to renderer. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $isEnabled=$this->getEnabled(true); - if($this->getEnabled() && !$isEnabled) - $writer->addAttribute('disabled','disabled'); - parent::addAttributesToRender($writer); - if(($url=$this->getNavigateUrl())!=='' && $isEnabled) - $writer->addAttribute('href',$url); - if(($target=$this->getTarget())!=='') - $writer->addAttribute('target',$target); - } - - /** - * Renders the body content of the hyperlink. - * @param THtmlWriter the writer for rendering - */ - public function renderContents($writer) - { - if(($imageUrl=$this->getImageUrl())==='') - { - if(($text=$this->getText())!=='') - $writer->write(THttpUtility::htmlEncode($text)); - else if($this->getHasControls()) - parent::renderContents($writer); - else - $writer->write(THttpUtility::htmlEncode($this->getNavigateUrl())); - } - else - { - $this->createImage($imageUrl)->renderControl($writer); - } - } - - /** - * Gets the TImage for rendering the ImageUrl property. This is not for - * creating dynamic images. - * @param string image url. - * @return TImage image control for rendering. - */ - protected function createImage($imageUrl) - { - $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); - $image->setImageUrl($imageUrl); - if(($width=$this->getImageWidth())!=='') - $image->setWidth($width); - if(($height=$this->getImageHeight())!=='') - $image->setHeight($height); - if(($toolTip=$this->getToolTip())!=='') - $image->setToolTip($toolTip); - if(($text=$this->getText())!=='') - $image->setAlternateText($text); - $image->setBorderWidth('0'); - return $image; - } - - /** - * @return string the text caption of the THyperLink - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * Sets the text caption of the THyperLink. - * @param string the text caption to be set - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * @return string height of the image in the THyperLink - */ - public function getImageHeight() - { - return $this->getViewState('ImageHeight',''); - } - - /** - * Sets the height of the image in the THyperLink - * @param string height of the image in the THyperLink - */ - public function setImageHeight($value) - { - $this->setViewSTate('ImageHeight',$value,''); - } - - /** - * @return string the location of the image file for the THyperLink - */ - public function getImageUrl() - { - return $this->getViewState('ImageUrl',''); - } - - /** - * Sets the location of image file of the THyperLink. - * @param string the image file location - */ - public function setImageUrl($value) - { - $this->setViewState('ImageUrl',$value,''); - } - - /** - * @return string width of the image in the THyperLink - */ - public function getImageWidth() - { - return $this->getViewState('ImageWidth',''); - } - - /** - * Sets the width of the image in the THyperLink - * @param string width of the image - */ - public function setImageWidth($value) - { - $this->setViewState('ImageWidth',$value,''); - } - - /** - * @return string the URL to link to when the THyperLink component is clicked. - */ - public function getNavigateUrl() - { - return $this->getViewState('NavigateUrl',''); - } - - /** - * Sets the URL to link to when the THyperLink component is clicked. - * @param string the URL - */ - public function setNavigateUrl($value) - { - $this->setViewState('NavigateUrl',$value,''); - } - - /** - * Returns the URL to link to when the THyperLink component is clicked. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string the text caption - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the URL to link to when the THyperLink component is clicked. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string the text caption to be set - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * @return string the target window or frame to display the Web page content linked to when the THyperLink component is clicked. - */ - public function getTarget() - { - return $this->getViewState('Target',''); - } - - /** - * Sets the target window or frame to display the Web page content linked to when the THyperLink component is clicked. - * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. - */ - public function setTarget($value) - { - $this->setViewState('Target',$value,''); - } -} - + + * @link http://www.xisc.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * THyperLink class + * + * THyperLink displays a hyperlink on a page. The hyperlink URL is specified + * via the {@link setNavigateUrl NavigateUrl} property, and link text is via + * the {@link setText Text} property. It is also possible to display an image + * by setting the {@link setImageUrl ImageUrl} property. In this case, + * {@link getText Text} is displayed as the alternate text of the image. + * The link target is specified via the {@link setTarget Target} property. + * If both {@link getImageUrl ImageUrl} and {@link getText Text} are empty, + * the content enclosed within the control tag will be rendered. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THyperLink extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name of the hyperlink + */ + protected function getTagName() + { + return 'a'; + } + + /** + * Adds attributes related to a hyperlink element to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $isEnabled=$this->getEnabled(true); + if($this->getEnabled() && !$isEnabled) + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + if(($url=$this->getNavigateUrl())!=='' && $isEnabled) + $writer->addAttribute('href',$url); + if(($target=$this->getTarget())!=='') + $writer->addAttribute('target',$target); + } + + /** + * Renders the body content of the hyperlink. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if(($imageUrl=$this->getImageUrl())==='') + { + if(($text=$this->getText())!=='') + $writer->write(THttpUtility::htmlEncode($text)); + else if($this->getHasControls()) + parent::renderContents($writer); + else + $writer->write(THttpUtility::htmlEncode($this->getNavigateUrl())); + } + else + { + $this->createImage($imageUrl)->renderControl($writer); + } + } + + /** + * Gets the TImage for rendering the ImageUrl property. This is not for + * creating dynamic images. + * @param string image url. + * @return TImage image control for rendering. + */ + protected function createImage($imageUrl) + { + $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); + $image->setImageUrl($imageUrl); + if(($width=$this->getImageWidth())!=='') + $image->setWidth($width); + if(($height=$this->getImageHeight())!=='') + $image->setHeight($height); + if(($toolTip=$this->getToolTip())!=='') + $image->setToolTip($toolTip); + if(($text=$this->getText())!=='') + $image->setAlternateText($text); + $image->setBorderWidth('0'); + return $image; + } + + /** + * @return string the text caption of the THyperLink + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text caption of the THyperLink. + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return string height of the image in the THyperLink + */ + public function getImageHeight() + { + return $this->getViewState('ImageHeight',''); + } + + /** + * Sets the height of the image in the THyperLink + * @param string height of the image in the THyperLink + */ + public function setImageHeight($value) + { + $this->setViewSTate('ImageHeight',$value,''); + } + + /** + * @return string the location of the image file for the THyperLink + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * Sets the location of image file of the THyperLink. + * @param string the image file location + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * @return string width of the image in the THyperLink + */ + public function getImageWidth() + { + return $this->getViewState('ImageWidth',''); + } + + /** + * Sets the width of the image in the THyperLink + * @param string width of the image + */ + public function setImageWidth($value) + { + $this->setViewState('ImageWidth',$value,''); + } + + /** + * @return string the URL to link to when the THyperLink component is clicked. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * Sets the URL to link to when the THyperLink component is clicked. + * @param string the URL + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',$value,''); + } + + /** + * Returns the URL to link to when the THyperLink component is clicked. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text caption + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the URL to link to when the THyperLink component is clicked. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text caption to be set + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the target window or frame to display the Web page content linked to when the THyperLink component is clicked. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * Sets the target window or frame to display the Web page content linked to when the THyperLink component is clicked. + * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. + */ + public function setTarget($value) + { + $this->setViewState('Target',$value,''); + } +} + diff --git a/framework/Web/UI/WebControls/THyperLinkColumn.php b/framework/Web/UI/WebControls/THyperLinkColumn.php index a73880f1..faa4ce42 100644 --- a/framework/Web/UI/WebControls/THyperLinkColumn.php +++ b/framework/Web/UI/WebControls/THyperLinkColumn.php @@ -1,273 +1,273 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TDataGridColumn class file - */ -Prado::using('System.Web.UI.WebControls.TDataGridColumn'); -/** - * THyperLink class file - */ -Prado::using('System.Web.UI.WebControls.THyperLink'); - -/** - * THyperLinkColumn class - * - * THyperLinkColumn contains a hyperlink for each item in the column. - * You can set the text and the url of the hyperlink by {@link setText Text} - * and {@link setNavigateUrl NavigateUrl} properties, respectively. - * You can also bind the text and url to specific data field in datasource - * by setting {@link setDataTextField DataTextField} and - * {@link setDataNavigateUrlField DataNavigateUrlField}. - * Both can be formatted before rendering according to the - * {@link setDataTextFormatString DataTextFormatString} and - * and {@link setDataNavigateUrlFormatString DataNavigateUrlFormatString} - * properties, respectively. If both {@link setText Text} and {@link setDataTextField DataTextField} - * are present, the latter takes precedence. - * The same rule applies to {@link setNavigateUrl NavigateUrl} and - * {@link setDataNavigateUrlField DataNavigateUrlField} properties. - * - * The hyperlinks in the column can be accessed by one of the following two methods: - * - * $datagridItem->HyperLinkColumnID->HyperLink - * $datagridItem->HyperLinkColumnID->Controls[0] - * - * The second method is possible because the hyperlink control created within the - * datagrid cell is the first child. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class THyperLinkColumn extends TDataGridColumn -{ - /** - * @return string the text caption of the hyperlink - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * Sets the text caption of the hyperlink. - * @param string the text caption to be set - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * @return string the field name from the data source to bind to the hyperlink caption - */ - public function getDataTextField() - { - return $this->getViewState('DataTextField',''); - } - - /** - * @param string the field name from the data source to bind to the hyperlink caption - */ - public function setDataTextField($value) - { - $this->setViewState('DataTextField',$value,''); - } - - /** - * @return string the formatting string used to control how the hyperlink caption will be displayed. - */ - public function getDataTextFormatString() - { - return $this->getViewState('DataTextFormatString',''); - } - - /** - * @param string the formatting string used to control how the hyperlink caption will be displayed. - */ - public function setDataTextFormatString($value) - { - $this->setViewState('DataTextFormatString',$value,''); - } - - /** - * @return string height of the image in the THyperLink - */ - public function getImageHeight() - { - return $this->getViewState('ImageHeight',''); - } - - /** - * @param string height of the image in the THyperLink - */ - public function setImageHeight($value) - { - $this->setViewState('ImageHeight',$value,''); - } - - /** - * @return string url of the image in the THyperLink - */ - public function getImageUrl() - { - return $this->getViewState('ImageUrl',''); - } - - /** - * @param string url of the image in the THyperLink - */ - public function setImageUrl($value) - { - $this->setViewState('ImageUrl',$value,''); - } - - /** - * @return string width of the image in the THyperLink - */ - public function getImageWidth() - { - return $this->getViewState('ImageWidth',''); - } - - /** - * @param string width of the image in the THyperLink - */ - public function setImageWidth($value) - { - $this->setViewState('ImageWidth',$value,''); - } - - /** - * @return string the URL to link to when the hyperlink is clicked. - */ - public function getNavigateUrl() - { - return $this->getViewState('NavigateUrl',''); - } - - /** - * Sets the URL to link to when the hyperlink is clicked. - * @param string the URL - */ - public function setNavigateUrl($value) - { - $this->setViewState('NavigateUrl',$value,''); - } - - /** - * @return string the field name from the data source to bind to the navigate url of hyperlink - */ - public function getDataNavigateUrlField() - { - return $this->getViewState('DataNavigateUrlField',''); - } - - /** - * @param string the field name from the data source to bind to the navigate url of hyperlink - */ - public function setDataNavigateUrlField($value) - { - $this->setViewState('DataNavigateUrlField',$value,''); - } - - /** - * @return string the formatting string used to control how the navigate url of hyperlink will be displayed. - */ - public function getDataNavigateUrlFormatString() - { - return $this->getViewState('DataNavigateUrlFormatString',''); - } - - /** - * @param string the formatting string used to control how the navigate url of hyperlink will be displayed. - */ - public function setDataNavigateUrlFormatString($value) - { - $this->setViewState('DataNavigateUrlFormatString',$value,''); - } - - /** - * @return string the target window or frame to display the Web page content linked to when the hyperlink is clicked. - */ - public function getTarget() - { - return $this->getViewState('Target',''); - } - - /** - * Sets the target window or frame to display the Web page content linked to when the hyperlink is clicked. - * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. - */ - public function setTarget($value) - { - $this->setViewState('Target',$value,''); - } - - /** - * Initializes the specified cell to its initial values. - * This method overrides the parent implementation. - * It creates a hyperlink within the cell. - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) - { - $link=new THyperLink; - if(($url = $this->getImageUrl())!=='') - { - $link->setImageUrl($url); - if(($width=$this->getImageWidth())!=='') - $link->setImageWidth($width); - if(($height=$this->getImageHeight())!=='') - $link->setImageHeight($height); - } - $link->setText($this->getText()); - $link->setNavigateUrl($this->getNavigateUrl()); - $link->setTarget($this->getTarget()); - if($this->getDataTextField()!=='' || $this->getDataNavigateUrlField()!=='') - $link->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - $cell->getControls()->add($link); - $cell->registerObject('HyperLink',$link); - } - else - parent::initializeCell($cell,$columnIndex,$itemType); - } - - /** - * Databinds a cell in the column. - * This method is invoked when datagrid performs databinding. - * It populates the content of the cell with the relevant data from data source. - */ - public function dataBindColumn($sender,$param) - { - $item=$sender->getNamingContainer(); - $data=$item->getData(); - if(($field=$this->getDataTextField())!=='') - { - $value=$this->getDataFieldValue($data,$field); - $text=$this->formatDataValue($this->getDataTextFormatString(),$value); - $sender->setText($text); - } - if(($field=$this->getDataNavigateUrlField())!=='') - { - $value=$this->getDataFieldValue($data,$field); - $url=$this->formatDataValue($this->getDataNavigateUrlFormatString(),$value); - $sender->setNavigateUrl($url); - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); +/** + * THyperLink class file + */ +Prado::using('System.Web.UI.WebControls.THyperLink'); + +/** + * THyperLinkColumn class + * + * THyperLinkColumn contains a hyperlink for each item in the column. + * You can set the text and the url of the hyperlink by {@link setText Text} + * and {@link setNavigateUrl NavigateUrl} properties, respectively. + * You can also bind the text and url to specific data field in datasource + * by setting {@link setDataTextField DataTextField} and + * {@link setDataNavigateUrlField DataNavigateUrlField}. + * Both can be formatted before rendering according to the + * {@link setDataTextFormatString DataTextFormatString} and + * and {@link setDataNavigateUrlFormatString DataNavigateUrlFormatString} + * properties, respectively. If both {@link setText Text} and {@link setDataTextField DataTextField} + * are present, the latter takes precedence. + * The same rule applies to {@link setNavigateUrl NavigateUrl} and + * {@link setDataNavigateUrlField DataNavigateUrlField} properties. + * + * The hyperlinks in the column can be accessed by one of the following two methods: + * + * $datagridItem->HyperLinkColumnID->HyperLink + * $datagridItem->HyperLinkColumnID->Controls[0] + * + * The second method is possible because the hyperlink control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THyperLinkColumn extends TDataGridColumn +{ + /** + * @return string the text caption of the hyperlink + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text caption of the hyperlink. + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return string the field name from the data source to bind to the hyperlink caption + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * @param string the field name from the data source to bind to the hyperlink caption + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the hyperlink caption will be displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the hyperlink caption will be displayed. + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string height of the image in the THyperLink + */ + public function getImageHeight() + { + return $this->getViewState('ImageHeight',''); + } + + /** + * @param string height of the image in the THyperLink + */ + public function setImageHeight($value) + { + $this->setViewState('ImageHeight',$value,''); + } + + /** + * @return string url of the image in the THyperLink + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * @param string url of the image in the THyperLink + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * @return string width of the image in the THyperLink + */ + public function getImageWidth() + { + return $this->getViewState('ImageWidth',''); + } + + /** + * @param string width of the image in the THyperLink + */ + public function setImageWidth($value) + { + $this->setViewState('ImageWidth',$value,''); + } + + /** + * @return string the URL to link to when the hyperlink is clicked. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * Sets the URL to link to when the hyperlink is clicked. + * @param string the URL + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',$value,''); + } + + /** + * @return string the field name from the data source to bind to the navigate url of hyperlink + */ + public function getDataNavigateUrlField() + { + return $this->getViewState('DataNavigateUrlField',''); + } + + /** + * @param string the field name from the data source to bind to the navigate url of hyperlink + */ + public function setDataNavigateUrlField($value) + { + $this->setViewState('DataNavigateUrlField',$value,''); + } + + /** + * @return string the formatting string used to control how the navigate url of hyperlink will be displayed. + */ + public function getDataNavigateUrlFormatString() + { + return $this->getViewState('DataNavigateUrlFormatString',''); + } + + /** + * @param string the formatting string used to control how the navigate url of hyperlink will be displayed. + */ + public function setDataNavigateUrlFormatString($value) + { + $this->setViewState('DataNavigateUrlFormatString',$value,''); + } + + /** + * @return string the target window or frame to display the Web page content linked to when the hyperlink is clicked. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * Sets the target window or frame to display the Web page content linked to when the hyperlink is clicked. + * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. + */ + public function setTarget($value) + { + $this->setViewState('Target',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates a hyperlink within the cell. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) + { + $link=new THyperLink; + if(($url = $this->getImageUrl())!=='') + { + $link->setImageUrl($url); + if(($width=$this->getImageWidth())!=='') + $link->setImageWidth($width); + if(($height=$this->getImageHeight())!=='') + $link->setImageHeight($height); + } + $link->setText($this->getText()); + $link->setNavigateUrl($this->getNavigateUrl()); + $link->setTarget($this->getTarget()); + if($this->getDataTextField()!=='' || $this->getDataNavigateUrlField()!=='') + $link->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + $cell->getControls()->add($link); + $cell->registerObject('HyperLink',$link); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + if(($field=$this->getDataTextField())!=='') + { + $value=$this->getDataFieldValue($data,$field); + $text=$this->formatDataValue($this->getDataTextFormatString(),$value); + $sender->setText($text); + } + if(($field=$this->getDataNavigateUrlField())!=='') + { + $value=$this->getDataFieldValue($data,$field); + $url=$this->formatDataValue($this->getDataNavigateUrlFormatString(),$value); + $sender->setNavigateUrl($url); + } + } +} + diff --git a/framework/Web/UI/WebControls/TImage.php b/framework/Web/UI/WebControls/TImage.php index 37f9a050..c8dde281 100644 --- a/framework/Web/UI/WebControls/TImage.php +++ b/framework/Web/UI/WebControls/TImage.php @@ -1,157 +1,157 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TImage class - * - * TImage displays an image on a page. The image is specified via the - * {@link setImageUrl ImageUrl} property which takes a relative or absolute - * URL to the image file. The alignment of the image displayed is set by - * the {@link setImageAlign ImageAlign} property. To set alternative texts - * or long description of the image, use {@link setAlternateText AlternateText} - * or {@link setDescriptionUrl DescriptionUrl} property, respectively. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TImage extends TWebControl implements IDataRenderer -{ - /** - * @return string tag name of image control - */ - protected function getTagName() - { - return 'img'; - } - - /** - * Adds attributes related to an HTML image element to renderer. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $writer->addAttribute('src',$this->getImageUrl()); - $writer->addAttribute('alt',$this->getAlternateText()); - if(($desc=$this->getDescriptionUrl())!=='') - $writer->addAttribute('longdesc',$desc); - if(($align=$this->getImageAlign())!=='') - $writer->addAttribute('align',$align); - parent::addAttributesToRender($writer); - } - - /** - * Renders the body content of the image. - * Nothing to be rendered within image tags. - * @param THtmlWriter the writer for rendering - */ - public function renderContents($writer) - { - } - - /** - * @return string the alternative text displayed in the TImage component when the image is unavailable. - */ - public function getAlternateText() - { - return $this->getViewState('AlternateText',''); - } - - /** - * Sets the alternative text to be displayed in the TImage when the image is unavailable. - * @param string the alternative text - */ - public function setAlternateText($value) - { - $this->setViewState('AlternateText',$value,''); - } - - /** - * @return string the alignment of the image with respective to other elements on the page, defaults to empty. - */ - public function getImageAlign() - { - return $this->getViewState('ImageAlign',''); - } - - /** - * Sets the alignment of the image with respective to other elements on the page. - * Possible values include: absbottom, absmiddle, baseline, bottom, left, - * middle, right, texttop, and top. If an empty string is passed in, - * imagealign attribute will not be rendered. - * @param string the alignment of the image - */ - public function setImageAlign($value) - { - $this->setViewState('ImageAlign',$value,''); - } - - /** - * @return string the URL of the image file - */ - public function getImageUrl() - { - return $this->getViewState('ImageUrl',''); - } - - /** - * @param string the URL of the image file - */ - public function setImageUrl($value) - { - $this->setViewState('ImageUrl',$value,''); - } - - /** - * Returns the URL of the image file. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getImageUrl()}. - * @return string the URL of the image file. - * @see getImageUrl - * @since 3.1.0 - */ - public function getData() - { - return $this->getImageUrl(); - } - - /** - * Sets the URL of the image. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setImageUrl()}. - * @param string the URL of the image file. - * @see setImageUrl - * @since 3.1.0 - */ - public function setData($value) - { - $this->setImageUrl($value); - } - - /** - * @return string the URL to long description - */ - public function getDescriptionUrl() - { - return $this->getViewState('DescriptionUrl',''); - } - - /** - * @param string the URL to the long description of the image. - */ - public function setDescriptionUrl($value) - { - $this->setViewState('DescriptionUrl',$value,''); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TImage class + * + * TImage displays an image on a page. The image is specified via the + * {@link setImageUrl ImageUrl} property which takes a relative or absolute + * URL to the image file. The alignment of the image displayed is set by + * the {@link setImageAlign ImageAlign} property. To set alternative texts + * or long description of the image, use {@link setAlternateText AlternateText} + * or {@link setDescriptionUrl DescriptionUrl} property, respectively. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImage extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name of image control + */ + protected function getTagName() + { + return 'img'; + } + + /** + * Adds attributes related to an HTML image element to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('src',$this->getImageUrl()); + $writer->addAttribute('alt',$this->getAlternateText()); + if(($desc=$this->getDescriptionUrl())!=='') + $writer->addAttribute('longdesc',$desc); + if(($align=$this->getImageAlign())!=='') + $writer->addAttribute('align',$align); + parent::addAttributesToRender($writer); + } + + /** + * Renders the body content of the image. + * Nothing to be rendered within image tags. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + } + + /** + * @return string the alternative text displayed in the TImage component when the image is unavailable. + */ + public function getAlternateText() + { + return $this->getViewState('AlternateText',''); + } + + /** + * Sets the alternative text to be displayed in the TImage when the image is unavailable. + * @param string the alternative text + */ + public function setAlternateText($value) + { + $this->setViewState('AlternateText',$value,''); + } + + /** + * @return string the alignment of the image with respective to other elements on the page, defaults to empty. + */ + public function getImageAlign() + { + return $this->getViewState('ImageAlign',''); + } + + /** + * Sets the alignment of the image with respective to other elements on the page. + * Possible values include: absbottom, absmiddle, baseline, bottom, left, + * middle, right, texttop, and top. If an empty string is passed in, + * imagealign attribute will not be rendered. + * @param string the alignment of the image + */ + public function setImageAlign($value) + { + $this->setViewState('ImageAlign',$value,''); + } + + /** + * @return string the URL of the image file + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * @param string the URL of the image file + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * Returns the URL of the image file. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getImageUrl()}. + * @return string the URL of the image file. + * @see getImageUrl + * @since 3.1.0 + */ + public function getData() + { + return $this->getImageUrl(); + } + + /** + * Sets the URL of the image. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setImageUrl()}. + * @param string the URL of the image file. + * @see setImageUrl + * @since 3.1.0 + */ + public function setData($value) + { + $this->setImageUrl($value); + } + + /** + * @return string the URL to long description + */ + public function getDescriptionUrl() + { + return $this->getViewState('DescriptionUrl',''); + } + + /** + * @param string the URL to the long description of the image. + */ + public function setDescriptionUrl($value) + { + $this->setViewState('DescriptionUrl',$value,''); + } +} + diff --git a/framework/Web/UI/WebControls/TImageButton.php b/framework/Web/UI/WebControls/TImageButton.php index 350c84d4..a92a9df8 100644 --- a/framework/Web/UI/WebControls/TImageButton.php +++ b/framework/Web/UI/WebControls/TImageButton.php @@ -1,442 +1,442 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TImage class file - */ -Prado::using('System.Web.UI.WebControls.TImage'); - -/** - * TImageButton class - * - * TImageButton creates an image button on the page. It is used to submit data to a page. - * You can create either a submit button or a command button. - * - * A command button has a command name (specified by - * the {@link setCommandName CommandName} property) and and a command parameter - * (specified by {@link setCommandParameter CommandParameter} property) - * associated with the button. This allows you to create multiple TLinkButton - * components on a Web page and programmatically determine which one is clicked - * with what parameter. You can provide an event handler for - * {@link onCommand OnCommand} event to programmatically control the actions performed - * when the command button is clicked. In the event handler, you can determine - * the {@link setCommandName CommandName} property value and - * the {@link setCommandParameter CommandParameter} property value - * through the {@link TCommandParameter::getName Name} and - * {@link TCommandParameter::getParameter Parameter} properties of the event - * parameter which is of type {@link TCommandEventParameter}. - * - * A submit button does not have a command name associated with the button - * and clicking on it simply posts the Web page back to the server. - * By default, a TImageButton control is a submit button. - * You can provide an event handler for the {@link onClick OnClick} event - * to programmatically control the actions performed when the submit button is clicked. - * The coordinates of the clicking point can be obtained from the {@link onClick OnClick} - * event parameter, which is of type {@link TImageClickEventParameter}. - * - * Clicking on button can trigger form validation, if - * {@link setCausesValidation CausesValidation} is true. - * And the validation may be restricted within a certain group of validator - * controls by setting {@link setValidationGroup ValidationGroup} property. - * If validation is successful, the data will be post back to the same page. - * - * TImageButton displays the {@link setText Text} property as the hint text to the displayed image. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TImageButton extends TImage implements IPostBackDataHandler, IPostBackEventHandler, IButtonControl -{ - /** - * @var integer x coordinate that the image is being clicked at - */ - private $_x=0; - /** - * @var integer y coordinate that the image is being clicked at - */ - private $_y=0; - private $_dataChanged=false; - - /** - * @return string tag name of the button - */ - protected function getTagName() - { - return 'input'; - } - - /** - * @return boolean whether to render javascript. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether to render javascript. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Adds attribute name-value pairs to renderer. - * This overrides the parent implementation with additional button specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $page=$this->getPage(); - $page->ensureRenderInForm($this); - $writer->addAttribute('type','image'); - if(($uniqueID=$this->getUniqueID())!=='') - $writer->addAttribute('name',$uniqueID); - if($this->getEnabled(true)) - { - if($this->getEnableClientScript() && $this->needPostBackScript()) - $this->renderClientControlScript($writer); - } - else if($this->getEnabled()) // in this case, parent will not render 'disabled' - $writer->addAttribute('disabled','disabled'); - parent::addAttributesToRender($writer); - } - - /** - * Renders the client-script code. - */ - protected function renderClientControlScript($writer) - { - $writer->addAttribute('id',$this->getClientID()); - $cs = $this->getPage()->getClientScript(); - $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); - } - - /** - * 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.TImageButton'; - } - - /** - * @return boolean whether to perform validation if the button is clicked - */ - protected function canCauseValidation() - { - if($this->getCausesValidation()) - { - $group=$this->getValidationGroup(); - return $this->getPage()->getValidators($group)->getCount()>0; - } - else - return false; - } - - /** - * @param boolean set by a panel to register this button as the default button for the panel. - */ - public function setIsDefaultButton($value) - { - $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean true if this button is registered as a default button for a panel. - */ - public function getIsDefaultButton() - { - return $this->getViewState('IsDefaultButton', false); - } - - /** - * @return boolean whether the button needs javascript to do postback - */ - protected function needPostBackScript() - { - return $this->canCauseValidation() || $this->getIsDefaultButton(); - } - - /** - * Returns postback specifications for the button. - * This method is used by framework and control developers. - * @return array parameters about how the button defines its postback behavior. - */ - protected function getPostBackOptions() - { - $options['ID'] = $this->getClientID(); - $options['CausesValidation'] = $this->getCausesValidation(); - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['EventTarget'] = $this->getUniqueID(); - - return $options; - } - - /** - * This method checks if the TImageButton is clicked and loads the coordinates of the clicking position. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - $uid=$this->getUniqueID(); - if(isset($values["{$uid}_x"]) && isset($values["{$uid}_y"])) - { - $this->_x=intval($values["{$uid}_x"]); - $this->_y=intval($values["{$uid}_y"]); - if($this->getPage()->getPostBackEventTarget()===null) - $this->getPage()->setPostBackEventTarget($this); - $this->_dataChanged=true; - } - return false; - } - - /** - * A dummy implementation for the IPostBackDataHandler interface. - */ - public function raisePostDataChangedEvent() - { - // no post data to handle - } - - /** - * This method is invoked when the button is clicked. - * The method raises 'OnClick' 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 TImageClickEventParameter event parameter to be passed to the event handlers - */ - public function onClick($param) - { - $this->raiseEvent('OnClick',$this,$param); - } - - /** - * This method is invoked when the button is clicked. - * The method raises 'OnCommand' event to fire up the event handlers. - * If you override this method, be sure to call the parent implementation - * so that the event handlers can be invoked. - * @param TCommandEventParameter event parameter to be passed to the event handlers - */ - public function onCommand($param) - { - $this->raiseEvent('OnCommand',$this,$param); - $this->raiseBubbleEvent($this,$param); - } - - /** - * Raises the postback event. - * This method is required by {@link IPostBackEventHandler} interface. - * If {@link getCausesValidation CausesValidation} is true, it will - * invoke the page's {@link TPage::validate validate} method first. - * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. - * This method is mainly used by framework and control developers. - * @param TEventParameter the event parameter - */ - public function raisePostBackEvent($param) - { - if($this->getCausesValidation()) - $this->getPage()->validate($this->getValidationGroup()); - $this->onClick(new TImageClickEventParameter($this->_x,$this->_y)); - $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * @return boolean whether postback event trigger by this button will cause input validation, default is true - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * @param boolean whether postback event trigger by this button will cause input validation - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the command name associated with the {@link onCommand OnCommand} event. - */ - public function getCommandName() - { - return $this->getViewState('CommandName',''); - } - - /** - * @param string the command name associated with the {@link onCommand OnCommand} event. - */ - public function setCommandName($value) - { - $this->setViewState('CommandName',$value,''); - } - - /** - * @return string the parameter associated with the {@link onCommand OnCommand} event - */ - public function getCommandParameter() - { - return $this->getViewState('CommandParameter',''); - } - - /** - * @param string the parameter associated with the {@link onCommand OnCommand} event. - */ - public function setCommandParameter($value) - { - $this->setViewState('CommandParameter',$value,''); - } - - /** - * @return string the group of validators which the button causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the button causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * @return string caption of the button - */ - public function getText() - { - return $this->getAlternateText(); - } - - /** - * @param string caption of the button - */ - public function setText($value) - { - $this->setAlternateText($value); - } - - /** - * Registers the image button to receive postback data during postback. - * This is necessary because an image button, when postback, does not have - * direct mapping between post data and the image button name. - * This method overrides the parent implementation and is invoked before render. - * @param mixed event parameter - */ - public function onPreRender($param) - { - parent::onPreRender($param); - $this->getPage()->registerRequiresPostData($this); - } - - /** - * Renders the body content enclosed between the control tag. - * This overrides the parent implementation with nothing to be rendered. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderContents($writer) - { - } -} - -/** - * TImageClickEventParameter class - * - * TImageClickEventParameter encapsulates the parameter data for - * {@link TImageButton::onClick Click} event of {@link TImageButton} controls. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TImageClickEventParameter extends TEventParameter -{ - /** - * the X coordinate of the clicking point - * @var integer - */ - private $_x=0; - /** - * the Y coordinate of the clicking point - * @var integer - */ - private $_y=0; - - /** - * Constructor. - * @param integer X coordinate of the clicking point - * @param integer Y coordinate of the clicking point - */ - public function __construct($x,$y) - { - $this->_x=$x; - $this->_y=$y; - } - - /** - * @return integer X coordinate of the clicking point, defaults to 0 - */ - public function getX() - { - return $this->_x; - } - - /** - * @param integer X coordinate of the clicking point - */ - public function setX($value) - { - $this->_x=TPropertyValue::ensureInteger($value); - } - - /** - * @return integer Y coordinate of the clicking point, defaults to 0 - */ - public function getY() - { - return $this->_y; - } - - /** - * @param integer Y coordinate of the clicking point - */ - public function setY($value) - { - $this->_y=TPropertyValue::ensureInteger($value); - } -} - -?> + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TImage class file + */ +Prado::using('System.Web.UI.WebControls.TImage'); + +/** + * TImageButton class + * + * TImageButton creates an image button on the page. It is used to submit data to a page. + * You can create either a submit button or a command button. + * + * A command button has a command name (specified by + * the {@link setCommandName CommandName} property) and and a command parameter + * (specified by {@link setCommandParameter CommandParameter} property) + * associated with the button. This allows you to create multiple TLinkButton + * components on a Web page and programmatically determine which one is clicked + * with what parameter. You can provide an event handler for + * {@link onCommand OnCommand} event to programmatically control the actions performed + * when the command button is clicked. In the event handler, you can determine + * the {@link setCommandName CommandName} property value and + * the {@link setCommandParameter CommandParameter} property value + * through the {@link TCommandParameter::getName Name} and + * {@link TCommandParameter::getParameter Parameter} properties of the event + * parameter which is of type {@link TCommandEventParameter}. + * + * A submit button does not have a command name associated with the button + * and clicking on it simply posts the Web page back to the server. + * By default, a TImageButton control is a submit button. + * You can provide an event handler for the {@link onClick OnClick} event + * to programmatically control the actions performed when the submit button is clicked. + * The coordinates of the clicking point can be obtained from the {@link onClick OnClick} + * event parameter, which is of type {@link TImageClickEventParameter}. + * + * Clicking on button can trigger form validation, if + * {@link setCausesValidation CausesValidation} is true. + * And the validation may be restricted within a certain group of validator + * controls by setting {@link setValidationGroup ValidationGroup} property. + * If validation is successful, the data will be post back to the same page. + * + * TImageButton displays the {@link setText Text} property as the hint text to the displayed image. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageButton extends TImage implements IPostBackDataHandler, IPostBackEventHandler, IButtonControl +{ + /** + * @var integer x coordinate that the image is being clicked at + */ + private $_x=0; + /** + * @var integer y coordinate that the image is being clicked at + */ + private $_y=0; + private $_dataChanged=false; + + /** + * @return string tag name of the button + */ + protected function getTagName() + { + return 'input'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional button specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + $writer->addAttribute('type','image'); + if(($uniqueID=$this->getUniqueID())!=='') + $writer->addAttribute('name',$uniqueID); + if($this->getEnabled(true)) + { + if($this->getEnableClientScript() && $this->needPostBackScript()) + $this->renderClientControlScript($writer); + } + else if($this->getEnabled()) // in this case, parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + } + + /** + * Renders the client-script code. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * 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.TImageButton'; + } + + /** + * @return boolean whether to perform validation if the button is clicked + */ + protected function canCauseValidation() + { + if($this->getCausesValidation()) + { + $group=$this->getValidationGroup(); + return $this->getPage()->getValidators($group)->getCount()>0; + } + else + return false; + } + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value) + { + $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton() + { + return $this->getViewState('IsDefaultButton', false); + } + + /** + * @return boolean whether the button needs javascript to do postback + */ + protected function needPostBackScript() + { + return $this->canCauseValidation() || $this->getIsDefaultButton(); + } + + /** + * Returns postback specifications for the button. + * This method is used by framework and control developers. + * @return array parameters about how the button defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['EventTarget'] = $this->getUniqueID(); + + return $options; + } + + /** + * This method checks if the TImageButton is clicked and loads the coordinates of the clicking position. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $uid=$this->getUniqueID(); + if(isset($values["{$uid}_x"]) && isset($values["{$uid}_y"])) + { + $this->_x=intval($values["{$uid}_x"]); + $this->_y=intval($values["{$uid}_y"]); + if($this->getPage()->getPostBackEventTarget()===null) + $this->getPage()->setPostBackEventTarget($this); + $this->_dataChanged=true; + } + return false; + } + + /** + * A dummy implementation for the IPostBackDataHandler interface. + */ + public function raisePostDataChangedEvent() + { + // no post data to handle + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnClick' 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 TImageClickEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnCommand' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param) + { + $this->raiseEvent('OnCommand',$this,$param); + $this->raiseBubbleEvent($this,$param); + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * If {@link getCausesValidation CausesValidation} is true, it will + * invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + if($this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onClick(new TImageClickEventParameter($this->_x,$this->_y)); + $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @return boolean whether postback event trigger by this button will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this button will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName() + { + return $this->getViewState('CommandName',''); + } + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value) + { + $this->setViewState('CommandName',$value,''); + } + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter() + { + return $this->getViewState('CommandParameter',''); + } + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value) + { + $this->setViewState('CommandParameter',$value,''); + } + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string caption of the button + */ + public function getText() + { + return $this->getAlternateText(); + } + + /** + * @param string caption of the button + */ + public function setText($value) + { + $this->setAlternateText($value); + } + + /** + * Registers the image button to receive postback data during postback. + * This is necessary because an image button, when postback, does not have + * direct mapping between post data and the image button name. + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getPage()->registerRequiresPostData($this); + } + + /** + * Renders the body content enclosed between the control tag. + * This overrides the parent implementation with nothing to be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + } +} + +/** + * TImageClickEventParameter class + * + * TImageClickEventParameter encapsulates the parameter data for + * {@link TImageButton::onClick Click} event of {@link TImageButton} controls. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageClickEventParameter extends TEventParameter +{ + /** + * the X coordinate of the clicking point + * @var integer + */ + private $_x=0; + /** + * the Y coordinate of the clicking point + * @var integer + */ + private $_y=0; + + /** + * Constructor. + * @param integer X coordinate of the clicking point + * @param integer Y coordinate of the clicking point + */ + public function __construct($x,$y) + { + $this->_x=$x; + $this->_y=$y; + } + + /** + * @return integer X coordinate of the clicking point, defaults to 0 + */ + public function getX() + { + return $this->_x; + } + + /** + * @param integer X coordinate of the clicking point + */ + public function setX($value) + { + $this->_x=TPropertyValue::ensureInteger($value); + } + + /** + * @return integer Y coordinate of the clicking point, defaults to 0 + */ + public function getY() + { + return $this->_y; + } + + /** + * @param integer Y coordinate of the clicking point + */ + public function setY($value) + { + $this->_y=TPropertyValue::ensureInteger($value); + } +} + +?> diff --git a/framework/Web/UI/WebControls/TImageMap.php b/framework/Web/UI/WebControls/TImageMap.php index 5907d03d..718a3414 100644 --- a/framework/Web/UI/WebControls/TImageMap.php +++ b/framework/Web/UI/WebControls/TImageMap.php @@ -1,837 +1,837 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TImage class file - */ -Prado::using('System.Web.UI.WebControls.TImage'); - -/** - * TImageMap class - * - * TImageMap represents an image on a page. Hotspot regions can be defined - * within the image. Depending on the {@link setHotSpotMode HotSpotMode}, - * clicking on the hotspots may trigger a postback or navigate to a specified - * URL. The hotspots defined may be accessed via {@link getHotSpots HotSpots}. - * Each hotspot is described as a {@link THotSpot}, which can be a circle, - * rectangle, polygon, etc. To add hotspot in a template, use the following, - * - * - * - * - * - * - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TImageMap extends TImage implements IPostBackEventHandler -{ - const MAP_NAME_PREFIX='ImageMap'; - - /** - * Processes an object that is created during parsing template. - * This method adds {@link THotSpot} objects into the hotspot collection - * of the imagemap. - * @param string|TComponent text string or component parsed and instantiated in template - */ - public function addParsedObject($object) - { - if($object instanceof THotSpot) - $this->getHotSpots()->add($object); - } - - /** - * Adds attribute name-value pairs to renderer. - * This overrides the parent implementation with additional imagemap specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - if($this->getHotSpots()->getCount()>0) - { - $writer->addAttribute('usemap','#'.self::MAP_NAME_PREFIX.$this->getClientID()); - $writer->addAttribute('id',$this->getUniqueID()); - } - if($this->getEnabled() && !$this->getEnabled(true)) - $writer->addAttribute('disabled','disabled'); - } - - /** - * Renders this imagemap. - * @param THtmlWriter - */ - public function render($writer) - { - parent::render($writer); - - $hotspots=$this->getHotSpots(); - - if($hotspots->getCount()>0) - { - $clientID=$this->getClientID(); - $cs=$this->getPage()->getClientScript(); - $writer->writeLine(); - $writer->addAttribute('name',self::MAP_NAME_PREFIX.$clientID); - $writer->renderBeginTag('map'); - $writer->writeLine(); - if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) - $mode=THotSpotMode::Navigate; - $target=$this->getTarget(); - $i=0; - $options['EventTarget'] = $this->getUniqueID(); - $options['StopEvent'] = true; - $cs=$this->getPage()->getClientScript(); - foreach($hotspots as $hotspot) - { - if($hotspot->getHotSpotMode()===THotSpotMode::NotSet) - $hotspot->setHotSpotMode($mode); - if($target!=='' && $hotspot->getTarget()==='') - $hotspot->setTarget($target); - if($hotspot->getHotSpotMode()===THotSpotMode::PostBack) - { - $id=$clientID.'_'.$i; - $writer->addAttribute('id',$id); - $writer->addAttribute('href','#'.$id); //create unique no-op url references - $options['ID']=$id; - $options['EventParameter']="$i"; - $options['CausesValidation']=$hotspot->getCausesValidation(); - $options['ValidationGroup']=$hotspot->getValidationGroup(); - $cs->registerPostBackControl($this->getClientClassName(),$options); - } - $hotspot->render($writer); - $writer->writeLine(); - $i++; - } - $writer->renderEndTag(); - } - } - - /** - * 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.TImageMap'; - } - - /** - * Raises the postback event. - * This method is required by {@link IPostBackEventHandler} interface. - * This method is mainly used by framework and control developers. - * @param TEventParameter the event parameter - */ - public function raisePostBackEvent($param) - { - $postBackValue=null; - if($param!=='') - { - $index=TPropertyValue::ensureInteger($param); - $hotspots=$this->getHotSpots(); - if($index>=0 && $index<$hotspots->getCount()) - { - $hotspot=$hotspots->itemAt($index); - if(($mode=$hotspot->getHotSpotMode())===THotSpotMode::NotSet) - $mode=$this->getHotSpotMode(); - if($mode===THotSpotMode::PostBack) - { - $postBackValue=$hotspot->getPostBackValue(); - if($hotspot->getCausesValidation()) - $this->getPage()->validate($hotspot->getValidationGroup()); - } - } - } - if($postBackValue!==null) - $this->onClick(new TImageMapEventParameter($postBackValue)); - } - - /** - * @return THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. Defaults to THotSpotMode::NotSet. - */ - public function getHotSpotMode() - { - return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); - } - - /** - * Sets the behavior of hotspot regions in this imagemap when they are clicked. - * If an individual hotspot has a mode other than 'NotSet', the mode set in this - * imagemap will be ignored. By default, 'NotSet' is equivalent to 'Navigate'. - * @param THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. - */ - public function setHotSpotMode($value) - { - $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); - } - - /** - * @return THotSpotCollection collection of hotspots defined in this imagemap. - */ - public function getHotSpots() - { - if(($hotspots=$this->getViewState('HotSpots',null))===null) - { - $hotspots=new THotSpotCollection; - $this->setViewState('HotSpots',$hotspots); - } - return $hotspots; - } - - /** - * @return string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. Defaults to ''. - */ - public function getTarget() - { - return $this->getViewState('Target',''); - } - - /** - * @param string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. - */ - public function setTarget($value) - { - $this->setViewState('Target',TPropertyValue::ensureString($value),''); - } - - /** - * Raises OnClick event. - * This method is invoked when a hotspot region is clicked within the imagemap. - * If you override this method, be sure to call the parent implementation - * so that the event handler can be invoked. - * @param TImageMapEventParameter event parameter to be passed to the event handlers - */ - public function onClick($param) - { - $this->raiseEvent('OnClick',$this,$param); - } -} - -/** - * TImageMapEventParameter class. - * - * TImageMapEventParameter represents a postback event parameter - * when a hotspot is clicked and posts back in a {@link TImageMap}. - * To retrieve the post back value associated with the hotspot being clicked, - * access {@link getPostBackValue PostBackValue}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TImageMapEventParameter extends TEventParameter -{ - private $_postBackValue; - - /** - * Constructor. - * @param string post back value associated with the hotspot clicked - */ - public function __construct($postBackValue) - { - $this->_postBackValue=$postBackValue; - } - - /** - * @return string post back value associated with the hotspot clicked - */ - public function getPostBackValue() - { - return $this->_postBackValue; - } -} - -/** - * THotSpotCollection class. - * - * THotSpotCollection represents a collection of hotspots in an imagemap. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class THotSpotCollection extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by inserting only {@link THotSpot}. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a THotSpot. - */ - public function insertAt($index,$item) - { - if($item instanceof THotSpot) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('hotspotcollection_hotspot_required'); - } -} - - -/** - * THotSpot class. - * - * THotSpot implements the basic functionality common to all hot spot shapes. - * Derived classes include {@link TCircleHotSpot}, {@link TPolygonHotSpot} - * and {@link TRectangleHotSpot}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class THotSpot extends TComponent -{ - private $_viewState=array(); - - /** - * Returns a viewstate value. - * - * This function is very useful in defining getter functions for component properties - * that must be kept in viewstate. - * @param string the name of the viewstate value to be returned - * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned - * @return mixed the viewstate value corresponding to $key - */ - protected function getViewState($key,$defaultValue=null) - { - return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; - } - - /** - * Sets a viewstate value. - * - * This function is very useful in defining setter functions for control properties - * that must be kept in viewstate. - * Make sure that the viewstate value must be serializable and unserializable. - * @param string the name of the viewstate value - * @param mixed the viewstate value to be set - * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. - */ - protected function setViewState($key,$value,$defaultValue=null) - { - if($value===$defaultValue) - unset($this->_viewState[$key]); - else - $this->_viewState[$key]=$value; - } - - /** - * @return string shape of the hotspot, can be 'circle', 'rect', 'poly', etc. - */ - abstract public function getShape(); - /** - * @return string coordinates defining the hotspot shape. - */ - abstract public function getCoordinates(); - - /** - * @return string the access key that allows you to quickly navigate to the HotSpot region. Defaults to ''. - */ - public function getAccessKey() - { - return $this->getViewState('AccessKey',''); - } - - /** - * @param string the access key that allows you to quickly navigate to the HotSpot region. - */ - public function setAccessKey($value) - { - $this->setViewState('AccessKey',TPropertyValue::ensureString($value),''); - } - - /** - * @return string the alternate text to display for a HotSpot object. Defaults to ''. - */ - public function getAlternateText() - { - return $this->getViewState('AlternateText',''); - } - - /** - * @param string the alternate text to display for a HotSpot object. - */ - public function setAlternateText($value) - { - $this->setViewState('AlternateText',TPropertyValue::ensureString($value),''); - } - - /** - * @return THotSpotMode the behavior of a HotSpot object when it is clicked. Defaults to THotSpotMode::NotSet. - */ - public function getHotSpotMode() - { - return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); - } - - /** - * @param THotSpotMode the behavior of a HotSpot object when it is clicked. - */ - public function setHotSpotMode($value) - { - $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); - } - - /** - * @return string the URL to navigate to when a HotSpot object is clicked. Defaults to ''. - */ - public function getNavigateUrl() - { - return $this->getViewState('NavigateUrl',''); - } - - /** - * @param string the URL to navigate to when a HotSpot object is clicked. - */ - public function setNavigateUrl($value) - { - $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),''); - } - - /** - * @return string a value that is post back when the HotSpot is clicked. Defaults to ''. - */ - public function getPostBackValue() - { - return $this->getViewState('PostBackValue',''); - } - - /** - * @param string a value that is post back when the HotSpot is clicked. - */ - public function setPostBackValue($value) - { - $this->setViewState('PostBackValue',TPropertyValue::ensureString($value),''); - } - - /** - * @return integer the tab index of the HotSpot region. Defaults to 0. - */ - public function getTabIndex() - { - return $this->getViewState('TabIndex',0); - } - - /** - * @param integer the tab index of the HotSpot region. - */ - public function setTabIndex($value) - { - $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return boolean whether postback event trigger by this hotspot will cause input validation, default is true - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * @param boolean whether postback event trigger by this hotspot will cause input validation - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the group of validators which the hotspot causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the hotspot causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * @return string the target window or frame to display the new page when the HotSpot region - * is clicked. Defaults to ''. - */ - public function getTarget() - { - return $this->getViewState('Target',''); - } - - /** - * @param string the target window or frame to display the new page when the HotSpot region - * is clicked. - */ - public function setTarget($value) - { - $this->setViewState('Target',TPropertyValue::ensureString($value),''); - } - - /** - * @return boolean whether the hotspot has custom attributes - */ - public function getHasAttributes() - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->getCount()>0; - else - return false; - } - - /** - * Returns the list of custom attributes. - * Custom attributes are name-value pairs that may be rendered - * as HTML tags' attributes. - * @return TAttributeCollection the list of custom attributes - */ - public function getAttributes() - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes; - else - { - $attributes=new TAttributeCollection; - $this->setViewState('Attributes',$attributes,null); - return $attributes; - } - } - - /** - * @return boolean whether the named attribute exists - */ - public function hasAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->contains($name); - else - return false; - } - - /** - * @return string attribute value, null if attribute does not exist - */ - public function getAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->itemAt($name); - else - return null; - } - - /** - * Sets a custom hotspot attribute. - * @param string attribute name - * @param string value of the attribute - */ - public function setAttribute($name,$value) - { - $this->getAttributes()->add($name,$value); - } - - /** - * Removes the named attribute. - * @param string the name of the attribute to be removed. - * @return string attribute value removed, null if attribute does not exist. - */ - public function removeAttribute($name) - { - if($attributes=$this->getViewState('Attributes',null)) - return $attributes->remove($name); - else - return null; - } - - /** - * Renders this hotspot. - * @param THtmlWriter - */ - public function render($writer) - { - $writer->addAttribute('shape',$this->getShape()); - $writer->addAttribute('coords',$this->getCoordinates()); - if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) - $mode=THotSpotMode::Navigate; - if($mode===THotSpotMode::Navigate) - { - $writer->addAttribute('href',$this->getNavigateUrl()); - if(($target=$this->getTarget())!=='') - $writer->addAttribute('target',$target); - } - else if($mode===THotSpotMode::Inactive) - $writer->addAttribute('nohref','true'); - $text=$this->getAlternateText(); - $writer->addAttribute('title',$text); - $writer->addAttribute('alt',$text); - if(($accessKey=$this->getAccessKey())!=='') - $writer->addAttribute('accesskey',$accessKey); - if(($tabIndex=$this->getTabIndex())!==0) - $writer->addAttribute('tabindex',"$tabIndex"); - if($this->getHasAttributes()) - { - foreach($this->getAttributes() as $name=>$value) - $writer->addAttribute($name,$value); - } - $writer->renderBeginTag('area'); - $writer->renderEndTag(); - } -} - -/** - * Class TCircleHotSpot. - * - * TCircleHotSpot defines a circular hot spot region in a {@link TImageMap} - * control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TCircleHotSpot extends THotSpot -{ - /** - * @return string shape of this hotspot. - */ - public function getShape() - { - return 'circle'; - } - - /** - * @return string coordinates defining this hotspot shape - */ - public function getCoordinates() - { - return $this->getX().','.$this->getY().','.$this->getRadius(); - } - - /** - * @return integer radius of the circular HotSpot region. Defaults to 0. - */ - public function getRadius() - { - return $this->getViewState('Radius',0); - } - - /** - * @param integer radius of the circular HotSpot region. - */ - public function setRadius($value) - { - $this->setViewState('Radius',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the X coordinate of the center of the circular HotSpot region. Defaults to 0. - */ - public function getX() - { - return $this->getViewState('X',0); - } - - /** - * @param integer the X coordinate of the center of the circular HotSpot region. - */ - public function setX($value) - { - $this->setViewState('X',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the Y coordinate of the center of the circular HotSpot region. Defaults to 0. - */ - public function getY() - { - return $this->getViewState('Y',0); - } - - /** - * @param integer the Y coordinate of the center of the circular HotSpot region. - */ - public function setY($value) - { - $this->setViewState('Y',TPropertyValue::ensureInteger($value),0); - } -} - -/** - * Class TRectangleHotSpot. - * - * TRectangleHotSpot defines a rectangle hot spot region in a {@link - * TImageMap} control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRectangleHotSpot extends THotSpot -{ - /** - * @return string shape of this hotspot. - */ - public function getShape() - { - return 'rect'; - } - - /** - * @return string coordinates defining this hotspot shape - */ - public function getCoordinates() - { - return $this->getLeft().','.$this->getTop().','.$this->getRight().','.$this->getBottom(); - } - - /** - * @return integer the Y coordinate of the bottom side of the rectangle HotSpot region. Defaults to 0. - */ - public function getBottom() - { - return $this->getViewState('Bottom',0); - } - - /** - * @param integer the Y coordinate of the bottom side of the rectangle HotSpot region. - */ - public function setBottom($value) - { - $this->setViewState('Bottom',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. - */ - public function getLeft() - { - return $this->getViewState('Left',0); - } - - /** - * @param integer the X coordinate of the right side of the rectangle HotSpot region. - */ - public function setLeft($value) - { - $this->setViewState('Left',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. - */ - public function getRight() - { - return $this->getViewState('Right',0); - } - - /** - * @param integer the X coordinate of the right side of the rectangle HotSpot region. - */ - public function setRight($value) - { - $this->setViewState('Right',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the Y coordinate of the top side of the rectangle HotSpot region. Defaults to 0. - */ - public function getTop() - { - return $this->getViewState('Top',0); - } - - /** - * @param integer the Y coordinate of the top side of the rectangle HotSpot region. - */ - public function setTop($value) - { - $this->setViewState('Top',TPropertyValue::ensureInteger($value),0); - } -} - -/** - * Class TPolygonHotSpot. - * - * TPolygonHotSpot defines a polygon hot spot region in a {@link - * TImageMap} control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TPolygonHotSpot extends THotSpot -{ - /** - * @return string shape of this hotspot. - */ - public function getShape() - { - return 'poly'; - } - - /** - * @return string coordinates of the vertices defining the polygon. - * Coordinates are concatenated together with comma ','. Each pair - * represents (x,y) of a vertex. - */ - public function getCoordinates() - { - return $this->getViewState('Coordinates',''); - } - - /** - * @param string coordinates of the vertices defining the polygon. - * Coordinates are concatenated together with comma ','. Each pair - * represents (x,y) of a vertex. - */ - public function setCoordinates($value) - { - $this->setViewState('Coordinates',$value,''); - } -} - - -/** - * THotSpotMode class. - * THotSpotMode defines the enumerable type for the possible hot spot modes. - * - * The following enumerable values are defined: - * - NotSet: the mode is not specified - * - Navigate: clicking on the hotspot will redirect the browser to a different page - * - PostBack: clicking on the hotspot will cause a postback - * - Inactive: the hotspot is inactive (not clickable) - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class THotSpotMode extends TEnumerable -{ - const NotSet='NotSet'; - const Navigate='Navigate'; - const PostBack='PostBack'; - const Inactive='Inactive'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TImage class file + */ +Prado::using('System.Web.UI.WebControls.TImage'); + +/** + * TImageMap class + * + * TImageMap represents an image on a page. Hotspot regions can be defined + * within the image. Depending on the {@link setHotSpotMode HotSpotMode}, + * clicking on the hotspots may trigger a postback or navigate to a specified + * URL. The hotspots defined may be accessed via {@link getHotSpots HotSpots}. + * Each hotspot is described as a {@link THotSpot}, which can be a circle, + * rectangle, polygon, etc. To add hotspot in a template, use the following, + * + * + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageMap extends TImage implements IPostBackEventHandler +{ + const MAP_NAME_PREFIX='ImageMap'; + + /** + * Processes an object that is created during parsing template. + * This method adds {@link THotSpot} objects into the hotspot collection + * of the imagemap. + * @param string|TComponent text string or component parsed and instantiated in template + */ + public function addParsedObject($object) + { + if($object instanceof THotSpot) + $this->getHotSpots()->add($object); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional imagemap specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if($this->getHotSpots()->getCount()>0) + { + $writer->addAttribute('usemap','#'.self::MAP_NAME_PREFIX.$this->getClientID()); + $writer->addAttribute('id',$this->getUniqueID()); + } + if($this->getEnabled() && !$this->getEnabled(true)) + $writer->addAttribute('disabled','disabled'); + } + + /** + * Renders this imagemap. + * @param THtmlWriter + */ + public function render($writer) + { + parent::render($writer); + + $hotspots=$this->getHotSpots(); + + if($hotspots->getCount()>0) + { + $clientID=$this->getClientID(); + $cs=$this->getPage()->getClientScript(); + $writer->writeLine(); + $writer->addAttribute('name',self::MAP_NAME_PREFIX.$clientID); + $writer->renderBeginTag('map'); + $writer->writeLine(); + if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) + $mode=THotSpotMode::Navigate; + $target=$this->getTarget(); + $i=0; + $options['EventTarget'] = $this->getUniqueID(); + $options['StopEvent'] = true; + $cs=$this->getPage()->getClientScript(); + foreach($hotspots as $hotspot) + { + if($hotspot->getHotSpotMode()===THotSpotMode::NotSet) + $hotspot->setHotSpotMode($mode); + if($target!=='' && $hotspot->getTarget()==='') + $hotspot->setTarget($target); + if($hotspot->getHotSpotMode()===THotSpotMode::PostBack) + { + $id=$clientID.'_'.$i; + $writer->addAttribute('id',$id); + $writer->addAttribute('href','#'.$id); //create unique no-op url references + $options['ID']=$id; + $options['EventParameter']="$i"; + $options['CausesValidation']=$hotspot->getCausesValidation(); + $options['ValidationGroup']=$hotspot->getValidationGroup(); + $cs->registerPostBackControl($this->getClientClassName(),$options); + } + $hotspot->render($writer); + $writer->writeLine(); + $i++; + } + $writer->renderEndTag(); + } + } + + /** + * 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.TImageMap'; + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + $postBackValue=null; + if($param!=='') + { + $index=TPropertyValue::ensureInteger($param); + $hotspots=$this->getHotSpots(); + if($index>=0 && $index<$hotspots->getCount()) + { + $hotspot=$hotspots->itemAt($index); + if(($mode=$hotspot->getHotSpotMode())===THotSpotMode::NotSet) + $mode=$this->getHotSpotMode(); + if($mode===THotSpotMode::PostBack) + { + $postBackValue=$hotspot->getPostBackValue(); + if($hotspot->getCausesValidation()) + $this->getPage()->validate($hotspot->getValidationGroup()); + } + } + } + if($postBackValue!==null) + $this->onClick(new TImageMapEventParameter($postBackValue)); + } + + /** + * @return THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. Defaults to THotSpotMode::NotSet. + */ + public function getHotSpotMode() + { + return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); + } + + /** + * Sets the behavior of hotspot regions in this imagemap when they are clicked. + * If an individual hotspot has a mode other than 'NotSet', the mode set in this + * imagemap will be ignored. By default, 'NotSet' is equivalent to 'Navigate'. + * @param THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. + */ + public function setHotSpotMode($value) + { + $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); + } + + /** + * @return THotSpotCollection collection of hotspots defined in this imagemap. + */ + public function getHotSpots() + { + if(($hotspots=$this->getViewState('HotSpots',null))===null) + { + $hotspots=new THotSpotCollection; + $this->setViewState('HotSpots',$hotspots); + } + return $hotspots; + } + + /** + * @return string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. Defaults to ''. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * @param string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. + */ + public function setTarget($value) + { + $this->setViewState('Target',TPropertyValue::ensureString($value),''); + } + + /** + * Raises OnClick event. + * This method is invoked when a hotspot region is clicked within the imagemap. + * If you override this method, be sure to call the parent implementation + * so that the event handler can be invoked. + * @param TImageMapEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } +} + +/** + * TImageMapEventParameter class. + * + * TImageMapEventParameter represents a postback event parameter + * when a hotspot is clicked and posts back in a {@link TImageMap}. + * To retrieve the post back value associated with the hotspot being clicked, + * access {@link getPostBackValue PostBackValue}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageMapEventParameter extends TEventParameter +{ + private $_postBackValue; + + /** + * Constructor. + * @param string post back value associated with the hotspot clicked + */ + public function __construct($postBackValue) + { + $this->_postBackValue=$postBackValue; + } + + /** + * @return string post back value associated with the hotspot clicked + */ + public function getPostBackValue() + { + return $this->_postBackValue; + } +} + +/** + * THotSpotCollection class. + * + * THotSpotCollection represents a collection of hotspots in an imagemap. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THotSpotCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only {@link THotSpot}. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a THotSpot. + */ + public function insertAt($index,$item) + { + if($item instanceof THotSpot) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('hotspotcollection_hotspot_required'); + } +} + + +/** + * THotSpot class. + * + * THotSpot implements the basic functionality common to all hot spot shapes. + * Derived classes include {@link TCircleHotSpot}, {@link TPolygonHotSpot} + * and {@link TRectangleHotSpot}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class THotSpot extends TComponent +{ + private $_viewState=array(); + + /** + * Returns a viewstate value. + * + * This function is very useful in defining getter functions for component properties + * that must be kept in viewstate. + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + protected function getViewState($key,$defaultValue=null) + { + return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; + } + + /** + * Sets a viewstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in viewstate. + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + protected function setViewState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_viewState[$key]); + else + $this->_viewState[$key]=$value; + } + + /** + * @return string shape of the hotspot, can be 'circle', 'rect', 'poly', etc. + */ + abstract public function getShape(); + /** + * @return string coordinates defining the hotspot shape. + */ + abstract public function getCoordinates(); + + /** + * @return string the access key that allows you to quickly navigate to the HotSpot region. Defaults to ''. + */ + public function getAccessKey() + { + return $this->getViewState('AccessKey',''); + } + + /** + * @param string the access key that allows you to quickly navigate to the HotSpot region. + */ + public function setAccessKey($value) + { + $this->setViewState('AccessKey',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the alternate text to display for a HotSpot object. Defaults to ''. + */ + public function getAlternateText() + { + return $this->getViewState('AlternateText',''); + } + + /** + * @param string the alternate text to display for a HotSpot object. + */ + public function setAlternateText($value) + { + $this->setViewState('AlternateText',TPropertyValue::ensureString($value),''); + } + + /** + * @return THotSpotMode the behavior of a HotSpot object when it is clicked. Defaults to THotSpotMode::NotSet. + */ + public function getHotSpotMode() + { + return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); + } + + /** + * @param THotSpotMode the behavior of a HotSpot object when it is clicked. + */ + public function setHotSpotMode($value) + { + $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); + } + + /** + * @return string the URL to navigate to when a HotSpot object is clicked. Defaults to ''. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * @param string the URL to navigate to when a HotSpot object is clicked. + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string a value that is post back when the HotSpot is clicked. Defaults to ''. + */ + public function getPostBackValue() + { + return $this->getViewState('PostBackValue',''); + } + + /** + * @param string a value that is post back when the HotSpot is clicked. + */ + public function setPostBackValue($value) + { + $this->setViewState('PostBackValue',TPropertyValue::ensureString($value),''); + } + + /** + * @return integer the tab index of the HotSpot region. Defaults to 0. + */ + public function getTabIndex() + { + return $this->getViewState('TabIndex',0); + } + + /** + * @param integer the tab index of the HotSpot region. + */ + public function setTabIndex($value) + { + $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return boolean whether postback event trigger by this hotspot will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this hotspot will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the hotspot causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the hotspot causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string the target window or frame to display the new page when the HotSpot region + * is clicked. Defaults to ''. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * @param string the target window or frame to display the new page when the HotSpot region + * is clicked. + */ + public function setTarget($value) + { + $this->setViewState('Target',TPropertyValue::ensureString($value),''); + } + + /** + * @return boolean whether the hotspot has custom attributes + */ + public function getHasAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->getCount()>0; + else + return false; + } + + /** + * Returns the list of custom attributes. + * Custom attributes are name-value pairs that may be rendered + * as HTML tags' attributes. + * @return TAttributeCollection the list of custom attributes + */ + public function getAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes; + else + { + $attributes=new TAttributeCollection; + $this->setViewState('Attributes',$attributes,null); + return $attributes; + } + } + + /** + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->contains($name); + else + return false; + } + + /** + * @return string attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->itemAt($name); + else + return null; + } + + /** + * Sets a custom hotspot attribute. + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, null if attribute does not exist. + */ + public function removeAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->remove($name); + else + return null; + } + + /** + * Renders this hotspot. + * @param THtmlWriter + */ + public function render($writer) + { + $writer->addAttribute('shape',$this->getShape()); + $writer->addAttribute('coords',$this->getCoordinates()); + if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) + $mode=THotSpotMode::Navigate; + if($mode===THotSpotMode::Navigate) + { + $writer->addAttribute('href',$this->getNavigateUrl()); + if(($target=$this->getTarget())!=='') + $writer->addAttribute('target',$target); + } + else if($mode===THotSpotMode::Inactive) + $writer->addAttribute('nohref','true'); + $text=$this->getAlternateText(); + $writer->addAttribute('title',$text); + $writer->addAttribute('alt',$text); + if(($accessKey=$this->getAccessKey())!=='') + $writer->addAttribute('accesskey',$accessKey); + if(($tabIndex=$this->getTabIndex())!==0) + $writer->addAttribute('tabindex',"$tabIndex"); + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + $writer->renderBeginTag('area'); + $writer->renderEndTag(); + } +} + +/** + * Class TCircleHotSpot. + * + * TCircleHotSpot defines a circular hot spot region in a {@link TImageMap} + * control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCircleHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'circle'; + } + + /** + * @return string coordinates defining this hotspot shape + */ + public function getCoordinates() + { + return $this->getX().','.$this->getY().','.$this->getRadius(); + } + + /** + * @return integer radius of the circular HotSpot region. Defaults to 0. + */ + public function getRadius() + { + return $this->getViewState('Radius',0); + } + + /** + * @param integer radius of the circular HotSpot region. + */ + public function setRadius($value) + { + $this->setViewState('Radius',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the center of the circular HotSpot region. Defaults to 0. + */ + public function getX() + { + return $this->getViewState('X',0); + } + + /** + * @param integer the X coordinate of the center of the circular HotSpot region. + */ + public function setX($value) + { + $this->setViewState('X',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the Y coordinate of the center of the circular HotSpot region. Defaults to 0. + */ + public function getY() + { + return $this->getViewState('Y',0); + } + + /** + * @param integer the Y coordinate of the center of the circular HotSpot region. + */ + public function setY($value) + { + $this->setViewState('Y',TPropertyValue::ensureInteger($value),0); + } +} + +/** + * Class TRectangleHotSpot. + * + * TRectangleHotSpot defines a rectangle hot spot region in a {@link + * TImageMap} control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRectangleHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'rect'; + } + + /** + * @return string coordinates defining this hotspot shape + */ + public function getCoordinates() + { + return $this->getLeft().','.$this->getTop().','.$this->getRight().','.$this->getBottom(); + } + + /** + * @return integer the Y coordinate of the bottom side of the rectangle HotSpot region. Defaults to 0. + */ + public function getBottom() + { + return $this->getViewState('Bottom',0); + } + + /** + * @param integer the Y coordinate of the bottom side of the rectangle HotSpot region. + */ + public function setBottom($value) + { + $this->setViewState('Bottom',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. + */ + public function getLeft() + { + return $this->getViewState('Left',0); + } + + /** + * @param integer the X coordinate of the right side of the rectangle HotSpot region. + */ + public function setLeft($value) + { + $this->setViewState('Left',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. + */ + public function getRight() + { + return $this->getViewState('Right',0); + } + + /** + * @param integer the X coordinate of the right side of the rectangle HotSpot region. + */ + public function setRight($value) + { + $this->setViewState('Right',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the Y coordinate of the top side of the rectangle HotSpot region. Defaults to 0. + */ + public function getTop() + { + return $this->getViewState('Top',0); + } + + /** + * @param integer the Y coordinate of the top side of the rectangle HotSpot region. + */ + public function setTop($value) + { + $this->setViewState('Top',TPropertyValue::ensureInteger($value),0); + } +} + +/** + * Class TPolygonHotSpot. + * + * TPolygonHotSpot defines a polygon hot spot region in a {@link + * TImageMap} control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TPolygonHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'poly'; + } + + /** + * @return string coordinates of the vertices defining the polygon. + * Coordinates are concatenated together with comma ','. Each pair + * represents (x,y) of a vertex. + */ + public function getCoordinates() + { + return $this->getViewState('Coordinates',''); + } + + /** + * @param string coordinates of the vertices defining the polygon. + * Coordinates are concatenated together with comma ','. Each pair + * represents (x,y) of a vertex. + */ + public function setCoordinates($value) + { + $this->setViewState('Coordinates',$value,''); + } +} + + +/** + * THotSpotMode class. + * THotSpotMode defines the enumerable type for the possible hot spot modes. + * + * The following enumerable values are defined: + * - NotSet: the mode is not specified + * - Navigate: clicking on the hotspot will redirect the browser to a different page + * - PostBack: clicking on the hotspot will cause a postback + * - Inactive: the hotspot is inactive (not clickable) + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class THotSpotMode extends TEnumerable +{ + const NotSet='NotSet'; + const Navigate='Navigate'; + const PostBack='PostBack'; + const Inactive='Inactive'; +} + diff --git a/framework/Web/UI/WebControls/TItemDataRenderer.php b/framework/Web/UI/WebControls/TItemDataRenderer.php index 60b34873..80d7f418 100644 --- a/framework/Web/UI/WebControls/TItemDataRenderer.php +++ b/framework/Web/UI/WebControls/TItemDataRenderer.php @@ -1,83 +1,83 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.2 - */ - -Prado::using('System.Web.UI.WebControls.TDataBoundControl'); -Prado::using('System.Web.UI.WebControls.TDataRenderer'); - -/** - * TItemDataRenderer class - * - * TItemDataRenderer is the convient base class for template-based item data renderers. - * It implements the {@link IItemDataRenderer} interface, and because - * TItemDataRenderer extends from {@link TTemplateControl}, derived child - * classes can have templates to define their presentational layout. - * - * The following properties are provided by TItemDataRenderer: - * - {@link getItemIndex ItemIndex}: zero-based index of this renderer in the item list collection. - * - {@link getItemType ItemType}: item type of this renderer, such as TListItemType::AlternatingItem - * - {@link getData Data}: data associated with this renderer - - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.2 - */ -abstract class TItemDataRenderer extends TDataRenderer implements IItemDataRenderer -{ - /** - * index of the data item in the Items collection of repeater - */ - private $_itemIndex; - /** - * type of the TRepeaterItem - * @var TListItemType - */ - private $_itemType; - - /** - * @return TListItemType item type - */ - public function getItemType() - { - return $this->_itemType; - } - - /** - * @param TListItemType item type. - */ - public function setItemType($value) - { - $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); - } - - /** - * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. - * If the item is not in the collection (e.g. it is a header item), it returns -1. - * @return integer zero-based index of the item. - */ - public function getItemIndex() - { - return $this->_itemIndex; - } - - /** - * Sets the zero-based index for the item. - * If the item is not in the item collection (e.g. it is a header item), -1 should be used. - * @param integer zero-based index of the item. - */ - public function setItemIndex($value) - { - $this->_itemIndex=TPropertyValue::ensureInteger($value); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ + +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Web.UI.WebControls.TDataRenderer'); + +/** + * TItemDataRenderer class + * + * TItemDataRenderer is the convient base class for template-based item data renderers. + * It implements the {@link IItemDataRenderer} interface, and because + * TItemDataRenderer extends from {@link TTemplateControl}, derived child + * classes can have templates to define their presentational layout. + * + * The following properties are provided by TItemDataRenderer: + * - {@link getItemIndex ItemIndex}: zero-based index of this renderer in the item list collection. + * - {@link getItemType ItemType}: item type of this renderer, such as TListItemType::AlternatingItem + * - {@link getData Data}: data associated with this renderer + + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +abstract class TItemDataRenderer extends TDataRenderer implements IItemDataRenderer +{ + /** + * index of the data item in the Items collection of repeater + */ + private $_itemIndex; + /** + * type of the TRepeaterItem + * @var TListItemType + */ + private $_itemType; + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } +} + diff --git a/framework/Web/UI/WebControls/TJavascriptLogger.php b/framework/Web/UI/WebControls/TJavascriptLogger.php index e0d695f4..3b430357 100644 --- a/framework/Web/UI/WebControls/TJavascriptLogger.php +++ b/framework/Web/UI/WebControls/TJavascriptLogger.php @@ -1,93 +1,93 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TJavascriptLogger class. - * - * Provides logging for client-side javascript. Example: template code - * - * - * Client-side javascript code to log info, error, warn, debug - * Logger.warn('A warning'); - * Logger.info('something happend'); - * - * - * To see the logger and console, press ALT-D (or CTRL-D on OS X). - * More information on the logger can be found at - * http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/ - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TJavascriptLogger extends TWebControl -{ - private static $_keyCodes = array( - '0'=>48, '1'=>49, '2'=>50, '3'=>51, '4'=>52, '5'=>53, '6'=>54, '7'=>55, '8'=>56, '9'=>57, - 'a'=>65, 'b'=>66, 'c'=>67, 'd'=>68, 'e'=>69, 'f'=>70, 'g'=>71, 'h'=>72, - 'i'=>73, 'j'=>74, 'k'=>75, 'l'=>76, 'm'=>77, 'n'=>78, 'o'=>79, 'p'=>80, - 'q'=>81, 'r'=>82, 's'=>83, 't'=>84, 'u'=>85, 'v'=>86, 'w'=>87, 'x'=>88, 'y'=>89, 'z'=>90); - - /** - * @return string tag name of the panel - */ - protected function getTagName() - { - return 'div'; - } - - /** - * @param string keyboard key for toggling the console, default is J. - */ - public function setToggleKey($value) - { - $this->setViewState('ToggleKey', $value, 'j'); - } - - /** - * @return string keyboard key for toggling the console. - */ - public function getToggleKey() - { - return $this->getViewState('ToggleKey', 'j'); - } - - /** - * Registers the required logger javascript. - * @param TEventParameter event parameter - */ - public function onPreRender($param) - { - $key = strtolower($this->getToggleKey()); - $code = isset(self::$_keyCodes[$key]) ? self::$_keyCodes[$key] : 74; - $js = "var logConsole; Event.OnLoad(function() { logConsole = new LogConsole($code)}); "; - $cs = $this->getPage()->getClientScript(); - $cs->registerBeginScript($this->getClientID(),$js); - $cs->registerPradoScript('logger'); - } - - /** - * Register the required javascript libraries and - * display some general usage information. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderContents($writer) - { - $code = strtoupper($this->getToggleKey()); - $info = '(more info).'; - $link = 'toggle the javascript log console.'; - $usage = 'Press ALT-'.$code.' (Or CTRL-'.$code.' on OS X) to'; - $writer->write("{$usage} {$link} {$info}"); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TJavascriptLogger class. + * + * Provides logging for client-side javascript. Example: template code + * + * + * Client-side javascript code to log info, error, warn, debug + * Logger.warn('A warning'); + * Logger.info('something happend'); + * + * + * To see the logger and console, press ALT-D (or CTRL-D on OS X). + * More information on the logger can be found at + * http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/ + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TJavascriptLogger extends TWebControl +{ + private static $_keyCodes = array( + '0'=>48, '1'=>49, '2'=>50, '3'=>51, '4'=>52, '5'=>53, '6'=>54, '7'=>55, '8'=>56, '9'=>57, + 'a'=>65, 'b'=>66, 'c'=>67, 'd'=>68, 'e'=>69, 'f'=>70, 'g'=>71, 'h'=>72, + 'i'=>73, 'j'=>74, 'k'=>75, 'l'=>76, 'm'=>77, 'n'=>78, 'o'=>79, 'p'=>80, + 'q'=>81, 'r'=>82, 's'=>83, 't'=>84, 'u'=>85, 'v'=>86, 'w'=>87, 'x'=>88, 'y'=>89, 'z'=>90); + + /** + * @return string tag name of the panel + */ + protected function getTagName() + { + return 'div'; + } + + /** + * @param string keyboard key for toggling the console, default is J. + */ + public function setToggleKey($value) + { + $this->setViewState('ToggleKey', $value, 'j'); + } + + /** + * @return string keyboard key for toggling the console. + */ + public function getToggleKey() + { + return $this->getViewState('ToggleKey', 'j'); + } + + /** + * Registers the required logger javascript. + * @param TEventParameter event parameter + */ + public function onPreRender($param) + { + $key = strtolower($this->getToggleKey()); + $code = isset(self::$_keyCodes[$key]) ? self::$_keyCodes[$key] : 74; + $js = "var logConsole; Event.OnLoad(function() { logConsole = new LogConsole($code)}); "; + $cs = $this->getPage()->getClientScript(); + $cs->registerBeginScript($this->getClientID(),$js); + $cs->registerPradoScript('logger'); + } + + /** + * Register the required javascript libraries and + * display some general usage information. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + $code = strtoupper($this->getToggleKey()); + $info = '(more info).'; + $link = 'toggle the javascript log console.'; + $usage = 'Press ALT-'.$code.' (Or CTRL-'.$code.' on OS X) to'; + $writer->write("{$usage} {$link} {$info}"); + } +} + diff --git a/framework/Web/UI/WebControls/TKeyboard.php b/framework/Web/UI/WebControls/TKeyboard.php index 27cb55c5..58c80e26 100644 --- a/framework/Web/UI/WebControls/TKeyboard.php +++ b/framework/Web/UI/WebControls/TKeyboard.php @@ -1,189 +1,189 @@ - and Qiang Xue - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ - -/** - * Class TKeyboard. - * - * TKeyboard displays a virtual keyboard that users can click on to enter input in - * an associated text box. It helps to reduce the keyboard recording hacking. - * - * To use TKeyboard, write a template like following: - * - * - * - * - * - * A TKeyboard control is associated with a {@link TTextBox} control by specifying {@link setForControl ForControl} - * to be the ID of that control. When the textbox is in focus, a virtual keyboard will pop up; and when - * the text box is losing focus, the keyboard will hide automatically. Set {@link setAutoHide AutoHide} to - * false to keep the keyboard showing all the time. - * - * The appearance of the keyboard can also be changed by specifying a customized CSS file via - * {@link setCssUrl CssUrl}. By default, the CSS class name for the keyboard is 'Keyboard'. This may - * also be changed by specifying {@link setKeyboardCssClass KeyboardCssClass}. - * - * @author Sergey Morkovkin and Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ -class TKeyboard extends TWebControl -{ - /** - * @return string the ID path of the {@link TTextBox} control - */ - public function getForControl() - { - return $this->getViewState('ForControl',''); - } - - /** - * Sets the ID path of the {@link TTextBox} control. - * The ID path is the dot-connected IDs of the controls reaching from - * the keyboard's naming container to the target control. - * @param string the ID path - */ - public function setForControl($value) - { - $this->setViewState('ForControl', TPropertyValue::ensureString($value)); - } - - /** - * @return boolean whether the keyboard should be hidden when the textbox is not in focus. Defaults to true. - */ - public function getAutoHide() - { - return $this->getViewState('AutoHide', true); - } - - /** - * @param boolean whether the keyboard should be hidden when the textbox is not in focus. - */ - public function setAutoHide($value) - { - $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); - } - - /** - * @return string the CSS class name for the keyboard
    element. Defaults to 'Keyboard'. - */ - public function getKeyboardCssClass() - { - return $this->getViewState('KeyboardCssClass', 'Keyboard'); - } - - /** - * Sets a value indicating the CSS class name for the keyboard
    element. - * Note, if you change this property, make sure you also supply a customized CSS file - * by specifying {@link setCssUrl CssUrl} which uses the new CSS class name for styling. - * @param string the CSS class name for the keyboard
    element. - */ - public function setKeyboardCssClass($value) - { - $this->setViewState('KeyboardCssClass', $value, 'Keyboard'); - } - - /** - * @return string the URL for the CSS file to customize the appearance of the keyboard. - */ - public function getCssUrl() - { - return $this->getViewState('CssUrl', ''); - } - - /** - * @param string the URL for the CSS file to customize the appearance of the keyboard. - */ - public function setCssUrl($value) - { - $this->setViewState('CssUrl', $value, ''); - } - - /** - * Registers CSS and JS. - * This method is invoked right before the control rendering, if the control is visible. - * @param mixed event parameter - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if($this->getPage()->getClientSupportsJavaScript()) - { - $this->registerStyleSheet(); - $this->registerClientScript(); - } - } - - /** - * Adds attribute name-value pairs to renderer. - * This method overrides the parent implementation with additional TKeyboard specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - if($this->getPage()->getClientSupportsJavaScript()) - $writer->addAttribute('id',$this->getClientID()); - } - - /** - * Registers the CSS relevant to the TKeyboard. - * It will register the CSS file specified by {@link getCssUrl CssUrl}. - * If that is not set, it will use the default CSS. - */ - protected function registerStyleSheet() - { - if(($url=$this->getCssUrl())==='') - $url=$this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'keyboard.css'); - $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); - } - - /** - * Registers the relevant JavaScript. - */ - protected function registerClientScript() - { - $options=TJavaScript::encode($this->getClientOptions()); - $className=$this->getClientClassName(); - $cs=$this->getPage()->getClientScript(); - $cs->registerPradoScript('keyboard'); - $cs->registerEndScript('prado:'.$this->getClientID(), "new $className($options);"); - } - - /** - * @return string the Javascript class name for this control - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TKeyboard'; - } - - /** - * @return array the JavaScript options for this control - */ - protected function getClientOptions() - { - if(($forControl=$this->getForControl())==='') - throw new TConfigurationException('keyboard_forcontrol_required'); - if(($target=$this->findControl($forControl))===null) - throw new TConfigurationException('keyboard_forcontrol_invalid',$forControl); - - $options['ID'] = $this->getClientID(); - $options['ForControl'] = $target->getClientID(); - $options['AutoHide'] = $this->getAutoHide(); - $options['CssClass'] = $this->getKeyboardCssClass(); - - return $options; - } -} - + and Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ + +/** + * Class TKeyboard. + * + * TKeyboard displays a virtual keyboard that users can click on to enter input in + * an associated text box. It helps to reduce the keyboard recording hacking. + * + * To use TKeyboard, write a template like following: + * + * + * + * + * + * A TKeyboard control is associated with a {@link TTextBox} control by specifying {@link setForControl ForControl} + * to be the ID of that control. When the textbox is in focus, a virtual keyboard will pop up; and when + * the text box is losing focus, the keyboard will hide automatically. Set {@link setAutoHide AutoHide} to + * false to keep the keyboard showing all the time. + * + * The appearance of the keyboard can also be changed by specifying a customized CSS file via + * {@link setCssUrl CssUrl}. By default, the CSS class name for the keyboard is 'Keyboard'. This may + * also be changed by specifying {@link setKeyboardCssClass KeyboardCssClass}. + * + * @author Sergey Morkovkin and Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TKeyboard extends TWebControl +{ + /** + * @return string the ID path of the {@link TTextBox} control + */ + public function getForControl() + { + return $this->getViewState('ForControl',''); + } + + /** + * Sets the ID path of the {@link TTextBox} control. + * The ID path is the dot-connected IDs of the controls reaching from + * the keyboard's naming container to the target control. + * @param string the ID path + */ + public function setForControl($value) + { + $this->setViewState('ForControl', TPropertyValue::ensureString($value)); + } + + /** + * @return boolean whether the keyboard should be hidden when the textbox is not in focus. Defaults to true. + */ + public function getAutoHide() + { + return $this->getViewState('AutoHide', true); + } + + /** + * @param boolean whether the keyboard should be hidden when the textbox is not in focus. + */ + public function setAutoHide($value) + { + $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return string the CSS class name for the keyboard
    element. Defaults to 'Keyboard'. + */ + public function getKeyboardCssClass() + { + return $this->getViewState('KeyboardCssClass', 'Keyboard'); + } + + /** + * Sets a value indicating the CSS class name for the keyboard
    element. + * Note, if you change this property, make sure you also supply a customized CSS file + * by specifying {@link setCssUrl CssUrl} which uses the new CSS class name for styling. + * @param string the CSS class name for the keyboard
    element. + */ + public function setKeyboardCssClass($value) + { + $this->setViewState('KeyboardCssClass', $value, 'Keyboard'); + } + + /** + * @return string the URL for the CSS file to customize the appearance of the keyboard. + */ + public function getCssUrl() + { + return $this->getViewState('CssUrl', ''); + } + + /** + * @param string the URL for the CSS file to customize the appearance of the keyboard. + */ + public function setCssUrl($value) + { + $this->setViewState('CssUrl', $value, ''); + } + + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getPage()->getClientSupportsJavaScript()) + { + $this->registerStyleSheet(); + $this->registerClientScript(); + } + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional TKeyboard specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if($this->getPage()->getClientSupportsJavaScript()) + $writer->addAttribute('id',$this->getClientID()); + } + + /** + * Registers the CSS relevant to the TKeyboard. + * It will register the CSS file specified by {@link getCssUrl CssUrl}. + * If that is not set, it will use the default CSS. + */ + protected function registerStyleSheet() + { + if(($url=$this->getCssUrl())==='') + $url=$this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'keyboard.css'); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + + /** + * Registers the relevant JavaScript. + */ + protected function registerClientScript() + { + $options=TJavaScript::encode($this->getClientOptions()); + $className=$this->getClientClassName(); + $cs=$this->getPage()->getClientScript(); + $cs->registerPradoScript('keyboard'); + $cs->registerEndScript('prado:'.$this->getClientID(), "new $className($options);"); + } + + /** + * @return string the Javascript class name for this control + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TKeyboard'; + } + + /** + * @return array the JavaScript options for this control + */ + protected function getClientOptions() + { + if(($forControl=$this->getForControl())==='') + throw new TConfigurationException('keyboard_forcontrol_required'); + if(($target=$this->findControl($forControl))===null) + throw new TConfigurationException('keyboard_forcontrol_invalid',$forControl); + + $options['ID'] = $this->getClientID(); + $options['ForControl'] = $target->getClientID(); + $options['AutoHide'] = $this->getAutoHide(); + $options['CssClass'] = $this->getKeyboardCssClass(); + + return $options; + } +} + diff --git a/framework/Web/UI/WebControls/TLabel.php b/framework/Web/UI/WebControls/TLabel.php index 31e424e5..7228d588 100644 --- a/framework/Web/UI/WebControls/TLabel.php +++ b/framework/Web/UI/WebControls/TLabel.php @@ -1,154 +1,154 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TLabel class - * - * TLabel displays a piece of text on a Web page. - * Use {@link setText Text} property to set the text to be displayed. - * TLabel will render the contents enclosed within its component tag - * if {@link setText Text} is empty. - * To use TLabel as a form label, associate it with a control by setting the - * {@link setForControl ForControl} property. - * The associated control must be locatable within the label's naming container. - * If the associated control is not visible, the label will not be rendered, either. - * - * Note, {@link setText Text} will NOT be encoded for rendering. - * Make sure it does not contain dangerous characters that you want to avoid. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TLabel extends TWebControl implements IDataRenderer -{ - private $_forControl=''; - - /** - * @return string tag name of the label, returns 'label' if there is an associated control, 'span' otherwise. - */ - protected function getTagName() - { - return ($this->getForControl()==='')?'span':'label'; - } - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - * @throws TInvalidDataValueException if associated control cannot be found using the ID - */ - protected function addAttributesToRender($writer) - { - if($this->_forControl!=='') - $writer->addAttribute('for',$this->_forControl); - parent::addAttributesToRender($writer); - } - - /** - * Renders the label. - * It overrides the parent implementation by checking if an associated - * control is visible or not. If not, the label will not be rendered. - * @param THtmlWriter writer - */ - public function render($writer) - { - if(($aid=$this->getForControl())!=='') - { - if($control=$this->findControl($aid)) - { - if($control->getVisible(true)) - { - $this->_forControl=$control->getClientID(); - parent::render($writer); - } - } - else - throw new TInvalidDataValueException('label_associatedcontrol_invalid',$aid); - } - else - parent::render($writer); - } - - /** - * Renders the body content of the label. - * @param THtmlWriter the renderer - */ - public function renderContents($writer) - { - if(($text=$this->getText())==='') - parent::renderContents($writer); - else - $writer->write($text); - } - - /** - * @return string the text value of the label - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * @param string the text value of the label - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * Returns the text value of the label. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string the text value of the label - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the text value of the label. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string the text value of the label - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * @return string the associated control ID - */ - public function getForControl() - { - return $this->getViewState('ForControl',''); - } - - /** - * Sets the ID of the control that the label is associated with. - * The control must be locatable via {@link TControl::findControl} using the ID. - * @param string the associated control ID - */ - public function setForControl($value) - { - $this->setViewState('ForControl',$value,''); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TLabel class + * + * TLabel displays a piece of text on a Web page. + * Use {@link setText Text} property to set the text to be displayed. + * TLabel will render the contents enclosed within its component tag + * if {@link setText Text} is empty. + * To use TLabel as a form label, associate it with a control by setting the + * {@link setForControl ForControl} property. + * The associated control must be locatable within the label's naming container. + * If the associated control is not visible, the label will not be rendered, either. + * + * Note, {@link setText Text} will NOT be encoded for rendering. + * Make sure it does not contain dangerous characters that you want to avoid. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLabel extends TWebControl implements IDataRenderer +{ + private $_forControl=''; + + /** + * @return string tag name of the label, returns 'label' if there is an associated control, 'span' otherwise. + */ + protected function getTagName() + { + return ($this->getForControl()==='')?'span':'label'; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + * @throws TInvalidDataValueException if associated control cannot be found using the ID + */ + protected function addAttributesToRender($writer) + { + if($this->_forControl!=='') + $writer->addAttribute('for',$this->_forControl); + parent::addAttributesToRender($writer); + } + + /** + * Renders the label. + * It overrides the parent implementation by checking if an associated + * control is visible or not. If not, the label will not be rendered. + * @param THtmlWriter writer + */ + public function render($writer) + { + if(($aid=$this->getForControl())!=='') + { + if($control=$this->findControl($aid)) + { + if($control->getVisible(true)) + { + $this->_forControl=$control->getClientID(); + parent::render($writer); + } + } + else + throw new TInvalidDataValueException('label_associatedcontrol_invalid',$aid); + } + else + parent::render($writer); + } + + /** + * Renders the body content of the label. + * @param THtmlWriter the renderer + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string the text value of the label + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string the text value of the label + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the text value of the label. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text value of the label + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text value of the label. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text value of the label + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the associated control ID + */ + public function getForControl() + { + return $this->getViewState('ForControl',''); + } + + /** + * Sets the ID of the control that the label is associated with. + * The control must be locatable via {@link TControl::findControl} using the ID. + * @param string the associated control ID + */ + public function setForControl($value) + { + $this->setViewState('ForControl',$value,''); + } +} + diff --git a/framework/Web/UI/WebControls/TLinkButton.php b/framework/Web/UI/WebControls/TLinkButton.php index 9da5ec59..f03f9098 100644 --- a/framework/Web/UI/WebControls/TLinkButton.php +++ b/framework/Web/UI/WebControls/TLinkButton.php @@ -1,334 +1,334 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TLinkButton class - * - * TLinkButton creates a hyperlink style button on the page. - * TLinkButton has the same appearance as a hyperlink. However, it is mainly - * used to submit data to a page. Like {@link TButton}, you can create either - * a submit button or a command button. - * - * A command button has a command name (specified by - * the {@link setCommandName CommandName} property) and and a command parameter - * (specified by {@link setCommandParameter CommandParameter} property) - * associated with the button. This allows you to create multiple TLinkButton - * components on a Web page and programmatically determine which one is clicked - * with what parameter. You can provide an event handler for - * {@link onCommand OnCommand} event to programmatically control the actions performed - * when the command button is clicked. In the event handler, you can determine - * the {@link setCommandName CommandName} property value and - * the {@link setCommandParameter CommandParameter} property value - * through the {@link TCommandParameter::getName Name} and - * {@link TCommandParameter::getParameter Parameter} properties of the event - * parameter which is of type {@link TCommandEventParameter}. - * - * A submit button does not have a command name associated with the button - * and clicking on it simply posts the Web page back to the server. - * By default, a TLinkButton component is a submit button. - * You can provide an event handler for the {@link onClick OnClick} event - * to programmatically control the actions performed when the submit button is clicked. - * - * Clicking on button can trigger form validation, if - * {@link setCausesValidation CausesValidation} is true. - * And the validation may be restricted within a certain group of validator - * controls by setting {@link setValidationGroup ValidationGroup} property. - * If validation is successful, the data will be post back to the same page. - * - * TLinkButton will display the {@link setText Text} property value - * as the hyperlink text. If {@link setText Text} is empty, the body content - * of TLinkButton will be displayed. Therefore, you can use TLinkButton - * as an image button by enclosing an <img> tag as the body of TLinkButton. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TLinkButton extends TWebControl implements IPostBackEventHandler, IButtonControl, IDataRenderer -{ - /** - * @return string tag name of the button - */ - protected function getTagName() - { - return 'a'; - } - - /** - * @return boolean whether to render javascript. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether to render javascript. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Adds attribute name-value pairs to renderer. - * This overrides the parent implementation with additional button specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $page=$this->getPage(); - $page->ensureRenderInForm($this); - - $writer->addAttribute('id',$this->getClientID()); - - // We call parent implementation here because some attributes - // may be overwritten in the following - parent::addAttributesToRender($writer); - - if($this->getEnabled(true) && $this->getEnableClientScript()) - { - $this->renderLinkButtonHref($writer); - $this->renderClientControlScript($writer); - } - else if($this->getEnabled()) // in this case, parent will not render 'disabled' - $writer->addAttribute('disabled','disabled'); - } - - /** - * Renders the client-script code. - * @param THtmlWriter renderer - */ - protected function renderClientControlScript($writer) - { - $cs = $this->getPage()->getClientScript(); - $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); - } - - /** - * @param boolean set by a panel to register this button as the default button for the panel. - */ - public function setIsDefaultButton($value) - { - $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean true if this button is registered as a default button for a panel. - */ - public function getIsDefaultButton() - { - return $this->getViewState('IsDefaultButton', false); - } - - /** - * Renders the Href for link button. - * @param THtmlWriter renderer - */ - protected function renderLinkButtonHref($writer) - { - //create unique no-op url references - $nop = "javascript:;//".$this->getClientID(); - $writer->addAttribute('href', $nop); - } - - /** - * 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.TLinkButton'; - } - - /** - * Returns postback specifications for the button. - * This method is used by framework and control developers. - * @return array parameters about how the button defines its postback behavior. - */ - protected function getPostBackOptions() - { - $options['ID'] = $this->getClientID(); - $options['EventTarget'] = $this->getUniqueID(); - $options['CausesValidation'] = $this->getCausesValidation(); - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['StopEvent'] = true; - - return $options; - } - - /** - * Renders the body content enclosed between the control tag. - * If {@link getText Text} is not empty, it will be rendered. Otherwise, - * the body content enclosed in the control tag will be rendered. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderContents($writer) - { - if(($text=$this->getText())==='') - parent::renderContents($writer); - else - $writer->write($text); - } - - /** - * @return string the text caption of the button - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * @param string the text caption to be set - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * Returns the caption of the button. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string caption of the button. - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the caption of the button. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string caption of the button - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * @return string the command name associated with the {@link onCommand OnCommand} event. - */ - public function getCommandName() - { - return $this->getViewState('CommandName',''); - } - - /** - * @param string the command name associated with the {@link onCommand OnCommand} event. - */ - public function setCommandName($value) - { - $this->setViewState('CommandName',$value,''); - } - - /** - * @return string the parameter associated with the {@link onCommand OnCommand} event - */ - public function getCommandParameter() - { - return $this->getViewState('CommandParameter',''); - } - - /** - * @param string the parameter associated with the {@link onCommand OnCommand} event. - */ - public function setCommandParameter($value) - { - $this->setViewState('CommandParameter',$value,''); - } - - /** - * @return boolean whether postback event trigger by this button will cause input validation - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * Sets the value indicating whether postback event trigger by this button will cause input validation. - * @param string the text caption to be set - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the group of validators which the button causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the button causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * Raises the postback event. - * This method is required by {@link IPostBackEventHandler} interface. - * If {@link getCausesValidation CausesValidation} is true, it will - * invoke the page's {@link TPage::validate validate} method first. - * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. - * This method is mainly used by framework and control developers. - * @param TEventParameter the event parameter - */ - public function raisePostBackEvent($param) - { - if($this->getCausesValidation()) - $this->getPage()->validate($this->getValidationGroup()); - $this->onClick(null); - $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); - } - - /** - * This method is invoked when the button is clicked. - * The method raises 'OnClick' 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 TEventParameter event parameter to be passed to the event handlers - */ - public function onClick($param) - { - $this->raiseEvent('OnClick',$this,$param); - } - - /** - * This method is invoked when the button is clicked. - * The method raises 'OnCommand' event to fire up the event handlers. - * If you override this method, be sure to call the parent implementation - * so that the event handlers can be invoked. - * @param TCommandEventParameter event parameter to be passed to the event handlers - */ - public function onCommand($param) - { - $this->raiseEvent('OnCommand',$this,$param); - $this->raiseBubbleEvent($this,$param); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TLinkButton class + * + * TLinkButton creates a hyperlink style button on the page. + * TLinkButton has the same appearance as a hyperlink. However, it is mainly + * used to submit data to a page. Like {@link TButton}, you can create either + * a submit button or a command button. + * + * A command button has a command name (specified by + * the {@link setCommandName CommandName} property) and and a command parameter + * (specified by {@link setCommandParameter CommandParameter} property) + * associated with the button. This allows you to create multiple TLinkButton + * components on a Web page and programmatically determine which one is clicked + * with what parameter. You can provide an event handler for + * {@link onCommand OnCommand} event to programmatically control the actions performed + * when the command button is clicked. In the event handler, you can determine + * the {@link setCommandName CommandName} property value and + * the {@link setCommandParameter CommandParameter} property value + * through the {@link TCommandParameter::getName Name} and + * {@link TCommandParameter::getParameter Parameter} properties of the event + * parameter which is of type {@link TCommandEventParameter}. + * + * A submit button does not have a command name associated with the button + * and clicking on it simply posts the Web page back to the server. + * By default, a TLinkButton component is a submit button. + * You can provide an event handler for the {@link onClick OnClick} event + * to programmatically control the actions performed when the submit button is clicked. + * + * Clicking on button can trigger form validation, if + * {@link setCausesValidation CausesValidation} is true. + * And the validation may be restricted within a certain group of validator + * controls by setting {@link setValidationGroup ValidationGroup} property. + * If validation is successful, the data will be post back to the same page. + * + * TLinkButton will display the {@link setText Text} property value + * as the hyperlink text. If {@link setText Text} is empty, the body content + * of TLinkButton will be displayed. Therefore, you can use TLinkButton + * as an image button by enclosing an <img> tag as the body of TLinkButton. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLinkButton extends TWebControl implements IPostBackEventHandler, IButtonControl, IDataRenderer +{ + /** + * @return string tag name of the button + */ + protected function getTagName() + { + return 'a'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional button specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + + $writer->addAttribute('id',$this->getClientID()); + + // We call parent implementation here because some attributes + // may be overwritten in the following + parent::addAttributesToRender($writer); + + if($this->getEnabled(true) && $this->getEnableClientScript()) + { + $this->renderLinkButtonHref($writer); + $this->renderClientControlScript($writer); + } + else if($this->getEnabled()) // in this case, parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + } + + /** + * Renders the client-script code. + * @param THtmlWriter renderer + */ + protected function renderClientControlScript($writer) + { + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value) + { + $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton() + { + return $this->getViewState('IsDefaultButton', false); + } + + /** + * Renders the Href for link button. + * @param THtmlWriter renderer + */ + protected function renderLinkButtonHref($writer) + { + //create unique no-op url references + $nop = "javascript:;//".$this->getClientID(); + $writer->addAttribute('href', $nop); + } + + /** + * 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.TLinkButton'; + } + + /** + * Returns postback specifications for the button. + * This method is used by framework and control developers. + * @return array parameters about how the button defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['StopEvent'] = true; + + return $options; + } + + /** + * Renders the body content enclosed between the control tag. + * If {@link getText Text} is not empty, it will be rendered. Otherwise, + * the body content enclosed in the control tag will be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string the text caption of the button + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the caption of the button. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string caption of the button. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the caption of the button. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string caption of the button + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName() + { + return $this->getViewState('CommandName',''); + } + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value) + { + $this->setViewState('CommandName',$value,''); + } + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter() + { + return $this->getViewState('CommandParameter',''); + } + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value) + { + $this->setViewState('CommandParameter',$value,''); + } + + /** + * @return boolean whether postback event trigger by this button will cause input validation + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * Sets the value indicating whether postback event trigger by this button will cause input validation. + * @param string the text caption to be set + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * If {@link getCausesValidation CausesValidation} is true, it will + * invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + if($this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onClick(null); + $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnClick' 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 TEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnCommand' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param) + { + $this->raiseEvent('OnCommand',$this,$param); + $this->raiseBubbleEvent($this,$param); + } +} + diff --git a/framework/Web/UI/WebControls/TListBox.php b/framework/Web/UI/WebControls/TListBox.php index 8e996b6e..f7ab4791 100644 --- a/framework/Web/UI/WebControls/TListBox.php +++ b/framework/Web/UI/WebControls/TListBox.php @@ -1,226 +1,226 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TListControl class - */ -Prado::using('System.Web.UI.WebControls.TListControl'); - -/** - * TListBox class - * - * TListBox displays a list box on a Web page that allows single or multiple selection. - * The list box allows multiple selections if {@link setSelectionMode SelectionMode} - * is TListSelectionMode::Multiple. It takes single selection only if Single. - * The property {@link setRows Rows} specifies how many rows of options are visible - * at a time. See {@link TListControl} for inherited properties. - * - * Since v3.0.3, TListBox starts to support optgroup. To specify an option group for - * a list item, set a Group attribute with it, - * - * $listitem->Attributes->Group="Group Name"; - * // or in template - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TListBox extends TListControl implements IPostBackDataHandler, IValidatable -{ - private $_dataChanged=false; + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TListControl class + */ +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * TListBox class + * + * TListBox displays a list box on a Web page that allows single or multiple selection. + * The list box allows multiple selections if {@link setSelectionMode SelectionMode} + * is TListSelectionMode::Multiple. It takes single selection only if Single. + * The property {@link setRows Rows} specifies how many rows of options are visible + * at a time. See {@link TListControl} for inherited properties. + * + * Since v3.0.3, TListBox starts to support optgroup. To specify an option group for + * a list item, set a Group attribute with it, + * + * $listitem->Attributes->Group="Group Name"; + * // or in template + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListBox extends TListControl implements IPostBackDataHandler, IValidatable +{ + private $_dataChanged=false; private $_isValid=true; - - /** - * Adds attribute name-value pairs to renderer. - * This method overrides the parent implementation with additional list box specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $rows=$this->getRows(); - $writer->addAttribute('size',"$rows"); - if($this->getSelectionMode()===TListSelectionMode::Multiple) - $writer->addAttribute('name',$this->getUniqueID().'[]'); - else - $writer->addAttribute('name',$this->getUniqueID()); - parent::addAttributesToRender($writer); - } - - /** - * 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.TListBox'; - } - - /** - * Registers the list control to load post data on postback. - * This method overrides the parent implementation. - * @param mixed event parameter - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if($this->getEnabled(true)) - $this->getPage()->registerRequiresPostData($this); - } - - /** - * Loads user input data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - if(!$this->getEnabled(true)) - return false; - $this->ensureDataBound(); - $selections=isset($values[$key])?$values[$key]:null; - if($selections!==null) - { - $items=$this->getItems(); - if($this->getSelectionMode()===TListSelectionMode::Single) - { - $selection=is_array($selections)?$selections[0]:$selections; - $index=$items->findIndexByValue($selection,false); - if($this->getSelectedIndex()!==$index) - { - $this->setSelectedIndex($index); - return $this->_dataChanged=true; - } - else - return false; - } - if(!is_array($selections)) - $selections=array($selections); - $list=array(); - foreach($selections as $selection) - $list[]=$items->findIndexByValue($selection,false); - $list2=$this->getSelectedIndices(); - $n=count($list); - $flag=false; - if($n===count($list2)) - { - sort($list,SORT_NUMERIC); - for($i=0;$i<$n;++$i) - { - if($list[$i]!==$list2[$i]) - { - $flag=true; - break; - } - } - } - else - $flag=true; - if($flag) - { - $this->setSelectedIndices($list); - $this->_dataChanged=true; - } - return $flag; - } - else if($this->getSelectedIndex()!==-1) - { - $this->clearSelection(); - return $this->_dataChanged=true; - } - else - return false; - } - - /** - * Raises postdata changed event. - * This method is required by {@link IPostBackDataHandler} interface. - * It is invoked by the framework when {@link getSelectedIndices SelectedIndices} property - * is changed on postback. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - if($this->getAutoPostBack() && $this->getCausesValidation()) - $this->getPage()->validate($this->getValidationGroup()); - $this->onSelectedIndexChanged(null); - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * @return boolean whether this control allows multiple selection - */ - protected function getIsMultiSelect() - { - return $this->getSelectionMode()===TListSelectionMode::Multiple; - } - - /** - * @return integer the number of rows to be displayed in the list control - */ - public function getRows() - { - return $this->getViewState('Rows', 4); - } - - /** - * @param integer the number of rows to be displayed in the list control - */ - public function setRows($value) - { - $value=TPropertyValue::ensureInteger($value); - if($value<=0) - $value=4; - $this->setViewState('Rows', $value, 4); - } - - /** - * @return TListSelectionMode the selection mode (Single, Multiple). Defaults to TListSelectionMode::Single. - */ - public function getSelectionMode() - { - return $this->getViewState('SelectionMode', TListSelectionMode::Single); - } - - /** - * @param TListSelectionMode the selection mode - */ - public function setSelectionMode($value) - { - $this->setViewState('SelectionMode',TPropertyValue::ensureEnum($value,'TListSelectionMode'),TListSelectionMode::Single); - } - - /** - * Returns the value to be validated. - * This methid is required by IValidatable interface. - * @return mixed the value of the property to be validated. - */ - public function getValidationPropertyValue() - { - return $this->getSelectedValue(); - } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional list box specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $rows=$this->getRows(); + $writer->addAttribute('size',"$rows"); + if($this->getSelectionMode()===TListSelectionMode::Multiple) + $writer->addAttribute('name',$this->getUniqueID().'[]'); + else + $writer->addAttribute('name',$this->getUniqueID()); + parent::addAttributesToRender($writer); + } + + /** + * 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.TListBox'; + } + + /** + * Registers the list control to load post data on postback. + * This method overrides the parent implementation. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getEnabled(true)) + $this->getPage()->registerRequiresPostData($this); + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if(!$this->getEnabled(true)) + return false; + $this->ensureDataBound(); + $selections=isset($values[$key])?$values[$key]:null; + if($selections!==null) + { + $items=$this->getItems(); + if($this->getSelectionMode()===TListSelectionMode::Single) + { + $selection=is_array($selections)?$selections[0]:$selections; + $index=$items->findIndexByValue($selection,false); + if($this->getSelectedIndex()!==$index) + { + $this->setSelectedIndex($index); + return $this->_dataChanged=true; + } + else + return false; + } + if(!is_array($selections)) + $selections=array($selections); + $list=array(); + foreach($selections as $selection) + $list[]=$items->findIndexByValue($selection,false); + $list2=$this->getSelectedIndices(); + $n=count($list); + $flag=false; + if($n===count($list2)) + { + sort($list,SORT_NUMERIC); + for($i=0;$i<$n;++$i) + { + if($list[$i]!==$list2[$i]) + { + $flag=true; + break; + } + } + } + else + $flag=true; + if($flag) + { + $this->setSelectedIndices($list); + $this->_dataChanged=true; + } + return $flag; + } + else if($this->getSelectedIndex()!==-1) + { + $this->clearSelection(); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getSelectedIndices SelectedIndices} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onSelectedIndexChanged(null); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @return boolean whether this control allows multiple selection + */ + protected function getIsMultiSelect() + { + return $this->getSelectionMode()===TListSelectionMode::Multiple; + } + + /** + * @return integer the number of rows to be displayed in the list control + */ + public function getRows() + { + return $this->getViewState('Rows', 4); + } + + /** + * @param integer the number of rows to be displayed in the list control + */ + public function setRows($value) + { + $value=TPropertyValue::ensureInteger($value); + if($value<=0) + $value=4; + $this->setViewState('Rows', $value, 4); + } + + /** + * @return TListSelectionMode the selection mode (Single, Multiple). Defaults to TListSelectionMode::Single. + */ + public function getSelectionMode() + { + return $this->getViewState('SelectionMode', TListSelectionMode::Single); + } + + /** + * @param TListSelectionMode the selection mode + */ + public function setSelectionMode($value) + { + $this->setViewState('SelectionMode',TPropertyValue::ensureEnum($value,'TListSelectionMode'),TListSelectionMode::Single); + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getSelectedValue(); + } /** * Returns true if this control validated successfully. @@ -238,25 +238,25 @@ class TListBox extends TListControl implements IPostBackDataHandler, IValidatabl { $this->_isValid=TPropertyValue::ensureBoolean($value); } -} - - -/** - * TListSelectionMode class. - * TListSelectionMode defines the enumerable type for the possible selection modes of a {@link TListBox}. - * - * The following enumerable values are defined: - * - Single: single selection - * - Multiple: allow multiple selection - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TListSelectionMode extends TEnumerable -{ - const Single='Single'; - const Multiple='Multiple'; -} - +} + + +/** + * TListSelectionMode class. + * TListSelectionMode defines the enumerable type for the possible selection modes of a {@link TListBox}. + * + * The following enumerable values are defined: + * - Single: single selection + * - Multiple: allow multiple selection + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TListSelectionMode extends TEnumerable +{ + const Single='Single'; + const Multiple='Multiple'; +} + diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index 1a07a292..4d388d45 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -1,923 +1,923 @@ - - * @author Qiang Xue - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes the supporting classes - */ -Prado::using('System.Web.UI.WebControls.TDataBoundControl'); -Prado::using('System.Web.UI.WebControls.TListItem'); -Prado::using('System.Collections.TListItemCollection'); -Prado::using('System.Collections.TAttributeCollection'); -Prado::using('System.Util.TDataFieldAccessor'); - -/** - * TListControl class - * - * TListControl is a base class for list controls, such as {@link TListBox}, - * {@link TDropDownList}, {@link TCheckBoxList}, etc. - * It manages the items and their status in a list control. - * It also implements how the items can be populated from template and - * data source. - * - * The property {@link getItems} returns a list of the items in the control. - * To specify or determine which item is selected, use the - * {@link getSelectedIndex SelectedIndex} property that indicates the zero-based - * index of the selected item in the item list. You may also use - * {@link getSelectedItem SelectedItem} and {@link getSelectedValue SelectedValue} - * to get the selected item and its value. For multiple selection lists - * (such as {@link TCheckBoxList} and {@link TListBox}), property - * {@link getSelectedIndices SelectedIndices} is useful. - * - * TListControl implements {@link setAutoPostBack AutoPostBack} which allows - * a list control to postback the page if the selections of the list items are changed. - * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} - * properties may be used to specify that validation be performed when auto postback occurs. - * - * There are three ways to populate the items in a list control: from template, - * using {@link setDataSource DataSource} and using {@link setDataSourceID DataSourceID}. - * The latter two are covered in {@link TDataBoundControl}. To specify items via - * template, using the following template syntax: - * - * - * - * - * - * - * - * - * When {@link setDataSource DataSource} or {@link setDataSourceID DataSourceID} - * is used to populate list items, the {@link setDataTextField DataTextField} and - * {@link setDataValueField DataValueField} properties are used to specify which - * columns of the data will be used to populate the text and value of the items. - * For example, if a data source is as follows, - * - * $dataSource=array( - * array('name'=>'John', 'age'=>31), - * array('name'=>'Cary', 'age'=>28), - * array('name'=>'Rose', 'age'=>35), - * ); - * - * setting {@link setDataTextField DataTextField} and {@link setDataValueField DataValueField} - * to 'name' and 'age' will make the first item's text be 'John', value be 31, - * the second item's text be 'Cary', value be 28, and so on. - * The {@link setDataTextFormatString DataTextFormatString} property may be further - * used to format how the item should be displayed. See {@link formatDataValue()} - * for an explanation of the format string. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TListControl extends TDataBoundControl implements IDataRenderer -{ - /** - * @var TListItemCollection item list - */ - private $_items=null; - /** - * @var boolean whether items are restored from viewstate - */ - private $_stateLoaded=false; - /** - * @var mixed the following selection variables are used - * to keep selections when Items are not available - */ - private $_cachedSelectedIndex=-1; - private $_cachedSelectedValue=null; - private $_cachedSelectedIndices=null; - private $_cachedSelectedValues=null; - - /** - * @return string tag name of the list control - */ - protected function getTagName() - { - return 'select'; - } - - /** - * @return boolean whether to render javascript. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether to render javascript. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - $page=$this->getPage(); - $page->ensureRenderInForm($this); - if($this->getIsMultiSelect()) - $writer->addAttribute('multiple','multiple'); - if($this->getEnabled(true)) - { - if($this->getAutoPostBack() - && $this->getEnableClientScript() - && $page->getClientSupportsJavaScript()) - { - $this->renderClientControlScript($writer); - } - } - else if($this->getEnabled()) - $writer->addAttribute('disabled','disabled'); - parent::addAttributesToRender($writer); - } - - /** - * Renders the javascript for list control. - */ - protected function renderClientControlScript($writer) - { - $writer->addAttribute('id',$this->getClientID()); - $this->getPage()->getClientScript()->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); - } - - /** - * Gets the name of the javascript class responsible for performing postback for this control. - * Derived classes may override this method and return customized js class names. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TListControl'; - } - - /** - * @return array postback options for JS postback code - */ - protected function getPostBackOptions() - { - $options['ID'] = $this->getClientID(); - $options['CausesValidation'] = $this->getCausesValidation(); - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['EventTarget'] = $this->getUniqueID(); - return $options; - } - - /** - * Adds object parsed from template to the control. - * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. - * All other objects are ignored. - * @param mixed object parsed from template - */ - public function addParsedObject($object) - { - // Do not add items from template if items are loaded from viewstate - if(!$this->_stateLoaded && ($object instanceof TListItem)) - { - $index=$this->getItems()->add($object); - if(($this->_cachedSelectedValue!==null && $this->_cachedSelectedValue===$object->getValue()) || ($this->_cachedSelectedIndex===$index)) - { - $object->setSelected(true); - $this->_cachedSelectedValue=null; - $this->_cachedSelectedIndex=-1; - } - } - } - - /** - * Performs databinding to populate list items from data source. - * This method is invoked by dataBind(). - * You may override this function to provide your own way of data population. - * @param Traversable the data - */ - protected function performDataBinding($data) - { - $items=$this->getItems(); - if(!$this->getAppendDataBoundItems()) - $items->clear(); - $textField=$this->getDataTextField(); - if($textField==='') - $textField=0; - $valueField=$this->getDataValueField(); - if($valueField==='') - $valueField=1; - $textFormat=$this->getDataTextFormatString(); - $groupField=$this->getDataGroupField(); - foreach($data as $key=>$object) - { - $item=$items->createListItem(); - if(is_array($object) || is_object($object)) - { - $text=TDataFieldAccessor::getDataFieldValue($object,$textField); - $value=TDataFieldAccessor::getDataFieldValue($object,$valueField); - $item->setValue($value); - if($groupField!=='') - $item->setAttribute('Group',TDataFieldAccessor::getDataFieldValue($object,$groupField)); - } - else - { - $text=$object; - $item->setValue("$key"); - } - $item->setText($this->formatDataValue($textFormat,$text)); - } - // SelectedValue or SelectedIndex may be set before databinding - // so we make them be effective now - if($this->_cachedSelectedValue!==null) - { - $this->setSelectedValue($this->_cachedSelectedValue); - $this->resetCachedSelections(); - } - else if($this->_cachedSelectedIndex!==-1) - { - $this->setSelectedIndex($this->_cachedSelectedIndex); - $this->resetCachedSelections(); - } - else if($this->_cachedSelectedValues!==null) - { - $this->setSelectedValues($this->_cachedSelectedValues); - $this->resetCachedSelections(); - } - else if($this->_cachedSelectedIndices!==null) - { - $this->setSelectedIndices($this->_cachedSelectedIndices); - $this->resetCachedSelections(); - } - } - - private function resetCachedSelections() - { - $this->_cachedSelectedValue=null; - $this->_cachedSelectedIndex=-1; - $this->_cachedSelectedValues=null; - $this->_cachedSelectedIndices=null; - } - - /** - * Creates a collection object to hold list items. - * This method may be overriden to create a customized collection. - * @return TListItemCollection the collection object - */ - protected function createListItemCollection() - { - return new TListItemCollection; - } - - /** - * Saves items into viewstate. - * This method is invoked right before control state is to be saved. - */ - public function saveState() - { - parent::saveState(); - if($this->_items) - $this->setViewState('Items',$this->_items->saveState(),null); - else - $this->clearViewState('Items'); - } - - /** - * Loads items from viewstate. - * This method is invoked right after control state is loaded. - */ - public function loadState() - { - parent::loadState(); - $this->_stateLoaded=true; - if(!$this->getIsDataBound()) - { - $this->_items=$this->createListItemCollection(); - $this->_items->loadState($this->getViewState('Items',null)); - } - $this->clearViewState('Items'); - } - - /** - * @return boolean whether this is a multiselect control. Defaults to false. - */ - protected function getIsMultiSelect() - { - return false; - } - - /** - * @return boolean whether performing databind should append items or clear the existing ones. Defaults to false. - */ - public function getAppendDataBoundItems() - { - return $this->getViewState('AppendDataBoundItems',false); - } - - /** - * @param boolean whether performing databind should append items or clear the existing ones. - */ - public function setAppendDataBoundItems($value) - { - $this->setViewState('AppendDataBoundItems',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean a value indicating whether an automatic postback to the server - * will occur whenever the user makes change to the list control and then tabs out of it. - * Defaults to false. - */ - public function getAutoPostBack() - { - return $this->getViewState('AutoPostBack',false); - } - - /** - * Sets the value indicating if postback automatically. - * An automatic postback to the server will occur whenever the user - * makes change to the list control and then tabs out of it. - * @param boolean the value indicating if postback automatically - */ - public function setAutoPostBack($value) - { - $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether postback event trigger by this list control will cause input validation, default is true. - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * @param boolean whether postback event trigger by this list control will cause input validation. - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the field of the data source that provides the text content of the list items. - */ - public function getDataTextField() - { - return $this->getViewState('DataTextField',''); - } - - /** - * @param string the field of the data source that provides the text content of the list items. - */ - public function setDataTextField($value) - { - $this->setViewState('DataTextField',$value,''); - } - - /** - * @return string the formatting string used to control how data bound to the list control is displayed. - */ - public function getDataTextFormatString() - { - return $this->getViewState('DataTextFormatString',''); - } - - /** - * Sets data text format string. - * The format string is used in {@link TDataValueFormatter::format()} to format the Text property value - * of each item in the list control. - * @param string the formatting string used to control how data bound to the list control is displayed. - * @see TDataValueFormatter::format() - */ - public function setDataTextFormatString($value) - { - $this->setViewState('DataTextFormatString',$value,''); - } - - /** - * @return string the field of the data source that provides the value of each list item. - */ - public function getDataValueField() - { - return $this->getViewState('DataValueField',''); - } - - /** - * @param string the field of the data source that provides the value of each list item. - */ - public function setDataValueField($value) - { - $this->setViewState('DataValueField',$value,''); - } - - /** - * @return string the field of the data source that provides the label of the list item groups - */ - public function getDataGroupField() - { - return $this->getViewState('DataGroupField',''); - } - - /** - * @param string the field of the data source that provides the label of the list item groups - */ - public function setDataGroupField($value) - { - $this->setViewState('DataGroupField',$value,''); - } - - /** - * @return integer the number of items in the list control - */ - public function getItemCount() - { - return $this->_items?$this->_items->getCount():0; - } - - /** - * @return boolean whether the list control contains any items. - */ - public function getHasItems() - { - return ($this->_items && $this->_items->getCount()>0); - } - - /** - * @return TListItemCollection the item collection - */ - public function getItems() - { - if(!$this->_items) - $this->_items=$this->createListItemCollection(); - return $this->_items; - } - - /** - * @return integer the index (zero-based) of the item being selected, -1 if no item is selected. - */ - public function getSelectedIndex() - { - if($this->_items) - { - $n=$this->_items->getCount(); - for($i=0;$i<$n;++$i) - if($this->_items->itemAt($i)->getSelected()) - return $i; - } - return -1; - } - - /** - * @param integer the index (zero-based) of the item to be selected - */ - public function setSelectedIndex($index) - { - if(($index=TPropertyValue::ensureInteger($index))<0) - $index=-1; - if($this->_items) - { - $this->clearSelection(); - if($index>=0 && $index<$this->_items->getCount()) - $this->_items->itemAt($index)->setSelected(true); - } - $this->_cachedSelectedIndex=$index; - if($this->getAdapter() instanceof IListControlAdapter) - $this->getAdapter()->setSelectedIndex($index); - } - - /** - * @return array list of index of items that are selected - */ - public function getSelectedIndices() - { - $selections=array(); - if($this->_items) - { - $n=$this->_items->getCount(); - for($i=0;$i<$n;++$i) - if($this->_items->itemAt($i)->getSelected()) - $selections[]=$i; - } - return $selections; - } - - /** - * @param array list of index of items to be selected - */ - public function setSelectedIndices($indices) - { - if($this->getIsMultiSelect()) - { - if($this->_items) - { - $this->clearSelection(); - $n=$this->_items->getCount(); - foreach($indices as $index) - { - if($index>=0 && $index<$n) - $this->_items->itemAt($index)->setSelected(true); - } - } - $this->_cachedSelectedIndices=$indices; - } - else - throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); - - if($this->getAdapter() instanceof IListControlAdapter) - $this->getAdapter()->setSelectedIndices($indices); - } - - /** - * @return TListItem|null the selected item with the lowest cardinal index, null if no item is selected. - */ - public function getSelectedItem() - { - if(($index=$this->getSelectedIndex())>=0) - return $this->_items->itemAt($index); - else - return null; - } - - /** - * Returns the value of the selected item with the lowest cardinal index. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getSelectedValue()}. - * @return string the value of the selected item with the lowest cardinal index, empty if no selection. - * @see getSelectedValue - * @since 3.1.0 - */ - public function getData() - { - return $this->getSelectedValue(); - } - - /** - * Selects an item by the specified value. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setSelectedValue()}. - * @param string the value of the item to be selected. - * @see setSelectedValue - * @since 3.1.0 - */ - public function setData($value) - { - $this->setSelectedValue($value); - } - - /** - * @return string the value of the selected item with the lowest cardinal index, empty if no selection - */ - public function getSelectedValue() - { - $index=$this->getSelectedIndex(); - return $index>=0?$this->getItems()->itemAt($index)->getValue():''; - } - - /** - * Sets selection by item value. - * Existing selections will be cleared if the item value is found in the item collection. - * Note, if the value is null, existing selections will also be cleared. - * @param string the value of the item to be selected. - */ - public function setSelectedValue($value) - { - if($this->_items) - { - if($value===null) - $this->clearSelection(); - else if(($item=$this->_items->findItemByValue($value))!==null) - { - $this->clearSelection(); - $item->setSelected(true); - } - else - $this->clearSelection(); - } - $this->_cachedSelectedValue=$value; - if($this->getAdapter() instanceof IListControlAdapter) - $this->getAdapter()->setSelectedValue($value); - } - - - /** - * @return array list of the selected item values (strings) - */ - public function getSelectedValues() - { - $values=array(); - if($this->_items) - { - foreach($this->_items as $item) - { - if($item->getSelected()) - $values[]=$item->getValue(); - } - } - return $values; - } - - /** - * @param array list of the selected item values - */ - public function setSelectedValues($values) - { - if($this->getIsMultiSelect()) - { - if($this->_items) - { - $this->clearSelection(); - $lookup=array(); - foreach($this->_items as $item) - $lookup[$item->getValue()]=$item; - foreach($values as $value) - { - if(isset($lookup["$value"])) - $lookup["$value"]->setSelected(true); - } - } - $this->_cachedSelectedValues=$values; - } - else - throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); - - if($this->getAdapter() instanceof IListControlAdapter) - $this->getAdapter()->setSelectedValues($values); - } - - /** - * @return string selected value - */ - public function getText() - { - return $this->getSelectedValue(); - } - - /** - * @param string value to be selected - */ - public function setText($value) - { - $this->setSelectedValue($value); - } - - /** - * Clears all existing selections. - */ - public function clearSelection() - { - if($this->_items) - { - foreach($this->_items as $item) - $item->setSelected(false); - } - - if($this->getAdapter() instanceof IListControlAdapter) - $this->getAdapter()->clearSelection(); - } - - /** - * @return string the group of validators which the list control causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the list control causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * @return string the prompt text which is to be displayed as the first list item. - * @since 3.1.1 - */ - public function getPromptText() - { - return $this->getViewState('PromptText',''); - } - - /** - * @param string the prompt text which is to be displayed as the first list item. - * @since 3.1.1 - */ - public function setPromptText($value) - { - $this->setViewState('PromptText',$value,''); - } - - /** - * @return string the prompt selection value. - * @see getPromptText - * @since 3.1.1 - */ - public function getPromptValue() - { - return $this->getViewState('PromptValue',''); - } - - /** - * @param string the prompt selection value. If empty, {@link getPromptText PromptText} will be used as the value. - * @see setPromptText - * @since 3.1.1 - */ - public function setPromptValue($value) - { - $this->setViewState('PromptValue',(string)$value,''); - } - - /** - * Raises OnSelectedIndexChanged event when selection is changed. - * This method is invoked when the list control has its selection changed - * by end-users. - * @param TEventParameter event parameter - */ - public function onSelectedIndexChanged($param) - { - $this->raiseEvent('OnSelectedIndexChanged',$this,$param); - $this->onTextChanged($param); - } - - /** - * Raises OnTextChanged event when selection is changed. - * This method is invoked when the list control has its selection changed - * by end-users. - * @param TEventParameter event parameter - */ - public function onTextChanged($param) - { - $this->raiseEvent('OnTextChanged',$this,$param); - } - - /** - * Renders the prompt text, if any. - * @param THtmlWriter writer - * @since 3.1.1 - */ - protected function renderPrompt($writer) - { - $text=$this->getPromptText(); - $value=$this->getPromptValue(); - if($value==='') - $value=$text; - if($value!=='') - { - $writer->addAttribute('value',$value); - $writer->renderBeginTag('option'); - $writer->write(THttpUtility::htmlEncode($text)); - $writer->renderEndTag(); - $writer->writeLine(); - } - } - - /** - * Renders body content of the list control. - * This method renders items contained in the list control as the body content. - * @param THtmlWriter writer - */ - public function renderContents($writer) - { - $this->renderPrompt($writer); - - if($this->_items) - { - $writer->writeLine(); - $previousGroup=null; - foreach($this->_items as $item) - { - if($item->getEnabled()) - { - if($item->getHasAttributes()) - { - $group=$item->getAttributes()->remove('Group'); - if($group!==$previousGroup) - { - if($previousGroup!==null) - { - $writer->renderEndTag(); - $writer->writeLine(); - $previousGroup=null; - } - if($group!==null) - { - $writer->addAttribute('label',$group); - $writer->renderBeginTag('optgroup'); - $writer->writeLine(); - $previousGroup=$group; - } - } - foreach($item->getAttributes() as $name=>$value) - $writer->addAttribute($name,$value); - } - else if($previousGroup!==null) - { - $writer->renderEndTag(); - $writer->writeLine(); - $previousGroup=null; - } - if($item->getSelected()) - $writer->addAttribute('selected','selected'); - $writer->addAttribute('value',$item->getValue()); - $writer->renderBeginTag('option'); - $writer->write(THttpUtility::htmlEncode($item->getText())); - $writer->renderEndTag(); - $writer->writeLine(); - } - } - if($previousGroup!==null) - { - $writer->renderEndTag(); - $writer->writeLine(); - } - } - } - - /** - * Formats the text value according to a format string. - * If the format string is empty, the original value is converted into - * a string and returned. - * If the format string starts with '#', the string is treated as a PHP expression - * within which the token '{0}' is translated with the data value to be formated. - * Otherwise, the format string and the data value are passed - * as the first and second parameters in {@link sprintf}. - * @param string format string - * @param mixed the data to be formatted - * @return string the formatted result - */ - protected function formatDataValue($formatString,$value) - { - if($formatString==='') - return TPropertyValue::ensureString($value); - else if($formatString[0]==='#') - { - $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); - try - { - if(eval("\$result=$expression;")===false) - throw new Exception(''); - return $result; - } - catch(Exception $e) - { - throw new TInvalidDataValueException('listcontrol_expression_invalid',get_class($this),$expression,$e->getMessage()); - } - } - else - return sprintf($formatString,$value); - } -} - -/** - * IListControlAdapter interface - * - * @author Wei Zhuo - * @version $Revision: $ Sun Jun 25 04:53:43 EST 2006 $ - * @package System.Web.UI.ActiveControls - * @since 3.0 - */ -interface IListControlAdapter -{ - /** - * Selects an item based on zero-base index on the client side. - * @param integer the index (zero-based) of the item to be selected - */ - public function setSelectedIndex($index); - /** - * Selects a list of item based on zero-base indices on the client side. - * @param array list of index of items to be selected - */ - public function setSelectedIndices($indices); - - /** - * Sets selection by item value on the client side. - * @param string the value of the item to be selected. - */ - public function setSelectedValue($value); - - /** - * Sets selection by a list of item values on the client side. - * @param array list of the selected item values - */ - public function setSelectedValues($values); - - /** - * Clears all existing selections on the client side. - */ - public function clearSelection(); -} - - -?> + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes the supporting classes + */ +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Web.UI.WebControls.TListItem'); +Prado::using('System.Collections.TListItemCollection'); +Prado::using('System.Collections.TAttributeCollection'); +Prado::using('System.Util.TDataFieldAccessor'); + +/** + * TListControl class + * + * TListControl is a base class for list controls, such as {@link TListBox}, + * {@link TDropDownList}, {@link TCheckBoxList}, etc. + * It manages the items and their status in a list control. + * It also implements how the items can be populated from template and + * data source. + * + * The property {@link getItems} returns a list of the items in the control. + * To specify or determine which item is selected, use the + * {@link getSelectedIndex SelectedIndex} property that indicates the zero-based + * index of the selected item in the item list. You may also use + * {@link getSelectedItem SelectedItem} and {@link getSelectedValue SelectedValue} + * to get the selected item and its value. For multiple selection lists + * (such as {@link TCheckBoxList} and {@link TListBox}), property + * {@link getSelectedIndices SelectedIndices} is useful. + * + * TListControl implements {@link setAutoPostBack AutoPostBack} which allows + * a list control to postback the page if the selections of the list items are changed. + * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} + * properties may be used to specify that validation be performed when auto postback occurs. + * + * There are three ways to populate the items in a list control: from template, + * using {@link setDataSource DataSource} and using {@link setDataSourceID DataSourceID}. + * The latter two are covered in {@link TDataBoundControl}. To specify items via + * template, using the following template syntax: + * + * + * + * + * + * + * + * + * When {@link setDataSource DataSource} or {@link setDataSourceID DataSourceID} + * is used to populate list items, the {@link setDataTextField DataTextField} and + * {@link setDataValueField DataValueField} properties are used to specify which + * columns of the data will be used to populate the text and value of the items. + * For example, if a data source is as follows, + * + * $dataSource=array( + * array('name'=>'John', 'age'=>31), + * array('name'=>'Cary', 'age'=>28), + * array('name'=>'Rose', 'age'=>35), + * ); + * + * setting {@link setDataTextField DataTextField} and {@link setDataValueField DataValueField} + * to 'name' and 'age' will make the first item's text be 'John', value be 31, + * the second item's text be 'Cary', value be 28, and so on. + * The {@link setDataTextFormatString DataTextFormatString} property may be further + * used to format how the item should be displayed. See {@link formatDataValue()} + * for an explanation of the format string. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TListControl extends TDataBoundControl implements IDataRenderer +{ + /** + * @var TListItemCollection item list + */ + private $_items=null; + /** + * @var boolean whether items are restored from viewstate + */ + private $_stateLoaded=false; + /** + * @var mixed the following selection variables are used + * to keep selections when Items are not available + */ + private $_cachedSelectedIndex=-1; + private $_cachedSelectedValue=null; + private $_cachedSelectedIndices=null; + private $_cachedSelectedValues=null; + + /** + * @return string tag name of the list control + */ + protected function getTagName() + { + return 'select'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + if($this->getIsMultiSelect()) + $writer->addAttribute('multiple','multiple'); + if($this->getEnabled(true)) + { + if($this->getAutoPostBack() + && $this->getEnableClientScript() + && $page->getClientSupportsJavaScript()) + { + $this->renderClientControlScript($writer); + } + } + else if($this->getEnabled()) + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + } + + /** + * Renders the javascript for list control. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $this->getPage()->getClientScript()->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * Derived classes may override this method and return customized js class names. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TListControl'; + } + + /** + * @return array postback options for JS postback code + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + // Do not add items from template if items are loaded from viewstate + if(!$this->_stateLoaded && ($object instanceof TListItem)) + { + $index=$this->getItems()->add($object); + if(($this->_cachedSelectedValue!==null && $this->_cachedSelectedValue===$object->getValue()) || ($this->_cachedSelectedIndex===$index)) + { + $object->setSelected(true); + $this->_cachedSelectedValue=null; + $this->_cachedSelectedIndex=-1; + } + } + } + + /** + * Performs databinding to populate list items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $items=$this->getItems(); + if(!$this->getAppendDataBoundItems()) + $items->clear(); + $textField=$this->getDataTextField(); + if($textField==='') + $textField=0; + $valueField=$this->getDataValueField(); + if($valueField==='') + $valueField=1; + $textFormat=$this->getDataTextFormatString(); + $groupField=$this->getDataGroupField(); + foreach($data as $key=>$object) + { + $item=$items->createListItem(); + if(is_array($object) || is_object($object)) + { + $text=TDataFieldAccessor::getDataFieldValue($object,$textField); + $value=TDataFieldAccessor::getDataFieldValue($object,$valueField); + $item->setValue($value); + if($groupField!=='') + $item->setAttribute('Group',TDataFieldAccessor::getDataFieldValue($object,$groupField)); + } + else + { + $text=$object; + $item->setValue("$key"); + } + $item->setText($this->formatDataValue($textFormat,$text)); + } + // SelectedValue or SelectedIndex may be set before databinding + // so we make them be effective now + if($this->_cachedSelectedValue!==null) + { + $this->setSelectedValue($this->_cachedSelectedValue); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedIndex!==-1) + { + $this->setSelectedIndex($this->_cachedSelectedIndex); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedValues!==null) + { + $this->setSelectedValues($this->_cachedSelectedValues); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedIndices!==null) + { + $this->setSelectedIndices($this->_cachedSelectedIndices); + $this->resetCachedSelections(); + } + } + + private function resetCachedSelections() + { + $this->_cachedSelectedValue=null; + $this->_cachedSelectedIndex=-1; + $this->_cachedSelectedValues=null; + $this->_cachedSelectedIndices=null; + } + + /** + * Creates a collection object to hold list items. + * This method may be overriden to create a customized collection. + * @return TListItemCollection the collection object + */ + protected function createListItemCollection() + { + return new TListItemCollection; + } + + /** + * Saves items into viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('Items',$this->_items->saveState(),null); + else + $this->clearViewState('Items'); + } + + /** + * Loads items from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + $this->_stateLoaded=true; + if(!$this->getIsDataBound()) + { + $this->_items=$this->createListItemCollection(); + $this->_items->loadState($this->getViewState('Items',null)); + } + $this->clearViewState('Items'); + } + + /** + * @return boolean whether this is a multiselect control. Defaults to false. + */ + protected function getIsMultiSelect() + { + return false; + } + + /** + * @return boolean whether performing databind should append items or clear the existing ones. Defaults to false. + */ + public function getAppendDataBoundItems() + { + return $this->getViewState('AppendDataBoundItems',false); + } + + /** + * @param boolean whether performing databind should append items or clear the existing ones. + */ + public function setAppendDataBoundItems($value) + { + $this->setViewState('AppendDataBoundItems',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user makes change to the list control and then tabs out of it. + * Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * makes change to the list control and then tabs out of it. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether postback event trigger by this list control will cause input validation, default is true. + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this list control will cause input validation. + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the field of the data source that provides the text content of the list items. + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * @param string the field of the data source that provides the text content of the list items. + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how data bound to the list control is displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * Sets data text format string. + * The format string is used in {@link TDataValueFormatter::format()} to format the Text property value + * of each item in the list control. + * @param string the formatting string used to control how data bound to the list control is displayed. + * @see TDataValueFormatter::format() + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string the field of the data source that provides the value of each list item. + */ + public function getDataValueField() + { + return $this->getViewState('DataValueField',''); + } + + /** + * @param string the field of the data source that provides the value of each list item. + */ + public function setDataValueField($value) + { + $this->setViewState('DataValueField',$value,''); + } + + /** + * @return string the field of the data source that provides the label of the list item groups + */ + public function getDataGroupField() + { + return $this->getViewState('DataGroupField',''); + } + + /** + * @param string the field of the data source that provides the label of the list item groups + */ + public function setDataGroupField($value) + { + $this->setViewState('DataGroupField',$value,''); + } + + /** + * @return integer the number of items in the list control + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * @return boolean whether the list control contains any items. + */ + public function getHasItems() + { + return ($this->_items && $this->_items->getCount()>0); + } + + /** + * @return TListItemCollection the item collection + */ + public function getItems() + { + if(!$this->_items) + $this->_items=$this->createListItemCollection(); + return $this->_items; + } + + /** + * @return integer the index (zero-based) of the item being selected, -1 if no item is selected. + */ + public function getSelectedIndex() + { + if($this->_items) + { + $n=$this->_items->getCount(); + for($i=0;$i<$n;++$i) + if($this->_items->itemAt($i)->getSelected()) + return $i; + } + return -1; + } + + /** + * @param integer the index (zero-based) of the item to be selected + */ + public function setSelectedIndex($index) + { + if(($index=TPropertyValue::ensureInteger($index))<0) + $index=-1; + if($this->_items) + { + $this->clearSelection(); + if($index>=0 && $index<$this->_items->getCount()) + $this->_items->itemAt($index)->setSelected(true); + } + $this->_cachedSelectedIndex=$index; + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedIndex($index); + } + + /** + * @return array list of index of items that are selected + */ + public function getSelectedIndices() + { + $selections=array(); + if($this->_items) + { + $n=$this->_items->getCount(); + for($i=0;$i<$n;++$i) + if($this->_items->itemAt($i)->getSelected()) + $selections[]=$i; + } + return $selections; + } + + /** + * @param array list of index of items to be selected + */ + public function setSelectedIndices($indices) + { + if($this->getIsMultiSelect()) + { + if($this->_items) + { + $this->clearSelection(); + $n=$this->_items->getCount(); + foreach($indices as $index) + { + if($index>=0 && $index<$n) + $this->_items->itemAt($index)->setSelected(true); + } + } + $this->_cachedSelectedIndices=$indices; + } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedIndices($indices); + } + + /** + * @return TListItem|null the selected item with the lowest cardinal index, null if no item is selected. + */ + public function getSelectedItem() + { + if(($index=$this->getSelectedIndex())>=0) + return $this->_items->itemAt($index); + else + return null; + } + + /** + * Returns the value of the selected item with the lowest cardinal index. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getSelectedValue()}. + * @return string the value of the selected item with the lowest cardinal index, empty if no selection. + * @see getSelectedValue + * @since 3.1.0 + */ + public function getData() + { + return $this->getSelectedValue(); + } + + /** + * Selects an item by the specified value. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setSelectedValue()}. + * @param string the value of the item to be selected. + * @see setSelectedValue + * @since 3.1.0 + */ + public function setData($value) + { + $this->setSelectedValue($value); + } + + /** + * @return string the value of the selected item with the lowest cardinal index, empty if no selection + */ + public function getSelectedValue() + { + $index=$this->getSelectedIndex(); + return $index>=0?$this->getItems()->itemAt($index)->getValue():''; + } + + /** + * Sets selection by item value. + * Existing selections will be cleared if the item value is found in the item collection. + * Note, if the value is null, existing selections will also be cleared. + * @param string the value of the item to be selected. + */ + public function setSelectedValue($value) + { + if($this->_items) + { + if($value===null) + $this->clearSelection(); + else if(($item=$this->_items->findItemByValue($value))!==null) + { + $this->clearSelection(); + $item->setSelected(true); + } + else + $this->clearSelection(); + } + $this->_cachedSelectedValue=$value; + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedValue($value); + } + + + /** + * @return array list of the selected item values (strings) + */ + public function getSelectedValues() + { + $values=array(); + if($this->_items) + { + foreach($this->_items as $item) + { + if($item->getSelected()) + $values[]=$item->getValue(); + } + } + return $values; + } + + /** + * @param array list of the selected item values + */ + public function setSelectedValues($values) + { + if($this->getIsMultiSelect()) + { + if($this->_items) + { + $this->clearSelection(); + $lookup=array(); + foreach($this->_items as $item) + $lookup[$item->getValue()]=$item; + foreach($values as $value) + { + if(isset($lookup["$value"])) + $lookup["$value"]->setSelected(true); + } + } + $this->_cachedSelectedValues=$values; + } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedValues($values); + } + + /** + * @return string selected value + */ + public function getText() + { + return $this->getSelectedValue(); + } + + /** + * @param string value to be selected + */ + public function setText($value) + { + $this->setSelectedValue($value); + } + + /** + * Clears all existing selections. + */ + public function clearSelection() + { + if($this->_items) + { + foreach($this->_items as $item) + $item->setSelected(false); + } + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->clearSelection(); + } + + /** + * @return string the group of validators which the list control causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the list control causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string the prompt text which is to be displayed as the first list item. + * @since 3.1.1 + */ + public function getPromptText() + { + return $this->getViewState('PromptText',''); + } + + /** + * @param string the prompt text which is to be displayed as the first list item. + * @since 3.1.1 + */ + public function setPromptText($value) + { + $this->setViewState('PromptText',$value,''); + } + + /** + * @return string the prompt selection value. + * @see getPromptText + * @since 3.1.1 + */ + public function getPromptValue() + { + return $this->getViewState('PromptValue',''); + } + + /** + * @param string the prompt selection value. If empty, {@link getPromptText PromptText} will be used as the value. + * @see setPromptText + * @since 3.1.1 + */ + public function setPromptValue($value) + { + $this->setViewState('PromptValue',(string)$value,''); + } + + /** + * Raises OnSelectedIndexChanged event when selection is changed. + * This method is invoked when the list control has its selection changed + * by end-users. + * @param TEventParameter event parameter + */ + public function onSelectedIndexChanged($param) + { + $this->raiseEvent('OnSelectedIndexChanged',$this,$param); + $this->onTextChanged($param); + } + + /** + * Raises OnTextChanged event when selection is changed. + * This method is invoked when the list control has its selection changed + * by end-users. + * @param TEventParameter event parameter + */ + public function onTextChanged($param) + { + $this->raiseEvent('OnTextChanged',$this,$param); + } + + /** + * Renders the prompt text, if any. + * @param THtmlWriter writer + * @since 3.1.1 + */ + protected function renderPrompt($writer) + { + $text=$this->getPromptText(); + $value=$this->getPromptValue(); + if($value==='') + $value=$text; + if($value!=='') + { + $writer->addAttribute('value',$value); + $writer->renderBeginTag('option'); + $writer->write(THttpUtility::htmlEncode($text)); + $writer->renderEndTag(); + $writer->writeLine(); + } + } + + /** + * Renders body content of the list control. + * This method renders items contained in the list control as the body content. + * @param THtmlWriter writer + */ + public function renderContents($writer) + { + $this->renderPrompt($writer); + + if($this->_items) + { + $writer->writeLine(); + $previousGroup=null; + foreach($this->_items as $item) + { + if($item->getEnabled()) + { + if($item->getHasAttributes()) + { + $group=$item->getAttributes()->remove('Group'); + if($group!==$previousGroup) + { + if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + $previousGroup=null; + } + if($group!==null) + { + $writer->addAttribute('label',$group); + $writer->renderBeginTag('optgroup'); + $writer->writeLine(); + $previousGroup=$group; + } + } + foreach($item->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + else if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + $previousGroup=null; + } + if($item->getSelected()) + $writer->addAttribute('selected','selected'); + $writer->addAttribute('value',$item->getValue()); + $writer->renderBeginTag('option'); + $writer->write(THttpUtility::htmlEncode($item->getText())); + $writer->renderEndTag(); + $writer->writeLine(); + } + } + if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + } + } + } + + /** + * Formats the text value according to a format string. + * If the format string is empty, the original value is converted into + * a string and returned. + * If the format string starts with '#', the string is treated as a PHP expression + * within which the token '{0}' is translated with the data value to be formated. + * Otherwise, the format string and the data value are passed + * as the first and second parameters in {@link sprintf}. + * @param string format string + * @param mixed the data to be formatted + * @return string the formatted result + */ + protected function formatDataValue($formatString,$value) + { + if($formatString==='') + return TPropertyValue::ensureString($value); + else if($formatString[0]==='#') + { + $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidDataValueException('listcontrol_expression_invalid',get_class($this),$expression,$e->getMessage()); + } + } + else + return sprintf($formatString,$value); + } +} + +/** + * IListControlAdapter interface + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 25 04:53:43 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +interface IListControlAdapter +{ + /** + * Selects an item based on zero-base index on the client side. + * @param integer the index (zero-based) of the item to be selected + */ + public function setSelectedIndex($index); + /** + * Selects a list of item based on zero-base indices on the client side. + * @param array list of index of items to be selected + */ + public function setSelectedIndices($indices); + + /** + * Sets selection by item value on the client side. + * @param string the value of the item to be selected. + */ + public function setSelectedValue($value); + + /** + * Sets selection by a list of item values on the client side. + * @param array list of the selected item values + */ + public function setSelectedValues($values); + + /** + * Clears all existing selections on the client side. + */ + public function clearSelection(); +} + + +?> diff --git a/framework/Web/UI/WebControls/TListControlValidator.php b/framework/Web/UI/WebControls/TListControlValidator.php index a5be67b3..75a0510c 100644 --- a/framework/Web/UI/WebControls/TListControlValidator.php +++ b/framework/Web/UI/WebControls/TListControlValidator.php @@ -1,225 +1,225 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TBaseValidator class - */ -Prado::using('System.Web.UI.WebControls.TBaseValidator'); - -/** - * TListControlValidator class. - * - * TListControlValidator checks the number of selection and their values - * for a TListControl that allows multiple selection. - * - * You can specify the minimum or maximum (or both) number of selections - * required using the {@link setMinSelection MinSelection} and - * {@link setMaxSelection MaxSelection} properties, respectively. In addition, - * you can specify a comma separated list of required selected values via the - * {@link setRequiredSelections RequiredSelections} property. - * - * Examples - * - At least two selections - * - * - * - * - * - * - * - * - * - * - "value1" must be selected and at least 1 other - * - * - * - * - * - * - * - * - * - * - * @author Xiang Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TListControlValidator extends TBaseValidator -{ - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TListControlValidator'; - } - - /** - * @return integer min number of selections. Defaults to -1, meaning not set. - */ - public function getMinSelection() - { - return $this->getViewState('MinSelection',-1); - } - - /** - * @param integer minimum number of selections. - */ - public function setMinSelection($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - $this->setViewState('MinSelection',$value,-1); - } - - /** - * @return integer max number of selections. Defaults to -1, meaning not set. - */ - public function getMaxSelection() - { - return $this->getViewState('MaxSelection',-1); - } - - /** - * @param integer max number of selections. - */ - public function setMaxSelection($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - $value=-1; - $this->setViewState('MaxSelection',$value,-1); - } - - /** - * Get a comma separated list of required selected values. - * @return string comma separated list of required values. - */ - public function getRequiredSelections() - { - return $this->getViewState('RequiredSelections',''); - } - - /** - * Set the list of required values, using aa comma separated list. - * @param string comma separated list of required values. - */ - public function setRequiredSelections($value) - { - $this->setViewState('RequiredSelections',$value,''); - } - - /** - * This method overrides the parent's implementation. - * The validation succeeds if the input component changes its data - * from the InitialValue or the input component is not given. - * @return boolean whether the validation succeeds - */ - protected function evaluateIsValid() - { - $control=$this->getValidationTarget(); - - $exists = true; - $values = $this->getSelection($control); - $count = count($values); - $required = $this->getRequiredValues(); - - //if required, check the values - if(!empty($required)) - { - if($count < count($required) ) - return false; - foreach($required as $require) - $exists = $exists && in_array($require, $values); - } - - $min = $this->getMinSelection(); - $max = $this->getMaxSelection(); - - if($min !== -1 && $max !== -1) - return $exists && $count >= $min && $count <= $max; - else if($min === -1 && $max !== -1) - return $exists && $count <= $max; - else if($min !== -1 && $max === -1) - return $exists && $count >= $min; - else - return $exists; - } - - /** - * @param TListControl control to validate - * @return array number of selected values and its values. - */ - protected function getSelection($control) - { - $values = array(); - - //get the data - foreach($control->getItems() as $item) - { - if($item->getSelected()) - $values[] = $item->getValue(); - } - return $values; - } - - /** - * @return array list of required values. - */ - protected function getRequiredValues() - { - $required = array(); - $string = $this->getRequiredSelections(); - if(!empty($string)) - $required = preg_split('/,\s*/', $string); - return $required; - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options = parent::getClientScriptOptions(); - $control = $this->getValidationTarget(); - - if(!$control instanceof TListControl) - { - throw new TConfigurationException( - 'listcontrolvalidator_invalid_control', - $this->getID(),$this->getControlToValidate(), get_class($control)); - } - - $min = $this->getMinSelection(); - $max = $this->getMaxSelection(); - if($min !== -1) - $options['Min']= $min; - if($max !== -1) - $options['Max']= $max; - $required = $this->getRequiredSelections(); - if(strlen($required) > 0) - $options['Required']= $required; - $options['TotalItems'] = $control->getItemCount(); - - return $options; - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TListControlValidator class. + * + * TListControlValidator checks the number of selection and their values + * for a TListControl that allows multiple selection. + * + * You can specify the minimum or maximum (or both) number of selections + * required using the {@link setMinSelection MinSelection} and + * {@link setMaxSelection MaxSelection} properties, respectively. In addition, + * you can specify a comma separated list of required selected values via the + * {@link setRequiredSelections RequiredSelections} property. + * + * Examples + * - At least two selections + * + * + * + * + * + * + * + * + * + * - "value1" must be selected and at least 1 other + * + * + * + * + * + * + * + * + * + * + * @author Xiang Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListControlValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TListControlValidator'; + } + + /** + * @return integer min number of selections. Defaults to -1, meaning not set. + */ + public function getMinSelection() + { + return $this->getViewState('MinSelection',-1); + } + + /** + * @param integer minimum number of selections. + */ + public function setMinSelection($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MinSelection',$value,-1); + } + + /** + * @return integer max number of selections. Defaults to -1, meaning not set. + */ + public function getMaxSelection() + { + return $this->getViewState('MaxSelection',-1); + } + + /** + * @param integer max number of selections. + */ + public function setMaxSelection($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MaxSelection',$value,-1); + } + + /** + * Get a comma separated list of required selected values. + * @return string comma separated list of required values. + */ + public function getRequiredSelections() + { + return $this->getViewState('RequiredSelections',''); + } + + /** + * Set the list of required values, using aa comma separated list. + * @param string comma separated list of required values. + */ + public function setRequiredSelections($value) + { + $this->setViewState('RequiredSelections',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input component changes its data + * from the InitialValue or the input component is not given. + * @return boolean whether the validation succeeds + */ + protected function evaluateIsValid() + { + $control=$this->getValidationTarget(); + + $exists = true; + $values = $this->getSelection($control); + $count = count($values); + $required = $this->getRequiredValues(); + + //if required, check the values + if(!empty($required)) + { + if($count < count($required) ) + return false; + foreach($required as $require) + $exists = $exists && in_array($require, $values); + } + + $min = $this->getMinSelection(); + $max = $this->getMaxSelection(); + + if($min !== -1 && $max !== -1) + return $exists && $count >= $min && $count <= $max; + else if($min === -1 && $max !== -1) + return $exists && $count <= $max; + else if($min !== -1 && $max === -1) + return $exists && $count >= $min; + else + return $exists; + } + + /** + * @param TListControl control to validate + * @return array number of selected values and its values. + */ + protected function getSelection($control) + { + $values = array(); + + //get the data + foreach($control->getItems() as $item) + { + if($item->getSelected()) + $values[] = $item->getValue(); + } + return $values; + } + + /** + * @return array list of required values. + */ + protected function getRequiredValues() + { + $required = array(); + $string = $this->getRequiredSelections(); + if(!empty($string)) + $required = preg_split('/,\s*/', $string); + return $required; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $control = $this->getValidationTarget(); + + if(!$control instanceof TListControl) + { + throw new TConfigurationException( + 'listcontrolvalidator_invalid_control', + $this->getID(),$this->getControlToValidate(), get_class($control)); + } + + $min = $this->getMinSelection(); + $max = $this->getMaxSelection(); + if($min !== -1) + $options['Min']= $min; + if($max !== -1) + $options['Max']= $max; + $required = $this->getRequiredSelections(); + if(strlen($required) > 0) + $options['Required']= $required; + $options['TotalItems'] = $control->getItemCount(); + + return $options; + } +} diff --git a/framework/Web/UI/WebControls/TListItem.php b/framework/Web/UI/WebControls/TListItem.php index 354aa62a..e80bcafd 100644 --- a/framework/Web/UI/WebControls/TListItem.php +++ b/framework/Web/UI/WebControls/TListItem.php @@ -1,184 +1,184 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TListItem class. - * - * TListItem represents an item in a list control. Each item has a {@link setText Text} - * property and a {@link setValue Value} property. If either one of them is not set, - * it will take the value of the other property. - * An item can be {@link setSelected Selected} or {@link setEnabled Enabled}, - * and it can have additional {@link getAttributes Attributes} which may be rendered - * if the list control supports so. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TListItem extends TComponent -{ - /** - * @var TMap list of custom attributes - */ - private $_attributes=null; - /** - * @var string text of the item - */ - private $_text; - /** - * @var string value of the item - */ - private $_value; - /** - * @var boolean whether the item is enabled - */ - private $_enabled; - /** - * @var boolean whether the item is selected - */ - private $_selected; - - /** - * Constructor. - * @param string text of the item - * @param string value of the item - * @param boolean whether the item is enabled - * @param boolean whether the item is selected - */ - public function __construct($text='',$value='',$enabled=true,$selected=false) - { - $this->setText($text); - $this->setValue($value); - $this->setEnabled($enabled); - $this->setSelected($selected); - } - - /** - * @return boolean whether the item is enabled - */ - public function getEnabled() - { - return $this->_enabled; - } - - /** - * @param boolean whether the item is enabled - */ - public function setEnabled($value) - { - $this->_enabled=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether the item is selected - */ - public function getSelected() - { - return $this->_selected; - } - - /** - * @param boolean whether the item is selected - */ - public function setSelected($value) - { - $this->_selected=TPropertyValue::ensureBoolean($value); - } - - /** - * @return string text of the item - */ - public function getText() - { - return $this->_text===''?$this->_value:$this->_text; - } - - /** - * @param string text of the item - */ - public function setText($value) - { - $this->_text=TPropertyValue::ensureString($value); - } - - /** - * @return string value of the item - */ - public function getValue() - { - return $this->_value===''?$this->_text:$this->_value; - } - - /** - * @param string value of the item - */ - public function setValue($value) - { - $this->_value=TPropertyValue::ensureString($value); - } - - /** - * @return TAttributeCollection custom attributes - */ - public function getAttributes() - { - if(!$this->_attributes) - $this->_attributes=new TAttributeCollection; - return $this->_attributes; - } - - /** - * @return boolean whether the item has any custom attribute - */ - public function getHasAttributes() - { - return $this->_attributes && $this->_attributes->getCount()>0; - } - - /** - * @param string name of the attribute - * @return boolean whether the named attribute exists - */ - public function hasAttribute($name) - { - return $this->_attributes?$this->_attributes->contains($name):false; - } - - /** - * @return string the named attribute value, null if attribute does not exist - */ - public function getAttribute($name) - { - return $this->_attributes?$this->_attributes->itemAt($name):null; - } - - /** - * @param string attribute name - * @param string value of the attribute - */ - public function setAttribute($name,$value) - { - $this->getAttributes()->add($name,$value); - } - - /** - * Removes the named attribute. - * @param string the name of the attribute to be removed. - * @return string attribute value removed, empty string if attribute does not exist. - */ - public function removeAttribute($name) - { - return $this->_attributes?$this->_attributes->remove($name):null; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TListItem class. + * + * TListItem represents an item in a list control. Each item has a {@link setText Text} + * property and a {@link setValue Value} property. If either one of them is not set, + * it will take the value of the other property. + * An item can be {@link setSelected Selected} or {@link setEnabled Enabled}, + * and it can have additional {@link getAttributes Attributes} which may be rendered + * if the list control supports so. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListItem extends TComponent +{ + /** + * @var TMap list of custom attributes + */ + private $_attributes=null; + /** + * @var string text of the item + */ + private $_text; + /** + * @var string value of the item + */ + private $_value; + /** + * @var boolean whether the item is enabled + */ + private $_enabled; + /** + * @var boolean whether the item is selected + */ + private $_selected; + + /** + * Constructor. + * @param string text of the item + * @param string value of the item + * @param boolean whether the item is enabled + * @param boolean whether the item is selected + */ + public function __construct($text='',$value='',$enabled=true,$selected=false) + { + $this->setText($text); + $this->setValue($value); + $this->setEnabled($enabled); + $this->setSelected($selected); + } + + /** + * @return boolean whether the item is enabled + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * @param boolean whether the item is enabled + */ + public function setEnabled($value) + { + $this->_enabled=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether the item is selected + */ + public function getSelected() + { + return $this->_selected; + } + + /** + * @param boolean whether the item is selected + */ + public function setSelected($value) + { + $this->_selected=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string text of the item + */ + public function getText() + { + return $this->_text===''?$this->_value:$this->_text; + } + + /** + * @param string text of the item + */ + public function setText($value) + { + $this->_text=TPropertyValue::ensureString($value); + } + + /** + * @return string value of the item + */ + public function getValue() + { + return $this->_value===''?$this->_text:$this->_value; + } + + /** + * @param string value of the item + */ + public function setValue($value) + { + $this->_value=TPropertyValue::ensureString($value); + } + + /** + * @return TAttributeCollection custom attributes + */ + public function getAttributes() + { + if(!$this->_attributes) + $this->_attributes=new TAttributeCollection; + return $this->_attributes; + } + + /** + * @return boolean whether the item has any custom attribute + */ + public function getHasAttributes() + { + return $this->_attributes && $this->_attributes->getCount()>0; + } + + /** + * @param string name of the attribute + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + return $this->_attributes?$this->_attributes->contains($name):false; + } + + /** + * @return string the named attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + return $this->_attributes?$this->_attributes->itemAt($name):null; + } + + /** + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, empty string if attribute does not exist. + */ + public function removeAttribute($name) + { + return $this->_attributes?$this->_attributes->remove($name):null; + } +} + diff --git a/framework/Web/UI/WebControls/TLiteral.php b/framework/Web/UI/WebControls/TLiteral.php index 423e2d8e..e98d56bb 100644 --- a/framework/Web/UI/WebControls/TLiteral.php +++ b/framework/Web/UI/WebControls/TLiteral.php @@ -1,112 +1,112 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TLiteral class - * - * TLiteral displays a static text on the Web page. - * TLiteral is similar to the TLabel control, except that the TLiteral - * control does not have style properties (e.g. BackColor, Font, etc.) - * You can programmatically control the text displayed in the control by setting - * the {@link setText Text} property. The text displayed may be HTML-encoded - * if the {@link setEncode Encode} property is set true (defaults to false). - * - * TLiteral will render the contents enclosed within its component tag - * if {@link setText Text} is empty. - * - * Note, if {@link setEncode Encode} is false, make sure {@link setText Text} - * does not contain unwanted characters that may bring security vulnerabilities. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TLiteral extends TControl implements IDataRenderer -{ - /** - * @return string the static text of the TLiteral - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * Sets the static text of the TLiteral - * @param string the text to be set - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * Returns the static text of the TLiteral. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string the static text of the TLiteral - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the static text of the TLiteral. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string the static text of the TLiteral - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. - */ - public function getEncode() - { - return $this->getViewState('Encode',false); - } - - /** - * @param boolean whether the rendered text should be HTML-encoded. - */ - public function setEncode($value) - { - $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); - } - - /** - * Renders the literal control. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - if(($text=$this->getText())!=='') - { - if($this->getEncode()) - $writer->write(THttpUtility::htmlEncode($text)); - else - $writer->write($text); - } - else - parent::render($writer); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TLiteral class + * + * TLiteral displays a static text on the Web page. + * TLiteral is similar to the TLabel control, except that the TLiteral + * control does not have style properties (e.g. BackColor, Font, etc.) + * You can programmatically control the text displayed in the control by setting + * the {@link setText Text} property. The text displayed may be HTML-encoded + * if the {@link setEncode Encode} property is set true (defaults to false). + * + * TLiteral will render the contents enclosed within its component tag + * if {@link setText Text} is empty. + * + * Note, if {@link setEncode Encode} is false, make sure {@link setText Text} + * does not contain unwanted characters that may bring security vulnerabilities. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLiteral extends TControl implements IDataRenderer +{ + /** + * @return string the static text of the TLiteral + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the static text of the TLiteral + * @param string the text to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the static text of the TLiteral. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the static text of the TLiteral + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the static text of the TLiteral. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the static text of the TLiteral + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. + */ + public function getEncode() + { + return $this->getViewState('Encode',false); + } + + /** + * @param boolean whether the rendered text should be HTML-encoded. + */ + public function setEncode($value) + { + $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Renders the literal control. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if(($text=$this->getText())!=='') + { + if($this->getEncode()) + $writer->write(THttpUtility::htmlEncode($text)); + else + $writer->write($text); + } + else + parent::render($writer); + } +} + diff --git a/framework/Web/UI/WebControls/TLiteralColumn.php b/framework/Web/UI/WebControls/TLiteralColumn.php index 5b1353fc..48cbe013 100644 --- a/framework/Web/UI/WebControls/TLiteralColumn.php +++ b/framework/Web/UI/WebControls/TLiteralColumn.php @@ -1,154 +1,154 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ - * @package System.Web.UI.WebControls - */ - -/** - * TDataGridColumn class file - */ -Prado::using('System.Web.UI.WebControls.TDataGridColumn'); - -/** - * TLiteralColumn class - * - * TLiteralColumn represents a static text column that is bound to a field in a data source. - * The cells in the column will be displayed with static texts using the data indexed by - * {@link setDataField DataField}. You can customize the display by - * setting {@link setDataFormatString DataFormatString}. - * - * If {@link setDataField DataField} is not specified, the cells will be filled - * with {@link setText Text}. - * - * If {@link setEncode Encode} is true, the static texts will be HTML-encoded. - * - * @author Qiang Xue - * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ - * @package System.Web.UI.WebControls - * @since 3.0.5 - */ -class TLiteralColumn extends TDataGridColumn -{ - /** - * @return string the field name from the data source to bind to the column - */ - public function getDataField() - { - return $this->getViewState('DataField',''); - } - - /** - * @param string the field name from the data source to bind to the column - */ - public function setDataField($value) - { - $this->setViewState('DataField',$value,''); - } - - /** - * @return string the formatting string used to control how the bound data will be displayed. - */ - public function getDataFormatString() - { - return $this->getViewState('DataFormatString',''); - } - - /** - * @param string the formatting string used to control how the bound data will be displayed. - */ - public function setDataFormatString($value) - { - $this->setViewState('DataFormatString',$value,''); - } - - /** - * @return string static text to be displayed in the column. Defaults to empty. - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * @param string static text to be displayed in the column. - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. - */ - public function getEncode() - { - return $this->getViewState('Encode',false); - } - - /** - * @param boolean whether the rendered text should be HTML-encoded. - */ - public function setEncode($value) - { - $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); - } - - /** - * Initializes the specified cell to its initial values. - * This method overrides the parent implementation. - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::EditItem || $itemType===TListItemType::SelectedItem) - { - if($this->getDataField()!=='') - $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - else - { - if(($dataField=$this->getDataField())!=='') - $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - else - { - $text=$this->getText(); - if($this->getEncode()) - $text=THttpUtility::htmlEncode($text); - $cell->setText($text); - } - } - } - else - parent::initializeCell($cell,$columnIndex,$itemType); - } - - /** - * Databinds a cell in the column. - * This method is invoked when datagrid performs databinding. - * It populates the content of the cell with the relevant data from data source. - */ - public function dataBindColumn($sender,$param) - { - $item=$sender->getNamingContainer(); - $data=$item->getData(); - $formatString=$this->getDataFormatString(); - if(($field=$this->getDataField())!=='') - $value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field)); - else - $value=$this->formatDataValue($formatString,$data); - if($sender instanceof TTableCell) - { - if($this->getEncode()) - $value=THttpUtility::htmlEncode($value); - $sender->setText($value); - } - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TLiteralColumn class + * + * TLiteralColumn represents a static text column that is bound to a field in a data source. + * The cells in the column will be displayed with static texts using the data indexed by + * {@link setDataField DataField}. You can customize the display by + * setting {@link setDataFormatString DataFormatString}. + * + * If {@link setDataField DataField} is not specified, the cells will be filled + * with {@link setText Text}. + * + * If {@link setEncode Encode} is true, the static texts will be HTML-encoded. + * + * @author Qiang Xue + * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ + * @package System.Web.UI.WebControls + * @since 3.0.5 + */ +class TLiteralColumn extends TDataGridColumn +{ + /** + * @return string the field name from the data source to bind to the column + */ + public function getDataField() + { + return $this->getViewState('DataField',''); + } + + /** + * @param string the field name from the data source to bind to the column + */ + public function setDataField($value) + { + $this->setViewState('DataField',$value,''); + } + + /** + * @return string the formatting string used to control how the bound data will be displayed. + */ + public function getDataFormatString() + { + return $this->getViewState('DataFormatString',''); + } + + /** + * @param string the formatting string used to control how the bound data will be displayed. + */ + public function setDataFormatString($value) + { + $this->setViewState('DataFormatString',$value,''); + } + + /** + * @return string static text to be displayed in the column. Defaults to empty. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string static text to be displayed in the column. + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. + */ + public function getEncode() + { + return $this->getViewState('Encode',false); + } + + /** + * @param boolean whether the rendered text should be HTML-encoded. + */ + public function setEncode($value) + { + $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::EditItem || $itemType===TListItemType::SelectedItem) + { + if($this->getDataField()!=='') + $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + else + { + if(($dataField=$this->getDataField())!=='') + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + else + { + $text=$this->getText(); + if($this->getEncode()) + $text=THttpUtility::htmlEncode($text); + $cell->setText($text); + } + } + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + $formatString=$this->getDataFormatString(); + if(($field=$this->getDataField())!=='') + $value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field)); + else + $value=$this->formatDataValue($formatString,$data); + if($sender instanceof TTableCell) + { + if($this->getEncode()) + $value=THttpUtility::htmlEncode($value); + $sender->setText($value); + } + } +} + diff --git a/framework/Web/UI/WebControls/TMarkdown.php b/framework/Web/UI/WebControls/TMarkdown.php index 3aa1c9be..726a1ebe 100644 --- a/framework/Web/UI/WebControls/TMarkdown.php +++ b/framework/Web/UI/WebControls/TMarkdown.php @@ -1,75 +1,75 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TTextHighlighter and MarkdownParser classes - */ -Prado::using('System.Web.UI.WebControls.TTextHighlighter'); -Prado::using('System.3rdParty.Markdown.MarkdownParser'); - -/** - * TMarkdown class - * - * TMarkdown is a control that produces HTML from code with markdown syntax. - * - * Markdown is a text-to-HTML conversion tool for web writers. Markdown allows - * you to write using an easy-to-read, easy-to-write plain text format, then - * convert it to structurally valid XHTML (or HTML). - * Further documentation regarding Markdown can be found at - * http://daringfireball.net/projects/markdown/ - * - * To use TMarkdown, simply enclose the content to be rendered within - * the body of TMarkdown in a template. - * - * See http://www.pradosoft.com/demos/quickstart/?page=Markdown for - * details on the Markdown syntax usage. - * - * TMarkdown also performs syntax highlighting for code blocks whose language - * is recognized by {@link TTextHighlighter}. - * The language of a code block must be specified in the first line of the block - * and enclosed within a pair of square brackets (e.g. [php]). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.1 - */ -class TMarkdown extends TTextHighlighter -{ - /** - * Processes a text string. - * This method is required by the parent class. - * @param string text string to be processed - * @return string the processed text result - */ - public function processText($text) - { - $renderer = new MarkdownParser; - $result = $renderer->parse($text); - return preg_replace_callback( - '/
    \[\s*(\w+)\s*\]\n+((.|\n)*?)\s*<\\/code><\\/pre>/im',
    -				array($this, 'highlightCode'), $result);
    -	}
    -
    -	/**
    -	 * Highlights source code using TTextHighlighter
    -	 * @param array matches of code blocks
    -	 * @return string highlighted code.
    -	 */
    -	protected function highlightCode($matches)
    -	{
    -		$text = html_entity_decode($matches[2],ENT_QUOTES,'UTF-8');
    -		$this->setLanguage($matches[1]);
    -		return parent::processText($text);
    -	}
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Using TTextHighlighter and MarkdownParser classes
    + */
    +Prado::using('System.Web.UI.WebControls.TTextHighlighter');
    +Prado::using('System.3rdParty.Markdown.MarkdownParser');
    +
    +/**
    + * TMarkdown class
    + *
    + * TMarkdown is a control that produces HTML from code with markdown syntax.
    + *
    + * Markdown is a text-to-HTML conversion tool for web writers. Markdown allows
    + * you to write using an easy-to-read, easy-to-write plain text format, then
    + * convert it to structurally valid XHTML (or HTML).
    + * Further documentation regarding Markdown can be found at
    + * http://daringfireball.net/projects/markdown/
    + *
    + * To use TMarkdown, simply enclose the content to be rendered within
    + * the body of TMarkdown in a template.
    + *
    + * See http://www.pradosoft.com/demos/quickstart/?page=Markdown for
    + * details on the Markdown syntax usage.
    + *
    + * TMarkdown also performs syntax highlighting for code blocks whose language
    + * is recognized by {@link TTextHighlighter}.
    + * The language of a code block must be specified in the first line of the block
    + * and enclosed within a pair of square brackets (e.g. [php]).
    + *
    + * @author Wei Zhuo 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.1
    + */
    +class TMarkdown extends TTextHighlighter
    +{
    +	/**
    +	 * Processes a text string.
    +	 * This method is required by the parent class.
    +	 * @param string text string to be processed
    +	 * @return string the processed text result
    +	 */
    +	public function processText($text)
    +	{
    +		$renderer = new MarkdownParser;
    +		$result = $renderer->parse($text);
    +		return preg_replace_callback(
    +				'/
    \[\s*(\w+)\s*\]\n+((.|\n)*?)\s*<\\/code><\\/pre>/im',
    +				array($this, 'highlightCode'), $result);
    +	}
    +
    +	/**
    +	 * Highlights source code using TTextHighlighter
    +	 * @param array matches of code blocks
    +	 * @return string highlighted code.
    +	 */
    +	protected function highlightCode($matches)
    +	{
    +		$text = html_entity_decode($matches[2],ENT_QUOTES,'UTF-8');
    +		$this->setLanguage($matches[1]);
    +		return parent::processText($text);
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TMultiView.php b/framework/Web/UI/WebControls/TMultiView.php
    index 81d404b2..0c40cd06 100644
    --- a/framework/Web/UI/WebControls/TMultiView.php
    +++ b/framework/Web/UI/WebControls/TMultiView.php
    @@ -1,378 +1,378 @@
    -
    - * @link http://www.pradosoft.com/
    - * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * TMultiView class
    - *
    - * TMultiView serves as a container for a group of {@link TView} controls.
    - * The view collection can be retrieved by {@link getViews Views}.
    - * Each view contains child controls. TMultiView determines which view and its
    - * child controls are visible. At any time, at most one view is visible (called
    - * active). To make a view active, set {@link setActiveView ActiveView} or
    - * {@link setActiveViewIndex ActiveViewIndex}.
    - *
    - * TMultiView also responds to specific command events raised from button controls
    - * contained in current active view. A command event with name 'NextView'
    - * will cause TMultiView to make the next available view active.
    - * Other command names recognized by TMultiView include
    - * - PreviousView : switch to previous view
    - * - SwitchViewID : switch to a view by its ID path
    - * - SwitchViewIndex : switch to a view by its index in the {@link getViews Views} collection.
    - *
    - * TMultiView raises {@link OnActiveViewChanged OnActiveViewChanged} event
    - * when its active view is changed during a postback.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TMultiView extends TControl
    -{
    -	const CMD_NEXTVIEW='NextView';
    -	const CMD_PREVIOUSVIEW='PreviousView';
    -	const CMD_SWITCHVIEWID='SwitchViewID';
    -	const CMD_SWITCHVIEWINDEX='SwitchViewIndex';
    -	private $_cachedActiveViewIndex=-1;
    -	private $_ignoreBubbleEvents=false;
    -
    -	/**
    -	 * Processes an object that is created during parsing template.
    -	 * This method overrides the parent implementation by adding only {@link TView}
    -	 * controls as children.
    -	 * @param string|TComponent text string or component parsed and instantiated in template
    -	 * @see createdOnTemplate
    -	 * @throws TConfigurationException if controls other than {@link TView} is being added
    -	 */
    -	public function addParsedObject($object)
    -	{
    -		if($object instanceof TView)
    -			$this->getControls()->add($object);
    -		else if(!is_string($object))
    -			throw new TConfigurationException('multiview_view_required');
    -	}
    -
    -	/**
    -	 * Creates a control collection object that is to be used to hold child controls
    -	 * @return TViewCollection control collection
    -	 */
    -	protected function createControlCollection()
    -	{
    -		return new TViewCollection($this);
    -	}
    -
    -	/**
    -	 * @return integer the zero-based index of the current view in the view collection. -1 if no active view. Default is -1.
    -	 */
    -	public function getActiveViewIndex()
    -	{
    -		if($this->_cachedActiveViewIndex>-1)
    -			return $this->_cachedActiveViewIndex;
    -		else
    -			return $this->getControlState('ActiveViewIndex',-1);
    -	}
    -
    -	/**
    -	 * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
    -	 * @throws TInvalidDataValueException if the view index is invalid
    -	 */
    -	public function setActiveViewIndex($value)
    -	{
    -		if(($index=TPropertyValue::ensureInteger($value))<0)
    -			$index=-1;
    -		$views=$this->getViews();
    -		$count=$views->getCount();
    -		if($count===0 && $this->getControlStage()_cachedActiveViewIndex=$index;
    -		else if($index<$count)
    -		{
    -			$this->setControlState('ActiveViewIndex',$index,-1);
    -			$this->_cachedActiveViewIndex=-1;
    -			if($index>=0)
    -				$this->activateView($views->itemAt($index),true);
    -		}
    -		else
    -			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
    -	}
    -
    -	/**
    -	 * @return TView the currently active view, null if no active view
    -	 * @throws TInvalidDataValueException if the current active view index is invalid
    -	 */
    -	public function getActiveView()
    -	{
    -		$index=$this->getActiveViewIndex();
    -		$views=$this->getViews();
    -		if($index>=$views->getCount())
    -			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
    -		if($index<0)
    -			return null;
    -		$view=$views->itemAt($index);
    -		if(!$view->getActive())
    -			$this->activateView($view,false);
    -		return $view;
    -	}
    -
    -	/**
    -	 * @param TView the view to be activated
    -	 * @throws TInvalidOperationException if the view is not in the view collection
    -	 */
    -	public function setActiveView($view)
    -	{
    -		if(($index=$this->getViews()->indexOf($view))>=0)
    -			$this->setActiveViewIndex($index);
    -		else
    -			throw new TInvalidOperationException('multiview_view_inexistent');
    -	}
    -
    -	/**
    -	 * Activates the specified view.
    -	 * If there is any view currently active, it will be deactivated.
    -	 * @param TView the view to be activated
    -	 * @param boolean whether to trigger OnActiveViewChanged event.
    -	 */
    -	protected function activateView($view,$triggerViewChangedEvent=true)
    -	{
    -		if($view->getActive())
    -			return;
    -		$triggerEvent=$triggerViewChangedEvent && ($this->getControlStage()>=TControl::CS_STATE_LOADED || ($this->getPage() && !$this->getPage()->getIsPostBack()));
    -		foreach($this->getViews() as $v)
    -		{
    -			if($v===$view)
    -			{
    -				$view->setActive(true);
    -				if($triggerEvent)
    -				{
    -					$view->onActivate(null);
    -					$this->onActiveViewChanged(null);
    -				}
    -			}
    -			else if($v->getActive())
    -			{
    -				$v->setActive(false);
    -				if($triggerEvent)
    -					$v->onDeactivate(null);
    -			}
    -		}
    -	}
    -
    -	/**
    -	 * @return TViewCollection the view collection
    -	 */
    -	public function getViews()
    -	{
    -		return $this->getControls();
    -	}
    -
    -	/**
    -	 * Makes the multiview ignore all bubbled events.
    -	 * This is method is used internally by framework and control
    -	 * developers.
    -	 */
    -	public function ignoreBubbleEvents()
    -	{
    -		$this->_ignoreBubbleEvents=true;
    -	}
    -
    -	/**
    -	 * Initializes the active view if any.
    -	 * This method overrides the parent implementation.
    -	 * @param TEventParameter event parameter
    -	 */
    -	public function onInit($param)
    -	{
    -		parent::onInit($param);
    -		if($this->_cachedActiveViewIndex>=0)
    -			$this->setActiveViewIndex($this->_cachedActiveViewIndex);
    -	}
    -
    -	/**
    -	 * Raises OnActiveViewChanged event.
    -	 * The event is raised when the currently active view is changed to a new one
    -	 * @param TEventParameter event parameter
    -	 */
    -	public function onActiveViewChanged($param)
    -	{
    -		$this->raiseEvent('OnActiveViewChanged',$this,$param);
    -	}
    -
    -	/**
    -	 * Processes the events bubbled from child controls.
    -	 * The method handles view-related command events.
    -	 * @param TControl sender of the event
    -	 * @param mixed event parameter
    -	 * @return boolean whether this event is handled
    -	 */
    -	public function bubbleEvent($sender,$param)
    -	{
    -		if(!$this->_ignoreBubbleEvents && ($param instanceof TCommandEventParameter))
    -		{
    -			switch($param->getCommandName())
    -			{
    -				case self::CMD_NEXTVIEW:
    -					if(($index=$this->getActiveViewIndex())<$this->getViews()->getCount()-1)
    -						$this->setActiveViewIndex($index+1);
    -					else
    -						$this->setActiveViewIndex(-1);
    -					return true;
    -				case self::CMD_PREVIOUSVIEW:
    -					if(($index=$this->getActiveViewIndex())>=0)
    -						$this->setActiveViewIndex($index-1);
    -					return true;
    -				case self::CMD_SWITCHVIEWID:
    -					$view=$this->findControl($viewID=$param->getCommandParameter());
    -					if($view!==null && $view->getParent()===$this)
    -					{
    -						$this->setActiveView($view);
    -						return true;
    -					}
    -					else
    -						throw new TInvalidDataValueException('multiview_viewid_invalid', $viewID);
    -				case self::CMD_SWITCHVIEWINDEX:
    -					$index=TPropertyValue::ensureInteger($param->getCommandParameter());
    -					$this->setActiveViewIndex($index);
    -					return true;
    -			}
    -		}
    -		return false;
    -	}
    -
    -	/**
    -	 * Loads state into the wizard.
    -	 * This method is invoked by the framework when the control state is being saved.
    -	 */
    -	public function loadState()
    -	{
    -		// a dummy call to ensure the view is activated
    -		$this->getActiveView();
    -	}
    -
    -	/**
    -	 * Renders the currently active view.
    -	 * @param THtmlWriter the writer for the rendering purpose.
    -	 */
    -	public function render($writer)
    -	{
    -		if(($view=$this->getActiveView())!==null)
    -			$view->renderControl($writer);
    -	}
    -}
    -
    -/**
    - * TViewCollection class.
    - * TViewCollection represents a collection that only takes {@link TView} instances
    - * as collection elements.
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TViewCollection extends TControlCollection
    -{
    -	/**
    -	 * Inserts an item at the specified position.
    -	 * This overrides the parent implementation by ensuring only {@link TView}
    -	 * controls be added into the collection.
    -	 * @param integer the speicified position.
    -	 * @param mixed new item
    -	 * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl.
    -	 */
    -	public function insertAt($index,$item)
    -	{
    -		if($item instanceof TView)
    -			parent::insertAt($index,$item);
    -		else
    -			throw new TInvalidDataTypeException('viewcollection_view_required');
    -	}
    -}
    -
    -/**
    - * TView class
    - *
    - * TView is a container for a group of controls. TView must be contained
    - * within a {@link TMultiView} control in which only one view can be active
    - * at one time.
    - *
    - * To activate a view, set {@link setActive Active} to true.
    - * When a view is activated, it raises {@link onActivate OnActivate} event;
    - * and when a view is deactivated, it raises {@link onDeactivate OnDeactivate}.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TView extends TControl
    -{
    -	private $_active=false;
    -
    -	/**
    -	 * Raises OnActivate event.
    -	 * @param TEventParameter event parameter
    -	 */
    -	public function onActivate($param)
    -	{
    -		$this->raiseEvent('OnActivate',$this,$param);
    -	}
    -
    -	/**
    -	 * Raises OnDeactivate event.
    -	 * @param TEventParameter event parameter
    -	 */
    -	public function onDeactivate($param)
    -	{
    -		$this->raiseEvent('OnDeactivate',$this,$param);
    -	}
    -
    -	/**
    -	 * @return boolean whether this view is active. Defaults to false.
    -	 */
    -	public function getActive()
    -	{
    -		return $this->_active;
    -	}
    -
    -	/**
    -	 * @param boolean whether this view is active.
    -	 */
    -	public function setActive($value)
    -	{
    -		$value=TPropertyValue::ensureBoolean($value);
    -		$this->_active=$value;
    -		parent::setVisible($value);
    -	}
    -
    -	/**
    -	 * @param boolean whether the parents should also be checked if visible
    -	 * @return boolean whether this view is visible.
    -	 * The view is visible if it is active and its parent is visible.
    -	 */
    -	public function getVisible($checkParents=true)
    -	{
    -		if(($parent=$this->getParent())===null)
    -			return $this->getActive();
    -		else if($this->getActive())
    -			return $parent->getVisible($checkParents);
    -		else
    -			return false;
    -	}
    -
    -	/**
    -	 * @param boolean
    -	 * @throws TInvalidOperationException whenever this method is invoked.
    -	 */
    -	public function setVisible($value)
    -	{
    -		throw new TInvalidOperationException('view_visible_readonly');
    -	}
    -}
    -
    +
    + * @link http://www.pradosoft.com/
    + * @copyright Copyright © 2005-2012 PradoSoft
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * TMultiView class
    + *
    + * TMultiView serves as a container for a group of {@link TView} controls.
    + * The view collection can be retrieved by {@link getViews Views}.
    + * Each view contains child controls. TMultiView determines which view and its
    + * child controls are visible. At any time, at most one view is visible (called
    + * active). To make a view active, set {@link setActiveView ActiveView} or
    + * {@link setActiveViewIndex ActiveViewIndex}.
    + *
    + * TMultiView also responds to specific command events raised from button controls
    + * contained in current active view. A command event with name 'NextView'
    + * will cause TMultiView to make the next available view active.
    + * Other command names recognized by TMultiView include
    + * - PreviousView : switch to previous view
    + * - SwitchViewID : switch to a view by its ID path
    + * - SwitchViewIndex : switch to a view by its index in the {@link getViews Views} collection.
    + *
    + * TMultiView raises {@link OnActiveViewChanged OnActiveViewChanged} event
    + * when its active view is changed during a postback.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TMultiView extends TControl
    +{
    +	const CMD_NEXTVIEW='NextView';
    +	const CMD_PREVIOUSVIEW='PreviousView';
    +	const CMD_SWITCHVIEWID='SwitchViewID';
    +	const CMD_SWITCHVIEWINDEX='SwitchViewIndex';
    +	private $_cachedActiveViewIndex=-1;
    +	private $_ignoreBubbleEvents=false;
    +
    +	/**
    +	 * Processes an object that is created during parsing template.
    +	 * This method overrides the parent implementation by adding only {@link TView}
    +	 * controls as children.
    +	 * @param string|TComponent text string or component parsed and instantiated in template
    +	 * @see createdOnTemplate
    +	 * @throws TConfigurationException if controls other than {@link TView} is being added
    +	 */
    +	public function addParsedObject($object)
    +	{
    +		if($object instanceof TView)
    +			$this->getControls()->add($object);
    +		else if(!is_string($object))
    +			throw new TConfigurationException('multiview_view_required');
    +	}
    +
    +	/**
    +	 * Creates a control collection object that is to be used to hold child controls
    +	 * @return TViewCollection control collection
    +	 */
    +	protected function createControlCollection()
    +	{
    +		return new TViewCollection($this);
    +	}
    +
    +	/**
    +	 * @return integer the zero-based index of the current view in the view collection. -1 if no active view. Default is -1.
    +	 */
    +	public function getActiveViewIndex()
    +	{
    +		if($this->_cachedActiveViewIndex>-1)
    +			return $this->_cachedActiveViewIndex;
    +		else
    +			return $this->getControlState('ActiveViewIndex',-1);
    +	}
    +
    +	/**
    +	 * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
    +	 * @throws TInvalidDataValueException if the view index is invalid
    +	 */
    +	public function setActiveViewIndex($value)
    +	{
    +		if(($index=TPropertyValue::ensureInteger($value))<0)
    +			$index=-1;
    +		$views=$this->getViews();
    +		$count=$views->getCount();
    +		if($count===0 && $this->getControlStage()_cachedActiveViewIndex=$index;
    +		else if($index<$count)
    +		{
    +			$this->setControlState('ActiveViewIndex',$index,-1);
    +			$this->_cachedActiveViewIndex=-1;
    +			if($index>=0)
    +				$this->activateView($views->itemAt($index),true);
    +		}
    +		else
    +			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
    +	}
    +
    +	/**
    +	 * @return TView the currently active view, null if no active view
    +	 * @throws TInvalidDataValueException if the current active view index is invalid
    +	 */
    +	public function getActiveView()
    +	{
    +		$index=$this->getActiveViewIndex();
    +		$views=$this->getViews();
    +		if($index>=$views->getCount())
    +			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
    +		if($index<0)
    +			return null;
    +		$view=$views->itemAt($index);
    +		if(!$view->getActive())
    +			$this->activateView($view,false);
    +		return $view;
    +	}
    +
    +	/**
    +	 * @param TView the view to be activated
    +	 * @throws TInvalidOperationException if the view is not in the view collection
    +	 */
    +	public function setActiveView($view)
    +	{
    +		if(($index=$this->getViews()->indexOf($view))>=0)
    +			$this->setActiveViewIndex($index);
    +		else
    +			throw new TInvalidOperationException('multiview_view_inexistent');
    +	}
    +
    +	/**
    +	 * Activates the specified view.
    +	 * If there is any view currently active, it will be deactivated.
    +	 * @param TView the view to be activated
    +	 * @param boolean whether to trigger OnActiveViewChanged event.
    +	 */
    +	protected function activateView($view,$triggerViewChangedEvent=true)
    +	{
    +		if($view->getActive())
    +			return;
    +		$triggerEvent=$triggerViewChangedEvent && ($this->getControlStage()>=TControl::CS_STATE_LOADED || ($this->getPage() && !$this->getPage()->getIsPostBack()));
    +		foreach($this->getViews() as $v)
    +		{
    +			if($v===$view)
    +			{
    +				$view->setActive(true);
    +				if($triggerEvent)
    +				{
    +					$view->onActivate(null);
    +					$this->onActiveViewChanged(null);
    +				}
    +			}
    +			else if($v->getActive())
    +			{
    +				$v->setActive(false);
    +				if($triggerEvent)
    +					$v->onDeactivate(null);
    +			}
    +		}
    +	}
    +
    +	/**
    +	 * @return TViewCollection the view collection
    +	 */
    +	public function getViews()
    +	{
    +		return $this->getControls();
    +	}
    +
    +	/**
    +	 * Makes the multiview ignore all bubbled events.
    +	 * This is method is used internally by framework and control
    +	 * developers.
    +	 */
    +	public function ignoreBubbleEvents()
    +	{
    +		$this->_ignoreBubbleEvents=true;
    +	}
    +
    +	/**
    +	 * Initializes the active view if any.
    +	 * This method overrides the parent implementation.
    +	 * @param TEventParameter event parameter
    +	 */
    +	public function onInit($param)
    +	{
    +		parent::onInit($param);
    +		if($this->_cachedActiveViewIndex>=0)
    +			$this->setActiveViewIndex($this->_cachedActiveViewIndex);
    +	}
    +
    +	/**
    +	 * Raises OnActiveViewChanged event.
    +	 * The event is raised when the currently active view is changed to a new one
    +	 * @param TEventParameter event parameter
    +	 */
    +	public function onActiveViewChanged($param)
    +	{
    +		$this->raiseEvent('OnActiveViewChanged',$this,$param);
    +	}
    +
    +	/**
    +	 * Processes the events bubbled from child controls.
    +	 * The method handles view-related command events.
    +	 * @param TControl sender of the event
    +	 * @param mixed event parameter
    +	 * @return boolean whether this event is handled
    +	 */
    +	public function bubbleEvent($sender,$param)
    +	{
    +		if(!$this->_ignoreBubbleEvents && ($param instanceof TCommandEventParameter))
    +		{
    +			switch($param->getCommandName())
    +			{
    +				case self::CMD_NEXTVIEW:
    +					if(($index=$this->getActiveViewIndex())<$this->getViews()->getCount()-1)
    +						$this->setActiveViewIndex($index+1);
    +					else
    +						$this->setActiveViewIndex(-1);
    +					return true;
    +				case self::CMD_PREVIOUSVIEW:
    +					if(($index=$this->getActiveViewIndex())>=0)
    +						$this->setActiveViewIndex($index-1);
    +					return true;
    +				case self::CMD_SWITCHVIEWID:
    +					$view=$this->findControl($viewID=$param->getCommandParameter());
    +					if($view!==null && $view->getParent()===$this)
    +					{
    +						$this->setActiveView($view);
    +						return true;
    +					}
    +					else
    +						throw new TInvalidDataValueException('multiview_viewid_invalid', $viewID);
    +				case self::CMD_SWITCHVIEWINDEX:
    +					$index=TPropertyValue::ensureInteger($param->getCommandParameter());
    +					$this->setActiveViewIndex($index);
    +					return true;
    +			}
    +		}
    +		return false;
    +	}
    +
    +	/**
    +	 * Loads state into the wizard.
    +	 * This method is invoked by the framework when the control state is being saved.
    +	 */
    +	public function loadState()
    +	{
    +		// a dummy call to ensure the view is activated
    +		$this->getActiveView();
    +	}
    +
    +	/**
    +	 * Renders the currently active view.
    +	 * @param THtmlWriter the writer for the rendering purpose.
    +	 */
    +	public function render($writer)
    +	{
    +		if(($view=$this->getActiveView())!==null)
    +			$view->renderControl($writer);
    +	}
    +}
    +
    +/**
    + * TViewCollection class.
    + * TViewCollection represents a collection that only takes {@link TView} instances
    + * as collection elements.
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TViewCollection extends TControlCollection
    +{
    +	/**
    +	 * Inserts an item at the specified position.
    +	 * This overrides the parent implementation by ensuring only {@link TView}
    +	 * controls be added into the collection.
    +	 * @param integer the speicified position.
    +	 * @param mixed new item
    +	 * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl.
    +	 */
    +	public function insertAt($index,$item)
    +	{
    +		if($item instanceof TView)
    +			parent::insertAt($index,$item);
    +		else
    +			throw new TInvalidDataTypeException('viewcollection_view_required');
    +	}
    +}
    +
    +/**
    + * TView class
    + *
    + * TView is a container for a group of controls. TView must be contained
    + * within a {@link TMultiView} control in which only one view can be active
    + * at one time.
    + *
    + * To activate a view, set {@link setActive Active} to true.
    + * When a view is activated, it raises {@link onActivate OnActivate} event;
    + * and when a view is deactivated, it raises {@link onDeactivate OnDeactivate}.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TView extends TControl
    +{
    +	private $_active=false;
    +
    +	/**
    +	 * Raises OnActivate event.
    +	 * @param TEventParameter event parameter
    +	 */
    +	public function onActivate($param)
    +	{
    +		$this->raiseEvent('OnActivate',$this,$param);
    +	}
    +
    +	/**
    +	 * Raises OnDeactivate event.
    +	 * @param TEventParameter event parameter
    +	 */
    +	public function onDeactivate($param)
    +	{
    +		$this->raiseEvent('OnDeactivate',$this,$param);
    +	}
    +
    +	/**
    +	 * @return boolean whether this view is active. Defaults to false.
    +	 */
    +	public function getActive()
    +	{
    +		return $this->_active;
    +	}
    +
    +	/**
    +	 * @param boolean whether this view is active.
    +	 */
    +	public function setActive($value)
    +	{
    +		$value=TPropertyValue::ensureBoolean($value);
    +		$this->_active=$value;
    +		parent::setVisible($value);
    +	}
    +
    +	/**
    +	 * @param boolean whether the parents should also be checked if visible
    +	 * @return boolean whether this view is visible.
    +	 * The view is visible if it is active and its parent is visible.
    +	 */
    +	public function getVisible($checkParents=true)
    +	{
    +		if(($parent=$this->getParent())===null)
    +			return $this->getActive();
    +		else if($this->getActive())
    +			return $parent->getVisible($checkParents);
    +		else
    +			return false;
    +	}
    +
    +	/**
    +	 * @param boolean
    +	 * @throws TInvalidOperationException whenever this method is invoked.
    +	 */
    +	public function setVisible($value)
    +	{
    +		throw new TInvalidOperationException('view_visible_readonly');
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TOutputCache.php b/framework/Web/UI/WebControls/TOutputCache.php
    index 64e4ff42..cc79e76f 100644
    --- a/framework/Web/UI/WebControls/TOutputCache.php
    +++ b/framework/Web/UI/WebControls/TOutputCache.php
    @@ -1,621 +1,621 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * TOutputCache class.
    - *
    - * TOutputCache enables caching a portion of a Web page, also known as
    - * partial caching. The content being cached can be either static or
    - * dynamic.
    - *
    - * To use TOutputCache, simply enclose the content to be cached
    - * within the TOutputCache component tag on a template, e.g.,
    - * 
    - * 
    - *   content to be cached
    - * 
    - * 
    - * where content to be cached can be static text and/or component tags.
    - *
    - * The validity of the cached content is determined based on two factors:
    - * the {@link setDuration Duration} and the cache dependency.
    - * The former specifies the number of seconds that the data can remain
    - * valid in cache (defaults to 60s), while the latter specifies conditions
    - * that the cached data depends on. If a dependency changes,
    - * (e.g. relevant data in DB are updated), the cached data will be invalidated.
    - *
    - * There are two ways to specify cache dependency. One may write event handlers
    - * to respond to the {@link onCheckDependency OnCheckDependency} event and set
    - * the event parameter's {@link TOutputCacheCheckDependencyEventParameter::getIsValid IsValid}
    - * property to indicate whether the cached data remains valid or not.
    - * One can also extend TOutputCache and override its {@link getCacheDependency}
    - * function. While the former is easier to use, the latter offers more extensibility.
    - *
    - * The content fetched from cache may be variated with respect to
    - * some parameters. It supports variation with respect to request parameters,
    - * which is specified by {@link setVaryByParam VaryByParam} property.
    - * If a specified request parameter is different, a different version of
    - * cached content is used. This is extremely useful if a page's content
    - * may be variated according to some GET parameters.
    - * The content being cached may also be variated with user sessions if
    - * {@link setVaryBySession VaryBySession} is set true.
    - * To variate the cached content by other factors, override {@link calculateCacheKey()} method.
    - *
    - * Output caches can be nested. An outer cache takes precedence over an
    - * inner cache. This means, if the content cached by the inner cache expires
    - * or is invalidated, while that by the outer cache not, the outer cached
    - * content will be used.
    - *
    - * Note, TOutputCache is effective only for non-postback page requests
    - * and when cache module is enabled.
    - *
    - * Do not attempt to address child controls of TOutputCache when the cached
    - * content is to be used. Use {@link getContentCached ContentCached} property
    - * to determine whether the content is cached or not.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.1
    - */
    -class TOutputCache extends TControl implements INamingContainer
    -{
    -	const CACHE_ID_PREFIX='prado:outputcache';
    -	private $_cacheModuleID='';
    -	private $_dataCached=false;
    -	private $_cacheAvailable=false;
    -	private $_cacheChecked=false;
    -	private $_cacheKey=null;
    -	private $_duration=60;
    -	private $_cache=null;
    -	private $_contents;
    -	private $_state;
    -	private $_actions=array();
    -	private $_varyByParam='';
    -	private $_keyPrefix='';
    -	private $_varyBySession=false;
    -	private $_cachePostBack=false;
    -	private $_cacheTime=0;
    -
    -	/**
    -	 * Returns a value indicating whether body contents are allowed for this control.
    -	 * This method overrides the parent implementation by checking if cached
    -	 * content is available or not. If yes, it returns false, otherwise true.
    -	 * @param boolean whether body contents are allowed for this control.
    -	 */
    -	public function getAllowChildControls()
    -	{
    -		$this->determineCacheability();
    -		return !$this->_dataCached;
    -	}
    -
    -	private function determineCacheability()
    -	{
    -		if(!$this->_cacheChecked)
    -		{
    -			$this->_cacheChecked=true;
    -			if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack()))
    -			{
    -				if($this->_cacheModuleID!=='')
    -				{
    -					$this->_cache=$this->getApplication()->getModule($this->_cacheModuleID);
    -					if(!($this->_cache instanceof ICache))
    -						throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID);
    -				}
    -				else
    -					$this->_cache=$this->getApplication()->getCache();
    -				if($this->_cache!==null)
    -				{
    -					$this->_cacheAvailable=true;
    -					$data=$this->_cache->get($this->getCacheKey());
    -					if(is_array($data))
    -					{
    -						$param=new TOutputCacheCheckDependencyEventParameter;
    -						$param->setCacheTime(isset($data[3])?$data[3]:0);
    -						$this->onCheckDependency($param);
    -						$this->_dataCached=$param->getIsValid();
    -					}
    -					else
    -						$this->_dataCached=false;
    -					if($this->_dataCached)
    -						list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data;
    -				}
    -			}
    -		}
    -	}
    -
    -	/**
    -	 * Performs the Init step for the control and all its child controls.
    -	 * This method overrides the parent implementation by setting up
    -	 * the stack of the output cache in the page.
    -	 * Only framework developers should use this method.
    -	 * @param TControl the naming container control
    -	 */
    -	protected function initRecursive($namingContainer=null)
    -	{
    -		if($this->_cacheAvailable && !$this->_dataCached)
    -		{
    -			$stack=$this->getPage()->getCachingStack();
    -			$stack->push($this);
    -			parent::initRecursive($namingContainer);
    -			$stack->pop();
    -		}
    -		else
    -			parent::initRecursive($namingContainer);
    -	}
    -
    -	/**
    -	 * Performs the Load step for the control and all its child controls.
    -	 * This method overrides the parent implementation by setting up
    -	 * the stack of the output cache in the page. If the data is restored
    -	 * from cache, it also recovers the actions associated with the cached data.
    -	 * Only framework developers should use this method.
    -	 * @param TControl the naming container control
    -	 */
    -	protected function loadRecursive()
    -	{
    -		if($this->_cacheAvailable && !$this->_dataCached)
    -		{
    -			$stack=$this->getPage()->getCachingStack();
    -			$stack->push($this);
    -			parent::loadRecursive();
    -			$stack->pop();
    -		}
    -		else
    -		{
    -			if($this->_dataCached)
    -				$this->performActions();
    -			parent::loadRecursive();
    -		}
    -	}
    -
    -	private function performActions()
    -	{
    -		$page=$this->getPage();
    -		$cs=$page->getClientScript();
    -		foreach($this->_actions as $action)
    -		{
    -			if($action[0]==='Page.ClientScript')
    -				call_user_func_array(array($cs,$action[1]),$action[2]);
    -			else if($action[0]==='Page')
    -				call_user_func_array(array($page,$action[1]),$action[2]);
    -			else
    -				call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
    -		}
    -	}
    -
    -	/**
    -	 * Performs the PreRender step for the control and all its child controls.
    -	 * This method overrides the parent implementation by setting up
    -	 * the stack of the output cache in the page.
    -	 * Only framework developers should use this method.
    -	 * @param TControl the naming container control
    -	 */
    -	protected function preRenderRecursive()
    -	{
    -		if($this->_cacheAvailable && !$this->_dataCached)
    -		{
    -			$stack=$this->getPage()->getCachingStack();
    -			$stack->push($this);
    -			parent::preRenderRecursive();
    -			$stack->pop();
    -		}
    -		else
    -			parent::preRenderRecursive();
    -	}
    -
    -	/**
    -	 * Loads state (viewstate and controlstate) into a control and its children.
    -	 * This method overrides the parent implementation by loading
    -	 * cached state if available.
    -	 * This method should only be used by framework developers.
    -	 * @param array the collection of the state
    -	 * @param boolean whether the viewstate should be loaded
    -	 */
    -	protected function loadStateRecursive(&$state,$needViewState=true)
    -	{
    -		$st=unserialize($state);
    -		parent::loadStateRecursive($st,$needViewState);
    -	}
    -
    -	/**
    -	 * Saves all control state (viewstate and controlstate) as a collection.
    -	 * This method overrides the parent implementation by saving state
    -	 * into cache if needed.
    -	 * This method should only be used by framework developers.
    -	 * @param boolean whether the viewstate should be saved
    -	 * @return array the collection of the control state (including its children's state).
    -	 */
    -	protected function &saveStateRecursive($needViewState=true)
    -	{
    -		if($this->_dataCached)
    -			return $this->_state;
    -		else
    -		{
    -			$st=parent::saveStateRecursive($needViewState);
    -			// serialization is needed to avoid undefined classes when loading state
    -			$this->_state=serialize($st);
    -			return $this->_state;
    -		}
    -	}
    -
    -	/**
    -	 * Registers an action associated with the content being cached.
    -	 * The registered action will be replayed if the content stored
    -	 * in the cache is served to end-users.
    -	 * @param string context of the action method. This is a property-path
    -	 * referring to the context object (e.g. Page, Page.ClientScript)
    -	 * @param string method name of the context object
    -	 * @param array list of parameters to be passed to the action method
    -	 */
    -	public function registerAction($context,$funcName,$funcParams)
    -	{
    -		$this->_actions[]=array($context,$funcName,$funcParams);
    -	}
    -
    -	public function getCacheKey()
    -	{
    -		if($this->_cacheKey===null)
    -			$this->_cacheKey=$this->calculateCacheKey();
    -		return $this->_cacheKey;
    -	}
    -
    -	/**
    -	 * Calculates the cache key.
    -	 * The key is calculated based on the unique ID of this control
    -	 * and the request parameters specified via {@link setVaryByParam VaryByParam}.
    -	 * If {@link getVaryBySession VaryBySession} is true, the session ID
    -	 * will also participate in the key calculation.
    -	 * This method may be overriden to support other variations in
    -	 * the calculated cache key.
    -	 * @return string cache key
    -	 */
    -	protected function calculateCacheKey()
    -	{
    -		$key=$this->getBaseCacheKey();
    -		if($this->_varyBySession)
    -			$key.=$this->getSession()->getSessionID();
    -		if($this->_varyByParam!=='')
    -		{
    -			$params=array();
    -			$request=$this->getRequest();
    -			foreach(explode(',',$this->_varyByParam) as $name)
    -			{
    -				$name=trim($name);
    -				$params[$name]=$request->itemAt($name);
    -			}
    -			$key.=serialize($params);
    -		}
    -		$param=new TOutputCacheCalculateKeyEventParameter;
    -		$this->onCalculateKey($param);
    -		$key.=$param->getCacheKey();
    -		return $key;
    -	}
    -
    -	/**
    -	 * @return string basic cache key without variations
    -	 */
    -	protected function getBaseCacheKey()
    -	{
    -		return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID();
    -	}
    -
    -	/**
    -	 * @return string the ID of the cache module. Defaults to '', meaning the primary cache module is used.
    -	 */
    -	public function getCacheModuleID()
    -	{
    -		return $this->_cacheModuleID;
    -	}
    -
    -	/**
    -	 * @param string the ID of the cache module. If empty, the primary cache module will be used.
    -	 */
    -	public function setCacheModuleID($value)
    -	{
    -		$this->_cacheModuleID=$value;
    -	}
    -
    -	/**
    -	 * Sets the prefix of the cache key.
    -	 * This method is used internally by {@link TTemplate}.
    -	 * @param string key prefix
    -	 */
    -	public function setCacheKeyPrefix($value)
    -	{
    -		$this->_keyPrefix=$value;
    -	}
    -
    -	/**
    -	 * @return integer the timestamp of the cached content. This is only valid if the content is being cached.
    -	 * @since 3.1.1
    -	 */
    -	public function getCacheTime()
    -	{
    -		return $this->_cacheTime;
    -	}
    -
    -	/**
    -	 * Returns the dependency of the data to be cached.
    -	 * The default implementation simply returns null, meaning no specific dependency.
    -	 * This method may be overriden to associate the data to be cached
    -	 * with additional dependencies.
    -	 * @return ICacheDependency
    -	 */
    -	protected function getCacheDependency()
    -	{
    -		return null;
    -	}
    -
    -	/**
    -	 * @return boolean whether content enclosed is cached or not
    -	 */
    -	public function getContentCached()
    -	{
    -		return $this->_dataCached;
    -	}
    -
    -	/**
    -	 * @return integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
    -	 * Note, if cache dependency changes or cache space is limited,
    -	 * the data may be purged out of cache earlier.
    -	 */
    -	public function getDuration()
    -	{
    -		return $this->_duration;
    -	}
    -
    -	/**
    -	 * @param integer number of seconds that the data can remain in cache. If 0, it means data is not cached.
    -	 * @throws TInvalidDataValueException if the value is smaller than 0.
    -	 */
    -	public function setDuration($value)
    -	{
    -		if(($value=TPropertyValue::ensureInteger($value))<0)
    -			throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
    -		$this->_duration=$value;
    -	}
    -
    -	/**
    -	 * @return string a semicolon-separated list of strings used to vary the output cache. Defaults to ''.
    -	 */
    -	public function getVaryByParam()
    -	{
    -		return $this->_varyByParam;
    -	}
    -
    -	/**
    -	 * Sets the names of the request parameters that should be used in calculating the cache key.
    -	 * The names should be concatenated by semicolons.
    -	 * By setting this value, the output cache will use different cached data
    -	 * for each different set of request parameter values.
    -	 * @return string a semicolon-separated list of strings used to vary the output cache.
    -	 */
    -	public function setVaryByParam($value)
    -	{
    -		$this->_varyByParam=trim($value);
    -	}
    -
    -	/**
    -	 * @return boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
    -	 */
    -	public function getVaryBySession()
    -	{
    -		return $this->_varyBySession;
    -	}
    -
    -	/**
    -	 * @param boolean whether the content being cached should be differentiated according to user sessions.
    -	 */
    -	public function setVaryBySession($value)
    -	{
    -		$this->_varyBySession=TPropertyValue::ensureBoolean($value);
    -	}
    -
    -	/**
    -	 * @return boolean whether cached output will be used on postback requests. Defaults to false.
    -	 */
    -	public function getCachingPostBack()
    -	{
    -		return $this->_cachePostBack;
    -	}
    -
    -	/**
    -	 * Sets a value indicating whether cached output will be used on postback requests.
    -	 * By default, this is disabled. Be very cautious when enabling it.
    -	 * If the cached content including interactive user controls such as
    -	 * TTextBox, TDropDownList, your page may fail to render on postbacks.
    -	 * @param boolean whether cached output will be used on postback requests.
    -	 */
    -	public function setCachingPostBack($value)
    -	{
    -		$this->_cachePostBack=TPropertyValue::ensureBoolean($value);
    -	}
    -
    -	/**
    -	 * This event is raised when the output cache is checking cache dependency.
    -	 * An event handler may be written to check customized dependency conditions.
    -	 * The checking result should be saved by setting {@link TOutputCacheCheckDependencyEventParameter::setIsValid IsValid}
    -	 * property of the event parameter (which defaults to true).
    -	 * @param TOutputCacheCheckDependencyEventParameter event parameter
    -	 */
    -	public function onCheckDependency($param)
    -	{
    -		$this->raiseEvent('OnCheckDependency',$this,$param);
    -	}
    -
    -	/**
    -	 * This event is raised when the output cache is calculating cache key.
    -	 * By varying cache keys, one can obtain different versions of cached content.
    -	 * An event handler may be written to add variety of the key calculation.
    -	 * The value set in {@link TOutputCacheCalculateKeyEventParameter::setCacheKey CacheKey} of
    -	 * this event parameter will be appended to the default key calculation scheme.
    -	 * @param TOutputCacheCalculateKeyEventParameter event parameter
    -	 */
    -	public function onCalculateKey($param)
    -	{
    -		$this->raiseEvent('OnCalculateKey',$this,$param);
    -	}
    -
    -	/**
    -	 * Renders the output cache control.
    -	 * This method overrides the parent implementation by capturing the output
    -	 * from its child controls and saving it into cache, if output cache is needed.
    -	 * @param THtmlWriter
    -	 */
    -	public function render($writer)
    -	{
    -		if($this->_dataCached)
    -			$writer->write($this->_contents);
    -		else if($this->_cacheAvailable)
    -		{
    -			$textwriter = new TTextWriter();
    -			$multiwriter = new TOutputCacheTextWriterMulti(array($writer->getWriter(),$textwriter));
    -			$htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $multiwriter);
    -			
    -			$stack=$this->getPage()->getCachingStack();
    -			$stack->push($this);
    -			parent::render($htmlWriter);
    -			$stack->pop();
    -
    -			$content=$textwriter->flush();
    -			$data=array($content,$this->_state,$this->_actions,time());
    -			$this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
    -		}
    -		else
    -			parent::render($writer);
    -	}
    -}
    -
    -/**
    - * TOutputCacheCheckDependencyEventParameter class
    - *
    - * TOutputCacheCheckDependencyEventParameter encapsulates the parameter data for
    - * OnCheckDependency event of {@link TOutputCache} control.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TOutputCacheCheckDependencyEventParameter extends TEventParameter
    -{
    -	private $_isValid=true;
    -	private $_cacheTime=0;
    -
    -	/**
    -	 * @return boolean whether the dependency remains valid. Defaults to true.
    -	 */
    -	public function getIsValid()
    -	{
    -		return $this->_isValid;
    -	}
    -
    -	/**
    -	 * @param boolean whether the dependency remains valid
    -	 */
    -	public function setIsValid($value)
    -	{
    -		$this->_isValid=TPropertyValue::ensureBoolean($value);
    -	}
    -
    -	/**
    -	 * @return integer the timestamp of the cached result. You may use this to help determine any dependency is changed.
    -	 * @since 3.1.1
    -	 */
    -	public function getCacheTime()
    -	{
    -		return $this->_cacheTime;
    -	}
    -
    -	/**
    -	 * @param integer the timestamp of the cached result. This is used internally.
    -	 * @since 3.1.1
    -	 */
    -	public function setCacheTime($value)
    -	{
    -		$this->_cacheTime=TPropertyValue::ensureInteger($value);
    -	}
    -}
    -
    -
    -/**
    - * TOutputCacheCalculateKeyEventParameter class
    - *
    - * TOutputCacheCalculateKeyEventParameter encapsulates the parameter data for
    - * OnCalculateKey event of {@link TOutputCache} control.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TOutputCacheCalculateKeyEventParameter extends TEventParameter
    -{
    -	/**
    -	 * @var string cache key to be appended to the default calculation scheme.
    -	 */
    -	private $_cacheKey='';
    -
    -	/**
    -	 * @return string cache key to be appended to the default calculation scheme.
    -	 */
    -	public function getCacheKey()
    -	{
    -		return $this->_cacheKey;
    -	}
    -
    -	/**
    -	 * @param string cache key to be appended to the default calculation scheme
    -	 */
    -	public function setCacheKey($value)
    -	{
    -		$this->_cacheKey=TPropertyValue::ensureString($value);
    -	}
    -}
    -
    -/**
    - * TOutputCacheTextWriterMulti class
    - *
    - * TOutputCacheTextWriterMulti is an internal class used by
    - * TOutputCache to write simultaneously to multiple writers.
    - *
    - * @author Gabor Berczi, DevWorx Hungary 
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.2
    - */
    -class TOutputCacheTextWriterMulti extends TTextWriter
    -{
    -	protected $_writers;
    -
    -	public function __construct(Array $writers)
    -	{
    -		//parent::__construct();
    -		$this->_writers = $writers;
    -	}
    -	
    -	public function write($s)
    -	{
    -		foreach($this->_writers as $writer)
    -			$writer->write($s);
    -	}
    -
    -	public function flush()
    -	{
    -		foreach($this->_writers as $writer)
    -			$s = $writer->flush();
    -		return $s;
    -	}
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * TOutputCache class.
    + *
    + * TOutputCache enables caching a portion of a Web page, also known as
    + * partial caching. The content being cached can be either static or
    + * dynamic.
    + *
    + * To use TOutputCache, simply enclose the content to be cached
    + * within the TOutputCache component tag on a template, e.g.,
    + * 
    + * 
    + *   content to be cached
    + * 
    + * 
    + * where content to be cached can be static text and/or component tags.
    + *
    + * The validity of the cached content is determined based on two factors:
    + * the {@link setDuration Duration} and the cache dependency.
    + * The former specifies the number of seconds that the data can remain
    + * valid in cache (defaults to 60s), while the latter specifies conditions
    + * that the cached data depends on. If a dependency changes,
    + * (e.g. relevant data in DB are updated), the cached data will be invalidated.
    + *
    + * There are two ways to specify cache dependency. One may write event handlers
    + * to respond to the {@link onCheckDependency OnCheckDependency} event and set
    + * the event parameter's {@link TOutputCacheCheckDependencyEventParameter::getIsValid IsValid}
    + * property to indicate whether the cached data remains valid or not.
    + * One can also extend TOutputCache and override its {@link getCacheDependency}
    + * function. While the former is easier to use, the latter offers more extensibility.
    + *
    + * The content fetched from cache may be variated with respect to
    + * some parameters. It supports variation with respect to request parameters,
    + * which is specified by {@link setVaryByParam VaryByParam} property.
    + * If a specified request parameter is different, a different version of
    + * cached content is used. This is extremely useful if a page's content
    + * may be variated according to some GET parameters.
    + * The content being cached may also be variated with user sessions if
    + * {@link setVaryBySession VaryBySession} is set true.
    + * To variate the cached content by other factors, override {@link calculateCacheKey()} method.
    + *
    + * Output caches can be nested. An outer cache takes precedence over an
    + * inner cache. This means, if the content cached by the inner cache expires
    + * or is invalidated, while that by the outer cache not, the outer cached
    + * content will be used.
    + *
    + * Note, TOutputCache is effective only for non-postback page requests
    + * and when cache module is enabled.
    + *
    + * Do not attempt to address child controls of TOutputCache when the cached
    + * content is to be used. Use {@link getContentCached ContentCached} property
    + * to determine whether the content is cached or not.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.1
    + */
    +class TOutputCache extends TControl implements INamingContainer
    +{
    +	const CACHE_ID_PREFIX='prado:outputcache';
    +	private $_cacheModuleID='';
    +	private $_dataCached=false;
    +	private $_cacheAvailable=false;
    +	private $_cacheChecked=false;
    +	private $_cacheKey=null;
    +	private $_duration=60;
    +	private $_cache=null;
    +	private $_contents;
    +	private $_state;
    +	private $_actions=array();
    +	private $_varyByParam='';
    +	private $_keyPrefix='';
    +	private $_varyBySession=false;
    +	private $_cachePostBack=false;
    +	private $_cacheTime=0;
    +
    +	/**
    +	 * Returns a value indicating whether body contents are allowed for this control.
    +	 * This method overrides the parent implementation by checking if cached
    +	 * content is available or not. If yes, it returns false, otherwise true.
    +	 * @param boolean whether body contents are allowed for this control.
    +	 */
    +	public function getAllowChildControls()
    +	{
    +		$this->determineCacheability();
    +		return !$this->_dataCached;
    +	}
    +
    +	private function determineCacheability()
    +	{
    +		if(!$this->_cacheChecked)
    +		{
    +			$this->_cacheChecked=true;
    +			if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack()))
    +			{
    +				if($this->_cacheModuleID!=='')
    +				{
    +					$this->_cache=$this->getApplication()->getModule($this->_cacheModuleID);
    +					if(!($this->_cache instanceof ICache))
    +						throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID);
    +				}
    +				else
    +					$this->_cache=$this->getApplication()->getCache();
    +				if($this->_cache!==null)
    +				{
    +					$this->_cacheAvailable=true;
    +					$data=$this->_cache->get($this->getCacheKey());
    +					if(is_array($data))
    +					{
    +						$param=new TOutputCacheCheckDependencyEventParameter;
    +						$param->setCacheTime(isset($data[3])?$data[3]:0);
    +						$this->onCheckDependency($param);
    +						$this->_dataCached=$param->getIsValid();
    +					}
    +					else
    +						$this->_dataCached=false;
    +					if($this->_dataCached)
    +						list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data;
    +				}
    +			}
    +		}
    +	}
    +
    +	/**
    +	 * Performs the Init step for the control and all its child controls.
    +	 * This method overrides the parent implementation by setting up
    +	 * the stack of the output cache in the page.
    +	 * Only framework developers should use this method.
    +	 * @param TControl the naming container control
    +	 */
    +	protected function initRecursive($namingContainer=null)
    +	{
    +		if($this->_cacheAvailable && !$this->_dataCached)
    +		{
    +			$stack=$this->getPage()->getCachingStack();
    +			$stack->push($this);
    +			parent::initRecursive($namingContainer);
    +			$stack->pop();
    +		}
    +		else
    +			parent::initRecursive($namingContainer);
    +	}
    +
    +	/**
    +	 * Performs the Load step for the control and all its child controls.
    +	 * This method overrides the parent implementation by setting up
    +	 * the stack of the output cache in the page. If the data is restored
    +	 * from cache, it also recovers the actions associated with the cached data.
    +	 * Only framework developers should use this method.
    +	 * @param TControl the naming container control
    +	 */
    +	protected function loadRecursive()
    +	{
    +		if($this->_cacheAvailable && !$this->_dataCached)
    +		{
    +			$stack=$this->getPage()->getCachingStack();
    +			$stack->push($this);
    +			parent::loadRecursive();
    +			$stack->pop();
    +		}
    +		else
    +		{
    +			if($this->_dataCached)
    +				$this->performActions();
    +			parent::loadRecursive();
    +		}
    +	}
    +
    +	private function performActions()
    +	{
    +		$page=$this->getPage();
    +		$cs=$page->getClientScript();
    +		foreach($this->_actions as $action)
    +		{
    +			if($action[0]==='Page.ClientScript')
    +				call_user_func_array(array($cs,$action[1]),$action[2]);
    +			else if($action[0]==='Page')
    +				call_user_func_array(array($page,$action[1]),$action[2]);
    +			else
    +				call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
    +		}
    +	}
    +
    +	/**
    +	 * Performs the PreRender step for the control and all its child controls.
    +	 * This method overrides the parent implementation by setting up
    +	 * the stack of the output cache in the page.
    +	 * Only framework developers should use this method.
    +	 * @param TControl the naming container control
    +	 */
    +	protected function preRenderRecursive()
    +	{
    +		if($this->_cacheAvailable && !$this->_dataCached)
    +		{
    +			$stack=$this->getPage()->getCachingStack();
    +			$stack->push($this);
    +			parent::preRenderRecursive();
    +			$stack->pop();
    +		}
    +		else
    +			parent::preRenderRecursive();
    +	}
    +
    +	/**
    +	 * Loads state (viewstate and controlstate) into a control and its children.
    +	 * This method overrides the parent implementation by loading
    +	 * cached state if available.
    +	 * This method should only be used by framework developers.
    +	 * @param array the collection of the state
    +	 * @param boolean whether the viewstate should be loaded
    +	 */
    +	protected function loadStateRecursive(&$state,$needViewState=true)
    +	{
    +		$st=unserialize($state);
    +		parent::loadStateRecursive($st,$needViewState);
    +	}
    +
    +	/**
    +	 * Saves all control state (viewstate and controlstate) as a collection.
    +	 * This method overrides the parent implementation by saving state
    +	 * into cache if needed.
    +	 * This method should only be used by framework developers.
    +	 * @param boolean whether the viewstate should be saved
    +	 * @return array the collection of the control state (including its children's state).
    +	 */
    +	protected function &saveStateRecursive($needViewState=true)
    +	{
    +		if($this->_dataCached)
    +			return $this->_state;
    +		else
    +		{
    +			$st=parent::saveStateRecursive($needViewState);
    +			// serialization is needed to avoid undefined classes when loading state
    +			$this->_state=serialize($st);
    +			return $this->_state;
    +		}
    +	}
    +
    +	/**
    +	 * Registers an action associated with the content being cached.
    +	 * The registered action will be replayed if the content stored
    +	 * in the cache is served to end-users.
    +	 * @param string context of the action method. This is a property-path
    +	 * referring to the context object (e.g. Page, Page.ClientScript)
    +	 * @param string method name of the context object
    +	 * @param array list of parameters to be passed to the action method
    +	 */
    +	public function registerAction($context,$funcName,$funcParams)
    +	{
    +		$this->_actions[]=array($context,$funcName,$funcParams);
    +	}
    +
    +	public function getCacheKey()
    +	{
    +		if($this->_cacheKey===null)
    +			$this->_cacheKey=$this->calculateCacheKey();
    +		return $this->_cacheKey;
    +	}
    +
    +	/**
    +	 * Calculates the cache key.
    +	 * The key is calculated based on the unique ID of this control
    +	 * and the request parameters specified via {@link setVaryByParam VaryByParam}.
    +	 * If {@link getVaryBySession VaryBySession} is true, the session ID
    +	 * will also participate in the key calculation.
    +	 * This method may be overriden to support other variations in
    +	 * the calculated cache key.
    +	 * @return string cache key
    +	 */
    +	protected function calculateCacheKey()
    +	{
    +		$key=$this->getBaseCacheKey();
    +		if($this->_varyBySession)
    +			$key.=$this->getSession()->getSessionID();
    +		if($this->_varyByParam!=='')
    +		{
    +			$params=array();
    +			$request=$this->getRequest();
    +			foreach(explode(',',$this->_varyByParam) as $name)
    +			{
    +				$name=trim($name);
    +				$params[$name]=$request->itemAt($name);
    +			}
    +			$key.=serialize($params);
    +		}
    +		$param=new TOutputCacheCalculateKeyEventParameter;
    +		$this->onCalculateKey($param);
    +		$key.=$param->getCacheKey();
    +		return $key;
    +	}
    +
    +	/**
    +	 * @return string basic cache key without variations
    +	 */
    +	protected function getBaseCacheKey()
    +	{
    +		return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID();
    +	}
    +
    +	/**
    +	 * @return string the ID of the cache module. Defaults to '', meaning the primary cache module is used.
    +	 */
    +	public function getCacheModuleID()
    +	{
    +		return $this->_cacheModuleID;
    +	}
    +
    +	/**
    +	 * @param string the ID of the cache module. If empty, the primary cache module will be used.
    +	 */
    +	public function setCacheModuleID($value)
    +	{
    +		$this->_cacheModuleID=$value;
    +	}
    +
    +	/**
    +	 * Sets the prefix of the cache key.
    +	 * This method is used internally by {@link TTemplate}.
    +	 * @param string key prefix
    +	 */
    +	public function setCacheKeyPrefix($value)
    +	{
    +		$this->_keyPrefix=$value;
    +	}
    +
    +	/**
    +	 * @return integer the timestamp of the cached content. This is only valid if the content is being cached.
    +	 * @since 3.1.1
    +	 */
    +	public function getCacheTime()
    +	{
    +		return $this->_cacheTime;
    +	}
    +
    +	/**
    +	 * Returns the dependency of the data to be cached.
    +	 * The default implementation simply returns null, meaning no specific dependency.
    +	 * This method may be overriden to associate the data to be cached
    +	 * with additional dependencies.
    +	 * @return ICacheDependency
    +	 */
    +	protected function getCacheDependency()
    +	{
    +		return null;
    +	}
    +
    +	/**
    +	 * @return boolean whether content enclosed is cached or not
    +	 */
    +	public function getContentCached()
    +	{
    +		return $this->_dataCached;
    +	}
    +
    +	/**
    +	 * @return integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
    +	 * Note, if cache dependency changes or cache space is limited,
    +	 * the data may be purged out of cache earlier.
    +	 */
    +	public function getDuration()
    +	{
    +		return $this->_duration;
    +	}
    +
    +	/**
    +	 * @param integer number of seconds that the data can remain in cache. If 0, it means data is not cached.
    +	 * @throws TInvalidDataValueException if the value is smaller than 0.
    +	 */
    +	public function setDuration($value)
    +	{
    +		if(($value=TPropertyValue::ensureInteger($value))<0)
    +			throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
    +		$this->_duration=$value;
    +	}
    +
    +	/**
    +	 * @return string a semicolon-separated list of strings used to vary the output cache. Defaults to ''.
    +	 */
    +	public function getVaryByParam()
    +	{
    +		return $this->_varyByParam;
    +	}
    +
    +	/**
    +	 * Sets the names of the request parameters that should be used in calculating the cache key.
    +	 * The names should be concatenated by semicolons.
    +	 * By setting this value, the output cache will use different cached data
    +	 * for each different set of request parameter values.
    +	 * @return string a semicolon-separated list of strings used to vary the output cache.
    +	 */
    +	public function setVaryByParam($value)
    +	{
    +		$this->_varyByParam=trim($value);
    +	}
    +
    +	/**
    +	 * @return boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
    +	 */
    +	public function getVaryBySession()
    +	{
    +		return $this->_varyBySession;
    +	}
    +
    +	/**
    +	 * @param boolean whether the content being cached should be differentiated according to user sessions.
    +	 */
    +	public function setVaryBySession($value)
    +	{
    +		$this->_varyBySession=TPropertyValue::ensureBoolean($value);
    +	}
    +
    +	/**
    +	 * @return boolean whether cached output will be used on postback requests. Defaults to false.
    +	 */
    +	public function getCachingPostBack()
    +	{
    +		return $this->_cachePostBack;
    +	}
    +
    +	/**
    +	 * Sets a value indicating whether cached output will be used on postback requests.
    +	 * By default, this is disabled. Be very cautious when enabling it.
    +	 * If the cached content including interactive user controls such as
    +	 * TTextBox, TDropDownList, your page may fail to render on postbacks.
    +	 * @param boolean whether cached output will be used on postback requests.
    +	 */
    +	public function setCachingPostBack($value)
    +	{
    +		$this->_cachePostBack=TPropertyValue::ensureBoolean($value);
    +	}
    +
    +	/**
    +	 * This event is raised when the output cache is checking cache dependency.
    +	 * An event handler may be written to check customized dependency conditions.
    +	 * The checking result should be saved by setting {@link TOutputCacheCheckDependencyEventParameter::setIsValid IsValid}
    +	 * property of the event parameter (which defaults to true).
    +	 * @param TOutputCacheCheckDependencyEventParameter event parameter
    +	 */
    +	public function onCheckDependency($param)
    +	{
    +		$this->raiseEvent('OnCheckDependency',$this,$param);
    +	}
    +
    +	/**
    +	 * This event is raised when the output cache is calculating cache key.
    +	 * By varying cache keys, one can obtain different versions of cached content.
    +	 * An event handler may be written to add variety of the key calculation.
    +	 * The value set in {@link TOutputCacheCalculateKeyEventParameter::setCacheKey CacheKey} of
    +	 * this event parameter will be appended to the default key calculation scheme.
    +	 * @param TOutputCacheCalculateKeyEventParameter event parameter
    +	 */
    +	public function onCalculateKey($param)
    +	{
    +		$this->raiseEvent('OnCalculateKey',$this,$param);
    +	}
    +
    +	/**
    +	 * Renders the output cache control.
    +	 * This method overrides the parent implementation by capturing the output
    +	 * from its child controls and saving it into cache, if output cache is needed.
    +	 * @param THtmlWriter
    +	 */
    +	public function render($writer)
    +	{
    +		if($this->_dataCached)
    +			$writer->write($this->_contents);
    +		else if($this->_cacheAvailable)
    +		{
    +			$textwriter = new TTextWriter();
    +			$multiwriter = new TOutputCacheTextWriterMulti(array($writer->getWriter(),$textwriter));
    +			$htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $multiwriter);
    +			
    +			$stack=$this->getPage()->getCachingStack();
    +			$stack->push($this);
    +			parent::render($htmlWriter);
    +			$stack->pop();
    +
    +			$content=$textwriter->flush();
    +			$data=array($content,$this->_state,$this->_actions,time());
    +			$this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
    +		}
    +		else
    +			parent::render($writer);
    +	}
    +}
    +
    +/**
    + * TOutputCacheCheckDependencyEventParameter class
    + *
    + * TOutputCacheCheckDependencyEventParameter encapsulates the parameter data for
    + * OnCheckDependency event of {@link TOutputCache} control.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TOutputCacheCheckDependencyEventParameter extends TEventParameter
    +{
    +	private $_isValid=true;
    +	private $_cacheTime=0;
    +
    +	/**
    +	 * @return boolean whether the dependency remains valid. Defaults to true.
    +	 */
    +	public function getIsValid()
    +	{
    +		return $this->_isValid;
    +	}
    +
    +	/**
    +	 * @param boolean whether the dependency remains valid
    +	 */
    +	public function setIsValid($value)
    +	{
    +		$this->_isValid=TPropertyValue::ensureBoolean($value);
    +	}
    +
    +	/**
    +	 * @return integer the timestamp of the cached result. You may use this to help determine any dependency is changed.
    +	 * @since 3.1.1
    +	 */
    +	public function getCacheTime()
    +	{
    +		return $this->_cacheTime;
    +	}
    +
    +	/**
    +	 * @param integer the timestamp of the cached result. This is used internally.
    +	 * @since 3.1.1
    +	 */
    +	public function setCacheTime($value)
    +	{
    +		$this->_cacheTime=TPropertyValue::ensureInteger($value);
    +	}
    +}
    +
    +
    +/**
    + * TOutputCacheCalculateKeyEventParameter class
    + *
    + * TOutputCacheCalculateKeyEventParameter encapsulates the parameter data for
    + * OnCalculateKey event of {@link TOutputCache} control.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TOutputCacheCalculateKeyEventParameter extends TEventParameter
    +{
    +	/**
    +	 * @var string cache key to be appended to the default calculation scheme.
    +	 */
    +	private $_cacheKey='';
    +
    +	/**
    +	 * @return string cache key to be appended to the default calculation scheme.
    +	 */
    +	public function getCacheKey()
    +	{
    +		return $this->_cacheKey;
    +	}
    +
    +	/**
    +	 * @param string cache key to be appended to the default calculation scheme
    +	 */
    +	public function setCacheKey($value)
    +	{
    +		$this->_cacheKey=TPropertyValue::ensureString($value);
    +	}
    +}
    +
    +/**
    + * TOutputCacheTextWriterMulti class
    + *
    + * TOutputCacheTextWriterMulti is an internal class used by
    + * TOutputCache to write simultaneously to multiple writers.
    + *
    + * @author Gabor Berczi, DevWorx Hungary 
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.2
    + */
    +class TOutputCacheTextWriterMulti extends TTextWriter
    +{
    +	protected $_writers;
    +
    +	public function __construct(Array $writers)
    +	{
    +		//parent::__construct();
    +		$this->_writers = $writers;
    +	}
    +	
    +	public function write($s)
    +	{
    +		foreach($this->_writers as $writer)
    +			$writer->write($s);
    +	}
    +
    +	public function flush()
    +	{
    +		foreach($this->_writers as $writer)
    +			$s = $writer->flush();
    +		return $s;
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TPager.php b/framework/Web/UI/WebControls/TPager.php
    index 8e4bf2b0..11de5233 100644
    --- a/framework/Web/UI/WebControls/TPager.php
    +++ b/framework/Web/UI/WebControls/TPager.php
    @@ -1,792 +1,792 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * TPager class.
    - *
    - * TPager creates a pager that provides UI for end-users to interactively
    - * specify which page of data to be rendered in a {@link TDataBoundControl}-derived control,
    - * such as {@link TDataList}, {@link TRepeater}, {@link TCheckBoxList}, etc.
    - * The target data-bound control is specified by {@link setControlToPaginate ControlToPaginate},
    - * which must be the ID path of the target control reaching from the pager's
    - * naming container. Note, the target control must have its {@link TDataBoundControl::setAllowPaging AllowPaging}
    - * set to true.
    - *
    - * TPager can display three different UIs, specified via {@link setMode Mode}:
    - * - NextPrev: a next page and a previous page button are rendered.
    - * - Numeric: a list of page index buttons are rendered.
    - * - List: a dropdown list of page indices are rendered.
    - *
    - * When the pager mode is either NextPrev or Numeric, the paging buttons may be displayed
    - * in three types by setting {@link setButtonType ButtonType}:
    - * - LinkButton: a hyperlink button
    - * - PushButton: a normal button
    - * - ImageButton: an image button (please set XXXPageImageUrl properties accordingly to specify the button images.)
    - *
    - * TPager raises an {@link onPageIndexChanged OnPageIndexChanged} event when
    - * the end-user interacts with it and specifies a new page (e.g. clicking
    - * on a page button that leads to a new page.) The new page index may be obtained
    - * from the event parameter's property {@link TPagerPageChangedEventParameter::getNewPageIndex NewPageIndex}.
    - * Normally, in the event handler, one can set the {@link TDataBoundControl::getCurrentPageIndex CurrentPageIndex}
    - * to this new page index so that the new page of data is rendered.
    - *
    - * Multiple pagers can be associated with the same data-bound control.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.2
    - */
    -class TPager extends TWebControl implements INamingContainer
    -{
    -	/**
    -	 * Command name that TPager understands.
    -	 */
    -	const CMD_PAGE='Page';
    -	const CMD_PAGE_NEXT='Next';
    -	const CMD_PAGE_PREV='Previous';
    -	const CMD_PAGE_FIRST='First';
    -	const CMD_PAGE_LAST='Last';
    -
    -	private $_pageCount=0;
    -
    -	/**
    -	 * Restores the pager state.
    -	 * This method overrides the parent implementation and is invoked when
    -	 * the control is loading persistent state.
    -	 */
    -	public function loadState()
    -	{
    -		parent::loadState();
    -		if($this->getEnableViewState(true))
    -		{
    -			$this->getControls()->clear();
    -			$this->buildPager();
    -		}
    -	}
    -
    -	/**
    -	 * @return string the ID path of the control whose content would be paginated.
    -	 */
    -	public function getControlToPaginate()
    -	{
    -		return $this->getViewState('ControlToPaginate','');
    -	}
    -
    -	/**
    -	 * Sets the ID path of the control whose content would be paginated.
    -	 * The ID path is the dot-connected IDs of the controls reaching from
    -	 * the pager's naming container to the target control.
    -	 * @param string the ID path
    -	 */
    -	public function setControlToPaginate($value)
    -	{
    -		$this->setViewState('ControlToPaginate',$value,'');
    -	}
    -
    -	/**
    -	 * @return TPagerMode pager mode. Defaults to TPagerMode::NextPrev.
    -	 */
    -	public function getMode()
    -	{
    -		return $this->getViewState('Mode',TPagerMode::NextPrev);
    -	}
    -
    -	/**
    -	 * @param TPagerMode pager mode.
    -	 */
    -	public function setMode($value)
    -	{
    -		$this->setViewState('Mode',TPropertyValue::ensureEnum($value,'TPagerMode'),TPagerMode::NextPrev);
    -	}
    -
    -	/**
    -	 * @return TPagerButtonType the type of command button for paging. Defaults to TPagerButtonType::LinkButton.
    -	 */
    -	public function getButtonType()
    -	{
    -		return $this->getViewState('ButtonType',TPagerButtonType::LinkButton);
    -	}
    -
    -	/**
    -	 * @param TPagerButtonType the type of command button for paging.
    -	 */
    -	public function setButtonType($value)
    -	{
    -		$this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TPagerButtonType'),TPagerButtonType::LinkButton);
    -	}
    -
    -	/**
    -	 * @return string text for the next page button. Defaults to '>'.
    -	 */
    -	public function getNextPageText()
    -	{
    -		return $this->getViewState('NextPageText','>');
    -	}
    -
    -	/**
    -	 * @param string text for the next page button.
    -	 */
    -	public function setNextPageText($value)
    -	{
    -		$this->setViewState('NextPageText',$value,'>');
    -	}
    -
    -	/**
    -	 * @return string text for the previous page button. Defaults to '<'.
    -	 */
    -	public function getPrevPageText()
    -	{
    -		return $this->getViewState('PrevPageText','<');
    -	}
    -
    -	/**
    -	 * @param string text for the next page button.
    -	 */
    -	public function setPrevPageText($value)
    -	{
    -		$this->setViewState('PrevPageText',$value,'<');
    -	}
    -
    -	/**
    -	 * @return string text for the first page button. Defaults to '<<'.
    -	 */
    -	public function getFirstPageText()
    -	{
    -		return $this->getViewState('FirstPageText','<<');
    -	}
    -
    -	/**
    -	 * @param string text for the first page button. If empty, the first page button will not be rendered.
    -	 */
    -	public function setFirstPageText($value)
    -	{
    -		$this->setViewState('FirstPageText',$value,'<<');
    -	}
    -
    -	/**
    -	 * @return string text for the last page button. Defaults to '>>'.
    -	 */
    -	public function getLastPageText()
    -	{
    -		return $this->getViewState('LastPageText','>>');
    -	}
    -
    -	/**
    -	 * @param string text for the last page button. If empty, the last page button will not be rendered.
    -	 */
    -	public function setLastPageText($value)
    -	{
    -		$this->setViewState('LastPageText',$value,'>>');
    -	}
    -
    -	/**
    -	 * @return string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function getFirstPageImageUrl()
    -	{
    -		return $this->getViewState('FirstPageImageUrl','');
    -	}
    -
    -	/**
    -	 * @param string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function setFirstPageImageUrl($value)
    -	{
    -		$this->setViewState('FirstPageImageUrl',$value);
    -	}
    -
    -	/**
    -	 * @return string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function getLastPageImageUrl()
    -	{
    -		return $this->getViewState('LastPageImageUrl','');
    -	}
    -
    -	/**
    -	 * @param string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function setLastPageImageUrl($value)
    -	{
    -		$this->setViewState('LastPageImageUrl',$value);
    -	}
    -
    -	/**
    -	 * @return string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function getNextPageImageUrl()
    -	{
    -		return $this->getViewState('NextPageImageUrl','');
    -	}
    -
    -	/**
    -	 * @param string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function setNextPageImageUrl($value)
    -	{
    -		$this->setViewState('NextPageImageUrl',$value);
    -	}
    -
    -	/**
    -	 * @return string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function getPrevPageImageUrl()
    -	{
    -		return $this->getViewState('PrevPageImageUrl','');
    -	}
    -
    -	/**
    -	 * @param string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    -	 * @since 3.1.1
    -	 */
    -	public function setPrevPageImageUrl($value)
    -	{
    -		$this->setViewState('PrevPageImageUrl',$value);
    -	}
    -
    -	/**
    -	 * @return string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
    -	 * @see setNumericPageImageUrl
    -	 * @since 3.1.1
    -	 */
    -	public function getNumericPageImageUrl()
    -	{
    -		return $this->getViewState('NumericPageImageUrl','');
    -	}
    -
    -	/**
    -	 * Sets the image URL for the numeric page buttons.
    -	 * This is actually a template for generating a set of URLs corresponding to numeric button 1, 2, 3, .., etc.
    -	 * Use {0} as the placeholder for the numbers.
    -	 * For example, the image URL http://example.com/images/button{0}.gif
    -	 * will be replaced as http://example.com/images/button1.gif, http://example.com/images/button2.gif, etc.
    -	 * @param string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
    -	 * @since 3.1.1
    -	 */
    -	public function setNumericPageImageUrl($value)
    -	{
    -		$this->setViewState('NumericPageImageUrl',$value);
    -	}
    -
    -	/**
    -	 * @return integer maximum number of pager buttons to be displayed. Defaults to 10.
    -	 */
    -	public function getPageButtonCount()
    -	{
    -		return $this->getViewState('PageButtonCount',10);
    -	}
    -
    -	/**
    -	 * @param integer maximum number of pager buttons to be displayed
    -	 * @throws TInvalidDataValueException if the value is less than 1.
    -	 */
    -	public function setPageButtonCount($value)
    -	{
    -		if(($value=TPropertyValue::ensureInteger($value))<1)
    -			throw new TInvalidDataValueException('pager_pagebuttoncount_invalid');
    -		$this->setViewState('PageButtonCount',$value,10);
    -	}
    -
    -	/**
    -	 * @return integer the zero-based index of the current page. Defaults to 0.
    -	 */
    -	public function getCurrentPageIndex()
    -	{
    -		return $this->getViewState('CurrentPageIndex',0);
    -	}
    -
    -	/**
    -	 * @param integer the zero-based index of the current page
    -	 * @throws TInvalidDataValueException if the value is less than 0
    -	 */
    -	protected function setCurrentPageIndex($value)
    -	{
    -		if(($value=TPropertyValue::ensureInteger($value))<0)
    -			throw new TInvalidDataValueException('pager_currentpageindex_invalid');
    -		$this->setViewState('CurrentPageIndex',$value,0);
    -	}
    -
    -	/**
    -	 * @return integer number of pages of data items available
    -	 */
    -	public function getPageCount()
    -	{
    -		return $this->getViewState('PageCount',0);
    -	}
    -
    -	/**
    -	 * @param integer number of pages of data items available
    -	 * @throws TInvalidDataValueException if the value is less than 0
    -	 */
    -	protected function setPageCount($value)
    -	{
    -		if(($value=TPropertyValue::ensureInteger($value))<0)
    -			throw new TInvalidDataValueException('pager_pagecount_invalid');
    -		$this->setViewState('PageCount',$value,0);
    -	}
    -
    -	/**
    -	 * @return boolean whether the current page is the first page Defaults to false.
    -	 */
    -	public function getIsFirstPage()
    -	{
    -		return $this->getCurrentPageIndex()===0;
    -	}
    -
    -	/**
    -	 * @return boolean whether the current page is the last page
    -	 */
    -	public function getIsLastPage()
    -	{
    -		return $this->getCurrentPageIndex()===$this->getPageCount()-1;
    -	}
    -
    -	/**
    -	 * Performs databinding to populate data items from data source.
    -	 * This method is invoked by {@link dataBind()}.
    -	 * You may override this function to provide your own way of data population.
    -	 * @param Traversable the bound data
    -	 */
    -	public function onPreRender($param)
    -	{
    -		parent::onPreRender($param);
    -
    -		$controlID=$this->getControlToPaginate();
    -		if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl))
    -			throw new TConfigurationException('pager_controltopaginate_invalid',$controlID);
    -
    -		if($targetControl->getAllowPaging())
    -		{
    -	 		$this->_pageCount=$targetControl->getPageCount();
    -			$this->getControls()->clear();
    -			$this->setPageCount($targetControl->getPageCount());
    -			$this->setCurrentPageIndex($targetControl->getCurrentPageIndex());
    -			$this->buildPager();
    -		}
    -		else
    -			$this->_pageCount=0;
    -	}
    -
    -	/**
    -	 * Renders the control.
    -	 * The method overrides the parent implementation by rendering
    -	 * the pager only when there are two or more pages.
    -	 * @param THtmlWriter the writer
    -	 */
    -	public function render($writer)
    -	{
    -		if($this->_pageCount>1)
    -			parent::render($writer);
    -	}
    -
    -	/**
    -	 * Builds the pager content based on the pager mode.
    -	 * Current implementation includes building 'NextPrev', 'Numeric' and 'DropDownList' pagers.
    -	 * Derived classes may override this method to provide additional pagers.
    -	 */
    -	protected function buildPager()
    -	{
    -		switch($this->getMode())
    -		{
    -			case TPagerMode::NextPrev:
    -				$this->buildNextPrevPager();
    -				break;
    -			case TPagerMode::Numeric:
    -				$this->buildNumericPager();
    -				break;
    -			case TPagerMode::DropDownList:
    -				$this->buildListPager();
    -				break;
    -		}
    -	}
    -
    -	/**
    -	 * Creates a pager button.
    -	 * Depending on the button type, a TLinkButton or a TButton may be created.
    -	 * If it is enabled (clickable), its command name and parameter will also be set.
    -	 * Derived classes may override this method to create additional types of buttons, such as TImageButton.
    -	 * @param string button type, either LinkButton or PushButton
    -	 * @param boolean whether the button should be enabled
    -	 * @param string caption of the button.
    -	 * @param string CommandName corresponding to the OnCommand event of the button.
    -	 * @param string CommandParameter corresponding to the OnCommand event of the button
    -	 * @return mixed the button instance
    -	 */
    -	protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter)
    -	{
    -		if($buttonType===TPagerButtonType::LinkButton)
    -		{
    -			if($enabled)
    -				$button=new TLinkButton;
    -			else
    -			{
    -				$button=new TLabel;
    -				$button->setText($text);
    -				return $button;
    -			}
    -		}
    -		else
    -		{
    -			if($buttonType===TPagerButtonType::ImageButton)
    -			{
    -				$button=new TImageButton;
    -				$button->setImageUrl($this->getPageImageUrl($text,$commandName));
    -			}
    -			else
    -				$button=new TButton;
    -			if(!$enabled)
    -				$button->setEnabled(false);
    -		}
    -		$button->setText($text);
    -		$button->setCommandName($commandName);
    -		$button->setCommandParameter($commandParameter);
    -		$button->setCausesValidation(false);
    -		return $button;
    -	}
    -
    -	/**
    -	 * @param string the caption of the image button
    -	 * @param string the command name associated with the image button
    -	 * @since 3.1.1
    -	 */
    -	protected function getPageImageUrl($text,$commandName)
    -	{
    -		switch($commandName)
    -		{
    -			case self::CMD_PAGE:
    -				$url=$this->getNumericPageImageUrl();
    -				return str_replace('{0}',$text,$url);
    -			case self::CMD_PAGE_NEXT:
    -				return $this->getNextPageImageUrl();
    -			case self::CMD_PAGE_PREV:
    -				return $this->getPrevPageImageUrl();
    -			case self::CMD_PAGE_FIRST:
    -				return $this->getFirstPageImageUrl();
    -			case self::CMD_PAGE_LAST:
    -				return $this->getLastPageImageUrl();
    -			default:
    -				return '';
    -		}
    -	}
    -
    -	/**
    -	 * Builds a next-prev pager
    -	 */
    -	protected function buildNextPrevPager()
    -	{
    -		$buttonType=$this->getButtonType();
    -		$controls=$this->getControls();
    -		if($this->getIsFirstPage())
    -		{
    -			if(($text=$this->getFirstPageText())!=='')
    -			{
    -				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_FIRST,'');
    -				$controls->add($label);
    -				$controls->add("\n");
    -			}
    -			$label=$this->createPagerButton($buttonType,false,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
    -			$controls->add($label);
    -		}
    -		else
    -		{
    -			if(($text=$this->getFirstPageText())!=='')
    -			{
    -				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
    -				$controls->add($button);
    -				$controls->add("\n");
    -			}
    -			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
    -			$controls->add($button);
    -		}
    -		$controls->add("\n");
    -		if($this->getIsLastPage())
    -		{
    -			$label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
    -			$controls->add($label);
    -			if(($text=$this->getLastPageText())!=='')
    -			{
    -				$controls->add("\n");
    -				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_LAST,'');
    -				$controls->add($label);
    -			}
    -		}
    -		else
    -		{
    -			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
    -			$controls->add($button);
    -			if(($text=$this->getLastPageText())!=='')
    -			{
    -				$controls->add("\n");
    -				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
    -				$controls->add($button);
    -			}
    -		}
    -	}
    -
    -	/**
    -	 * Builds a numeric pager
    -	 */
    -	protected function buildNumericPager()
    -	{
    -		$buttonType=$this->getButtonType();
    -		$controls=$this->getControls();
    -		$pageCount=$this->getPageCount();
    -		$pageIndex=$this->getCurrentPageIndex()+1;
    -		$maxButtonCount=$this->getPageButtonCount();
    -		$buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount;
    -		$startPageIndex=1;
    -		$endPageIndex=$buttonCount;
    -		if($pageIndex>$endPageIndex)
    -		{
    -			$startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1;
    -			if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount)
    -				$endPageIndex=$pageCount;
    -			if($endPageIndex-$startPageIndex+1<$maxButtonCount)
    -			{
    -				if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1)
    -					$startPageIndex=1;
    -			}
    -		}
    -
    -		if($startPageIndex>1)
    -		{
    -			if(($text=$this->getFirstPageText())!=='')
    -			{
    -				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
    -				$controls->add($button);
    -				$controls->add("\n");
    -			}
    -			$prevPageIndex=$startPageIndex-1;
    -			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex");
    -			$controls->add($button);
    -			$controls->add("\n");
    -		}
    -
    -		for($i=$startPageIndex;$i<=$endPageIndex;++$i)
    -		{
    -			if($i===$pageIndex)
    -			{
    -				$label=$this->createPagerButton($buttonType,false,"$i",self::CMD_PAGE,'');
    -				$controls->add($label);
    -			}
    -			else
    -			{
    -				$button=$this->createPagerButton($buttonType,true,"$i",self::CMD_PAGE,"$i");
    -				$controls->add($button);
    -			}
    -			if($i<$endPageIndex)
    -				$controls->add("\n");
    -		}
    -
    -		if($pageCount>$endPageIndex)
    -		{
    -			$controls->add("\n");
    -			$nextPageIndex=$endPageIndex+1;
    -			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE,"$nextPageIndex");
    -			$controls->add($button);
    -			if(($text=$this->getLastPageText())!=='')
    -			{
    -				$controls->add("\n");
    -				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
    -				$controls->add($button);
    -			}
    -		}
    -	}
    -
    -	/**
    -	 * Builds a dropdown list pager
    -	 */
    -	protected function buildListPager()
    -	{
    -		$list=new TDropDownList;
    -		$this->getControls()->add($list);
    -		$list->setDataSource(range(1,$this->getPageCount()));
    -		$list->dataBind();
    -		$list->setSelectedIndex($this->getCurrentPageIndex());
    -		$list->setAutoPostBack(true);
    -		$list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged'));
    -	}
    -
    -	/**
    -	 * Event handler to the OnSelectedIndexChanged event of the dropdown list.
    -	 * This handler will raise {@link onPageIndexChanged OnPageIndexChanged} event.
    -	 * @param TDropDownList the dropdown list control raising the event
    -	 * @param TEventParameter event parameter
    -	 */
    -	public function listIndexChanged($sender,$param)
    -	{
    -		$pageIndex=$sender->getSelectedIndex();
    -		$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    -	}
    -
    -	/**
    -	 * This event is raised when page index is changed due to a page button click.
    -	 * @param TPagerPageChangedEventParameter event parameter
    -	 */
    -	public function onPageIndexChanged($param)
    -	{
    -		$this->raiseEvent('OnPageIndexChanged',$this,$param);
    -	}
    -
    -	/**
    -	 * Processes a bubbled event.
    -	 * This method overrides parent's implementation by wrapping event parameter
    -	 * for OnCommand event with item information.
    -	 * @param TControl the sender of the event
    -	 * @param TEventParameter event parameter
    -	 * @return boolean whether the event bubbling should stop here.
    -	 */
    -	public function bubbleEvent($sender,$param)
    -	{
    -		if($param instanceof TCommandEventParameter)
    -		{
    -			$command=$param->getCommandName();
    -			if(strcasecmp($command,self::CMD_PAGE)===0)
    -			{
    -				$pageIndex=TPropertyValue::ensureInteger($param->getCommandParameter())-1;
    -				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    -				return true;
    -			}
    -			else if(strcasecmp($command,self::CMD_PAGE_NEXT)===0)
    -			{
    -				$pageIndex=$this->getCurrentPageIndex()+1;
    -				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    -				return true;
    -			}
    -			else if(strcasecmp($command,self::CMD_PAGE_PREV)===0)
    -			{
    -				$pageIndex=$this->getCurrentPageIndex()-1;
    -				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    -				return true;
    -			}
    -			else if(strcasecmp($command,self::CMD_PAGE_FIRST)===0)
    -			{
    -				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,0));
    -				return true;
    -			}
    -			else if(strcasecmp($command,self::CMD_PAGE_LAST)===0)
    -			{
    -				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$this->getPageCount()-1));
    -				return true;
    -			}
    -			return false;
    -		}
    -		else
    -			return false;
    -	}
    -}
    -
    -/**
    - * TPagerPageChangedEventParameter class
    - *
    - * TPagerPageChangedEventParameter encapsulates the parameter data for
    - * {@link TPager::onPageIndexChanged PageIndexChanged} event of {@link TPager} controls.
    - *
    - * The {@link getCommandSource CommandSource} property refers to the control
    - * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex}
    - * returns the new page index carried with the page command.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.2
    - */
    -class TPagerPageChangedEventParameter extends TEventParameter
    -{
    -	/**
    -	 * @var integer new page index
    -	 */
    -	private $_newIndex;
    -	/**
    -	 * @var TControl original event sender
    -	 */
    -	private $_source=null;
    -
    -	/**
    -	 * Constructor.
    -	 * @param TControl the control originally raises the OnCommand event.
    -	 * @param integer new page index
    -	 */
    -	public function __construct($source,$newPageIndex)
    -	{
    -		$this->_source=$source;
    -		$this->_newIndex=$newPageIndex;
    -	}
    -
    -	/**
    -	 * @return TControl the control originally raises the OnCommand event.
    -	 */
    -	public function getCommandSource()
    -	{
    -		return $this->_source;
    -	}
    -
    -	/**
    -	 * @return integer new page index
    -	 */
    -	public function getNewPageIndex()
    -	{
    -		return $this->_newIndex;
    -	}
    -}
    -
    -
    -/**
    - * TPagerMode class.
    - * TPagerMode defines the enumerable type for the possible modes that a {@link TPager} control can take.
    - *
    - * The following enumerable values are defined:
    - * - NextPrev: pager buttons are displayed as next and previous pages
    - * - Numeric: pager buttons are displayed as numeric page numbers
    - * - DropDownList: a dropdown list is used to select pages
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.4
    - */
    -class TPagerMode extends TEnumerable
    -{
    -	const NextPrev='NextPrev';
    -	const Numeric='Numeric';
    -	const DropDownList='DropDownList';
    -}
    -
    -
    -/**
    - * TPagerButtonType class.
    - * TPagerButtonType defines the enumerable type for the possible types of pager buttons.
    - *
    - * The following enumerable values are defined:
    - * - LinkButton: link buttons
    - * - PushButton: form submit buttons
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.4
    - */
    -class TPagerButtonType extends TEnumerable
    -{
    -	const LinkButton='LinkButton';
    -	const PushButton='PushButton';
    -	const ImageButton='ImageButton';
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * TPager class.
    + *
    + * TPager creates a pager that provides UI for end-users to interactively
    + * specify which page of data to be rendered in a {@link TDataBoundControl}-derived control,
    + * such as {@link TDataList}, {@link TRepeater}, {@link TCheckBoxList}, etc.
    + * The target data-bound control is specified by {@link setControlToPaginate ControlToPaginate},
    + * which must be the ID path of the target control reaching from the pager's
    + * naming container. Note, the target control must have its {@link TDataBoundControl::setAllowPaging AllowPaging}
    + * set to true.
    + *
    + * TPager can display three different UIs, specified via {@link setMode Mode}:
    + * - NextPrev: a next page and a previous page button are rendered.
    + * - Numeric: a list of page index buttons are rendered.
    + * - List: a dropdown list of page indices are rendered.
    + *
    + * When the pager mode is either NextPrev or Numeric, the paging buttons may be displayed
    + * in three types by setting {@link setButtonType ButtonType}:
    + * - LinkButton: a hyperlink button
    + * - PushButton: a normal button
    + * - ImageButton: an image button (please set XXXPageImageUrl properties accordingly to specify the button images.)
    + *
    + * TPager raises an {@link onPageIndexChanged OnPageIndexChanged} event when
    + * the end-user interacts with it and specifies a new page (e.g. clicking
    + * on a page button that leads to a new page.) The new page index may be obtained
    + * from the event parameter's property {@link TPagerPageChangedEventParameter::getNewPageIndex NewPageIndex}.
    + * Normally, in the event handler, one can set the {@link TDataBoundControl::getCurrentPageIndex CurrentPageIndex}
    + * to this new page index so that the new page of data is rendered.
    + *
    + * Multiple pagers can be associated with the same data-bound control.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.2
    + */
    +class TPager extends TWebControl implements INamingContainer
    +{
    +	/**
    +	 * Command name that TPager understands.
    +	 */
    +	const CMD_PAGE='Page';
    +	const CMD_PAGE_NEXT='Next';
    +	const CMD_PAGE_PREV='Previous';
    +	const CMD_PAGE_FIRST='First';
    +	const CMD_PAGE_LAST='Last';
    +
    +	private $_pageCount=0;
    +
    +	/**
    +	 * Restores the pager state.
    +	 * This method overrides the parent implementation and is invoked when
    +	 * the control is loading persistent state.
    +	 */
    +	public function loadState()
    +	{
    +		parent::loadState();
    +		if($this->getEnableViewState(true))
    +		{
    +			$this->getControls()->clear();
    +			$this->buildPager();
    +		}
    +	}
    +
    +	/**
    +	 * @return string the ID path of the control whose content would be paginated.
    +	 */
    +	public function getControlToPaginate()
    +	{
    +		return $this->getViewState('ControlToPaginate','');
    +	}
    +
    +	/**
    +	 * Sets the ID path of the control whose content would be paginated.
    +	 * The ID path is the dot-connected IDs of the controls reaching from
    +	 * the pager's naming container to the target control.
    +	 * @param string the ID path
    +	 */
    +	public function setControlToPaginate($value)
    +	{
    +		$this->setViewState('ControlToPaginate',$value,'');
    +	}
    +
    +	/**
    +	 * @return TPagerMode pager mode. Defaults to TPagerMode::NextPrev.
    +	 */
    +	public function getMode()
    +	{
    +		return $this->getViewState('Mode',TPagerMode::NextPrev);
    +	}
    +
    +	/**
    +	 * @param TPagerMode pager mode.
    +	 */
    +	public function setMode($value)
    +	{
    +		$this->setViewState('Mode',TPropertyValue::ensureEnum($value,'TPagerMode'),TPagerMode::NextPrev);
    +	}
    +
    +	/**
    +	 * @return TPagerButtonType the type of command button for paging. Defaults to TPagerButtonType::LinkButton.
    +	 */
    +	public function getButtonType()
    +	{
    +		return $this->getViewState('ButtonType',TPagerButtonType::LinkButton);
    +	}
    +
    +	/**
    +	 * @param TPagerButtonType the type of command button for paging.
    +	 */
    +	public function setButtonType($value)
    +	{
    +		$this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TPagerButtonType'),TPagerButtonType::LinkButton);
    +	}
    +
    +	/**
    +	 * @return string text for the next page button. Defaults to '>'.
    +	 */
    +	public function getNextPageText()
    +	{
    +		return $this->getViewState('NextPageText','>');
    +	}
    +
    +	/**
    +	 * @param string text for the next page button.
    +	 */
    +	public function setNextPageText($value)
    +	{
    +		$this->setViewState('NextPageText',$value,'>');
    +	}
    +
    +	/**
    +	 * @return string text for the previous page button. Defaults to '<'.
    +	 */
    +	public function getPrevPageText()
    +	{
    +		return $this->getViewState('PrevPageText','<');
    +	}
    +
    +	/**
    +	 * @param string text for the next page button.
    +	 */
    +	public function setPrevPageText($value)
    +	{
    +		$this->setViewState('PrevPageText',$value,'<');
    +	}
    +
    +	/**
    +	 * @return string text for the first page button. Defaults to '<<'.
    +	 */
    +	public function getFirstPageText()
    +	{
    +		return $this->getViewState('FirstPageText','<<');
    +	}
    +
    +	/**
    +	 * @param string text for the first page button. If empty, the first page button will not be rendered.
    +	 */
    +	public function setFirstPageText($value)
    +	{
    +		$this->setViewState('FirstPageText',$value,'<<');
    +	}
    +
    +	/**
    +	 * @return string text for the last page button. Defaults to '>>'.
    +	 */
    +	public function getLastPageText()
    +	{
    +		return $this->getViewState('LastPageText','>>');
    +	}
    +
    +	/**
    +	 * @param string text for the last page button. If empty, the last page button will not be rendered.
    +	 */
    +	public function setLastPageText($value)
    +	{
    +		$this->setViewState('LastPageText',$value,'>>');
    +	}
    +
    +	/**
    +	 * @return string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function getFirstPageImageUrl()
    +	{
    +		return $this->getViewState('FirstPageImageUrl','');
    +	}
    +
    +	/**
    +	 * @param string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function setFirstPageImageUrl($value)
    +	{
    +		$this->setViewState('FirstPageImageUrl',$value);
    +	}
    +
    +	/**
    +	 * @return string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function getLastPageImageUrl()
    +	{
    +		return $this->getViewState('LastPageImageUrl','');
    +	}
    +
    +	/**
    +	 * @param string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function setLastPageImageUrl($value)
    +	{
    +		$this->setViewState('LastPageImageUrl',$value);
    +	}
    +
    +	/**
    +	 * @return string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function getNextPageImageUrl()
    +	{
    +		return $this->getViewState('NextPageImageUrl','');
    +	}
    +
    +	/**
    +	 * @param string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function setNextPageImageUrl($value)
    +	{
    +		$this->setViewState('NextPageImageUrl',$value);
    +	}
    +
    +	/**
    +	 * @return string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function getPrevPageImageUrl()
    +	{
    +		return $this->getViewState('PrevPageImageUrl','');
    +	}
    +
    +	/**
    +	 * @param string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
    +	 * @since 3.1.1
    +	 */
    +	public function setPrevPageImageUrl($value)
    +	{
    +		$this->setViewState('PrevPageImageUrl',$value);
    +	}
    +
    +	/**
    +	 * @return string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
    +	 * @see setNumericPageImageUrl
    +	 * @since 3.1.1
    +	 */
    +	public function getNumericPageImageUrl()
    +	{
    +		return $this->getViewState('NumericPageImageUrl','');
    +	}
    +
    +	/**
    +	 * Sets the image URL for the numeric page buttons.
    +	 * This is actually a template for generating a set of URLs corresponding to numeric button 1, 2, 3, .., etc.
    +	 * Use {0} as the placeholder for the numbers.
    +	 * For example, the image URL http://example.com/images/button{0}.gif
    +	 * will be replaced as http://example.com/images/button1.gif, http://example.com/images/button2.gif, etc.
    +	 * @param string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
    +	 * @since 3.1.1
    +	 */
    +	public function setNumericPageImageUrl($value)
    +	{
    +		$this->setViewState('NumericPageImageUrl',$value);
    +	}
    +
    +	/**
    +	 * @return integer maximum number of pager buttons to be displayed. Defaults to 10.
    +	 */
    +	public function getPageButtonCount()
    +	{
    +		return $this->getViewState('PageButtonCount',10);
    +	}
    +
    +	/**
    +	 * @param integer maximum number of pager buttons to be displayed
    +	 * @throws TInvalidDataValueException if the value is less than 1.
    +	 */
    +	public function setPageButtonCount($value)
    +	{
    +		if(($value=TPropertyValue::ensureInteger($value))<1)
    +			throw new TInvalidDataValueException('pager_pagebuttoncount_invalid');
    +		$this->setViewState('PageButtonCount',$value,10);
    +	}
    +
    +	/**
    +	 * @return integer the zero-based index of the current page. Defaults to 0.
    +	 */
    +	public function getCurrentPageIndex()
    +	{
    +		return $this->getViewState('CurrentPageIndex',0);
    +	}
    +
    +	/**
    +	 * @param integer the zero-based index of the current page
    +	 * @throws TInvalidDataValueException if the value is less than 0
    +	 */
    +	protected function setCurrentPageIndex($value)
    +	{
    +		if(($value=TPropertyValue::ensureInteger($value))<0)
    +			throw new TInvalidDataValueException('pager_currentpageindex_invalid');
    +		$this->setViewState('CurrentPageIndex',$value,0);
    +	}
    +
    +	/**
    +	 * @return integer number of pages of data items available
    +	 */
    +	public function getPageCount()
    +	{
    +		return $this->getViewState('PageCount',0);
    +	}
    +
    +	/**
    +	 * @param integer number of pages of data items available
    +	 * @throws TInvalidDataValueException if the value is less than 0
    +	 */
    +	protected function setPageCount($value)
    +	{
    +		if(($value=TPropertyValue::ensureInteger($value))<0)
    +			throw new TInvalidDataValueException('pager_pagecount_invalid');
    +		$this->setViewState('PageCount',$value,0);
    +	}
    +
    +	/**
    +	 * @return boolean whether the current page is the first page Defaults to false.
    +	 */
    +	public function getIsFirstPage()
    +	{
    +		return $this->getCurrentPageIndex()===0;
    +	}
    +
    +	/**
    +	 * @return boolean whether the current page is the last page
    +	 */
    +	public function getIsLastPage()
    +	{
    +		return $this->getCurrentPageIndex()===$this->getPageCount()-1;
    +	}
    +
    +	/**
    +	 * Performs databinding to populate data items from data source.
    +	 * This method is invoked by {@link dataBind()}.
    +	 * You may override this function to provide your own way of data population.
    +	 * @param Traversable the bound data
    +	 */
    +	public function onPreRender($param)
    +	{
    +		parent::onPreRender($param);
    +
    +		$controlID=$this->getControlToPaginate();
    +		if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl))
    +			throw new TConfigurationException('pager_controltopaginate_invalid',$controlID);
    +
    +		if($targetControl->getAllowPaging())
    +		{
    +	 		$this->_pageCount=$targetControl->getPageCount();
    +			$this->getControls()->clear();
    +			$this->setPageCount($targetControl->getPageCount());
    +			$this->setCurrentPageIndex($targetControl->getCurrentPageIndex());
    +			$this->buildPager();
    +		}
    +		else
    +			$this->_pageCount=0;
    +	}
    +
    +	/**
    +	 * Renders the control.
    +	 * The method overrides the parent implementation by rendering
    +	 * the pager only when there are two or more pages.
    +	 * @param THtmlWriter the writer
    +	 */
    +	public function render($writer)
    +	{
    +		if($this->_pageCount>1)
    +			parent::render($writer);
    +	}
    +
    +	/**
    +	 * Builds the pager content based on the pager mode.
    +	 * Current implementation includes building 'NextPrev', 'Numeric' and 'DropDownList' pagers.
    +	 * Derived classes may override this method to provide additional pagers.
    +	 */
    +	protected function buildPager()
    +	{
    +		switch($this->getMode())
    +		{
    +			case TPagerMode::NextPrev:
    +				$this->buildNextPrevPager();
    +				break;
    +			case TPagerMode::Numeric:
    +				$this->buildNumericPager();
    +				break;
    +			case TPagerMode::DropDownList:
    +				$this->buildListPager();
    +				break;
    +		}
    +	}
    +
    +	/**
    +	 * Creates a pager button.
    +	 * Depending on the button type, a TLinkButton or a TButton may be created.
    +	 * If it is enabled (clickable), its command name and parameter will also be set.
    +	 * Derived classes may override this method to create additional types of buttons, such as TImageButton.
    +	 * @param string button type, either LinkButton or PushButton
    +	 * @param boolean whether the button should be enabled
    +	 * @param string caption of the button.
    +	 * @param string CommandName corresponding to the OnCommand event of the button.
    +	 * @param string CommandParameter corresponding to the OnCommand event of the button
    +	 * @return mixed the button instance
    +	 */
    +	protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter)
    +	{
    +		if($buttonType===TPagerButtonType::LinkButton)
    +		{
    +			if($enabled)
    +				$button=new TLinkButton;
    +			else
    +			{
    +				$button=new TLabel;
    +				$button->setText($text);
    +				return $button;
    +			}
    +		}
    +		else
    +		{
    +			if($buttonType===TPagerButtonType::ImageButton)
    +			{
    +				$button=new TImageButton;
    +				$button->setImageUrl($this->getPageImageUrl($text,$commandName));
    +			}
    +			else
    +				$button=new TButton;
    +			if(!$enabled)
    +				$button->setEnabled(false);
    +		}
    +		$button->setText($text);
    +		$button->setCommandName($commandName);
    +		$button->setCommandParameter($commandParameter);
    +		$button->setCausesValidation(false);
    +		return $button;
    +	}
    +
    +	/**
    +	 * @param string the caption of the image button
    +	 * @param string the command name associated with the image button
    +	 * @since 3.1.1
    +	 */
    +	protected function getPageImageUrl($text,$commandName)
    +	{
    +		switch($commandName)
    +		{
    +			case self::CMD_PAGE:
    +				$url=$this->getNumericPageImageUrl();
    +				return str_replace('{0}',$text,$url);
    +			case self::CMD_PAGE_NEXT:
    +				return $this->getNextPageImageUrl();
    +			case self::CMD_PAGE_PREV:
    +				return $this->getPrevPageImageUrl();
    +			case self::CMD_PAGE_FIRST:
    +				return $this->getFirstPageImageUrl();
    +			case self::CMD_PAGE_LAST:
    +				return $this->getLastPageImageUrl();
    +			default:
    +				return '';
    +		}
    +	}
    +
    +	/**
    +	 * Builds a next-prev pager
    +	 */
    +	protected function buildNextPrevPager()
    +	{
    +		$buttonType=$this->getButtonType();
    +		$controls=$this->getControls();
    +		if($this->getIsFirstPage())
    +		{
    +			if(($text=$this->getFirstPageText())!=='')
    +			{
    +				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_FIRST,'');
    +				$controls->add($label);
    +				$controls->add("\n");
    +			}
    +			$label=$this->createPagerButton($buttonType,false,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
    +			$controls->add($label);
    +		}
    +		else
    +		{
    +			if(($text=$this->getFirstPageText())!=='')
    +			{
    +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
    +				$controls->add($button);
    +				$controls->add("\n");
    +			}
    +			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
    +			$controls->add($button);
    +		}
    +		$controls->add("\n");
    +		if($this->getIsLastPage())
    +		{
    +			$label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
    +			$controls->add($label);
    +			if(($text=$this->getLastPageText())!=='')
    +			{
    +				$controls->add("\n");
    +				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_LAST,'');
    +				$controls->add($label);
    +			}
    +		}
    +		else
    +		{
    +			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
    +			$controls->add($button);
    +			if(($text=$this->getLastPageText())!=='')
    +			{
    +				$controls->add("\n");
    +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
    +				$controls->add($button);
    +			}
    +		}
    +	}
    +
    +	/**
    +	 * Builds a numeric pager
    +	 */
    +	protected function buildNumericPager()
    +	{
    +		$buttonType=$this->getButtonType();
    +		$controls=$this->getControls();
    +		$pageCount=$this->getPageCount();
    +		$pageIndex=$this->getCurrentPageIndex()+1;
    +		$maxButtonCount=$this->getPageButtonCount();
    +		$buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount;
    +		$startPageIndex=1;
    +		$endPageIndex=$buttonCount;
    +		if($pageIndex>$endPageIndex)
    +		{
    +			$startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1;
    +			if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount)
    +				$endPageIndex=$pageCount;
    +			if($endPageIndex-$startPageIndex+1<$maxButtonCount)
    +			{
    +				if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1)
    +					$startPageIndex=1;
    +			}
    +		}
    +
    +		if($startPageIndex>1)
    +		{
    +			if(($text=$this->getFirstPageText())!=='')
    +			{
    +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
    +				$controls->add($button);
    +				$controls->add("\n");
    +			}
    +			$prevPageIndex=$startPageIndex-1;
    +			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex");
    +			$controls->add($button);
    +			$controls->add("\n");
    +		}
    +
    +		for($i=$startPageIndex;$i<=$endPageIndex;++$i)
    +		{
    +			if($i===$pageIndex)
    +			{
    +				$label=$this->createPagerButton($buttonType,false,"$i",self::CMD_PAGE,'');
    +				$controls->add($label);
    +			}
    +			else
    +			{
    +				$button=$this->createPagerButton($buttonType,true,"$i",self::CMD_PAGE,"$i");
    +				$controls->add($button);
    +			}
    +			if($i<$endPageIndex)
    +				$controls->add("\n");
    +		}
    +
    +		if($pageCount>$endPageIndex)
    +		{
    +			$controls->add("\n");
    +			$nextPageIndex=$endPageIndex+1;
    +			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE,"$nextPageIndex");
    +			$controls->add($button);
    +			if(($text=$this->getLastPageText())!=='')
    +			{
    +				$controls->add("\n");
    +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
    +				$controls->add($button);
    +			}
    +		}
    +	}
    +
    +	/**
    +	 * Builds a dropdown list pager
    +	 */
    +	protected function buildListPager()
    +	{
    +		$list=new TDropDownList;
    +		$this->getControls()->add($list);
    +		$list->setDataSource(range(1,$this->getPageCount()));
    +		$list->dataBind();
    +		$list->setSelectedIndex($this->getCurrentPageIndex());
    +		$list->setAutoPostBack(true);
    +		$list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged'));
    +	}
    +
    +	/**
    +	 * Event handler to the OnSelectedIndexChanged event of the dropdown list.
    +	 * This handler will raise {@link onPageIndexChanged OnPageIndexChanged} event.
    +	 * @param TDropDownList the dropdown list control raising the event
    +	 * @param TEventParameter event parameter
    +	 */
    +	public function listIndexChanged($sender,$param)
    +	{
    +		$pageIndex=$sender->getSelectedIndex();
    +		$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    +	}
    +
    +	/**
    +	 * This event is raised when page index is changed due to a page button click.
    +	 * @param TPagerPageChangedEventParameter event parameter
    +	 */
    +	public function onPageIndexChanged($param)
    +	{
    +		$this->raiseEvent('OnPageIndexChanged',$this,$param);
    +	}
    +
    +	/**
    +	 * Processes a bubbled event.
    +	 * This method overrides parent's implementation by wrapping event parameter
    +	 * for OnCommand event with item information.
    +	 * @param TControl the sender of the event
    +	 * @param TEventParameter event parameter
    +	 * @return boolean whether the event bubbling should stop here.
    +	 */
    +	public function bubbleEvent($sender,$param)
    +	{
    +		if($param instanceof TCommandEventParameter)
    +		{
    +			$command=$param->getCommandName();
    +			if(strcasecmp($command,self::CMD_PAGE)===0)
    +			{
    +				$pageIndex=TPropertyValue::ensureInteger($param->getCommandParameter())-1;
    +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    +				return true;
    +			}
    +			else if(strcasecmp($command,self::CMD_PAGE_NEXT)===0)
    +			{
    +				$pageIndex=$this->getCurrentPageIndex()+1;
    +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    +				return true;
    +			}
    +			else if(strcasecmp($command,self::CMD_PAGE_PREV)===0)
    +			{
    +				$pageIndex=$this->getCurrentPageIndex()-1;
    +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
    +				return true;
    +			}
    +			else if(strcasecmp($command,self::CMD_PAGE_FIRST)===0)
    +			{
    +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,0));
    +				return true;
    +			}
    +			else if(strcasecmp($command,self::CMD_PAGE_LAST)===0)
    +			{
    +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$this->getPageCount()-1));
    +				return true;
    +			}
    +			return false;
    +		}
    +		else
    +			return false;
    +	}
    +}
    +
    +/**
    + * TPagerPageChangedEventParameter class
    + *
    + * TPagerPageChangedEventParameter encapsulates the parameter data for
    + * {@link TPager::onPageIndexChanged PageIndexChanged} event of {@link TPager} controls.
    + *
    + * The {@link getCommandSource CommandSource} property refers to the control
    + * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex}
    + * returns the new page index carried with the page command.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.2
    + */
    +class TPagerPageChangedEventParameter extends TEventParameter
    +{
    +	/**
    +	 * @var integer new page index
    +	 */
    +	private $_newIndex;
    +	/**
    +	 * @var TControl original event sender
    +	 */
    +	private $_source=null;
    +
    +	/**
    +	 * Constructor.
    +	 * @param TControl the control originally raises the OnCommand event.
    +	 * @param integer new page index
    +	 */
    +	public function __construct($source,$newPageIndex)
    +	{
    +		$this->_source=$source;
    +		$this->_newIndex=$newPageIndex;
    +	}
    +
    +	/**
    +	 * @return TControl the control originally raises the OnCommand event.
    +	 */
    +	public function getCommandSource()
    +	{
    +		return $this->_source;
    +	}
    +
    +	/**
    +	 * @return integer new page index
    +	 */
    +	public function getNewPageIndex()
    +	{
    +		return $this->_newIndex;
    +	}
    +}
    +
    +
    +/**
    + * TPagerMode class.
    + * TPagerMode defines the enumerable type for the possible modes that a {@link TPager} control can take.
    + *
    + * The following enumerable values are defined:
    + * - NextPrev: pager buttons are displayed as next and previous pages
    + * - Numeric: pager buttons are displayed as numeric page numbers
    + * - DropDownList: a dropdown list is used to select pages
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.4
    + */
    +class TPagerMode extends TEnumerable
    +{
    +	const NextPrev='NextPrev';
    +	const Numeric='Numeric';
    +	const DropDownList='DropDownList';
    +}
    +
    +
    +/**
    + * TPagerButtonType class.
    + * TPagerButtonType defines the enumerable type for the possible types of pager buttons.
    + *
    + * The following enumerable values are defined:
    + * - LinkButton: link buttons
    + * - PushButton: form submit buttons
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.4
    + */
    +class TPagerButtonType extends TEnumerable
    +{
    +	const LinkButton='LinkButton';
    +	const PushButton='PushButton';
    +	const ImageButton='ImageButton';
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TPanel.php b/framework/Web/UI/WebControls/TPanel.php
    index 5d651679..d4e05136 100644
    --- a/framework/Web/UI/WebControls/TPanel.php
    +++ b/framework/Web/UI/WebControls/TPanel.php
    @@ -1,246 +1,246 @@
    -
    - * @link http://www.pradosoft.com/
    - * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Includes TPanelStyle class file
    - */
    -Prado::using('System.Web.UI.WebControls.TPanelStyle');
    -
    -/**
    - * TPanel class
    - *
    - * TPanel represents a component that acts as a container for other component.
    - * It is especially useful when you want to generate components programmatically
    - * or hide/show a group of components.
    - *
    - * By default, TPanel displays a <div> element on a page.
    - * Children of TPanel are displayed as the body content of the element.
    - * The property {@link setWrap Wrap} can be used to set whether the body content
    - * should wrap or not. {@link setHorizontalAlign HorizontalAlign} governs how
    - * the content is aligned horizontally, and {@link getDirection Direction} indicates
    - * the content direction (left to right or right to left). You can set
    - * {@link setBackImageUrl BackImageUrl} to give a background image to the panel,
    - * and you can ste {@link setGroupingText GroupingText} so that the panel is
    - * displayed as a field set with a legend text. Finally, you can specify
    - * a default button to be fired when users press 'return' key within the panel
    - * by setting the {@link setDefaultButton DefaultButton} property.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TPanel extends TWebControl
    -{
    -	/**
    -	 * @var string ID path to the default button
    -	 */
    -	private $_defaultButton='';
    -
    -	/**
    -	 * @return string tag name of the panel
    -	 */
    -	protected function getTagName()
    -	{
    -		return 'div';
    -	}
    -
    -	/**
    -	 * Creates a style object to be used by the control.
    -	 * This method overrides the parent impementation by creating a TPanelStyle object.
    -	 * @return TPanelStyle the style used by TPanel.
    -	 */
    -	protected function createStyle()
    -	{
    -		return new TPanelStyle;
    -	}
    -
    -	/**
    -	 * Adds attributes to renderer.
    -	 * @param THtmlWriter the renderer
    -	 * @throws TInvalidDataValueException if default button is not right.
    -	 */
    -	protected function addAttributesToRender($writer)
    -	{
    -		parent::addAttributesToRender($writer);
    -		if(($butt=$this->getDefaultButton())!=='')
    -			$writer->addAttribute('id',$this->getClientID());
    -	}
    -
    -	/**
    -	 * @return boolean whether the content wraps within the panel. Defaults to true.
    -	 */
    -	public function getWrap()
    -	{
    -		return $this->getStyle()->getWrap();
    -	}
    -
    -	/**
    -	 * Sets the value indicating whether the content wraps within the panel.
    -	 * @param boolean whether the content wraps within the panel.
    -	 */
    -	public function setWrap($value)
    -	{
    -		$this->getStyle()->setWrap($value);
    -	}
    -
    -	/**
    -	 * @return string the horizontal alignment of the contents within the panel, defaults to 'NotSet'.
    -	 */
    -	public function getHorizontalAlign()
    -	{
    -		return $this->getStyle()->getHorizontalAlign();
    -	}
    -
    -	/**
    -	 * Sets the horizontal alignment of the contents within the panel.
    -     * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center'
    -	 * @param string the horizontal alignment
    -	 */
    -	public function setHorizontalAlign($value)
    -	{
    -		$this->getStyle()->setHorizontalAlign($value);
    -	}
    -
    -	/**
    -	 * @return string the URL of the background image for the panel component.
    -	 */
    -	public function getBackImageUrl()
    -	{
    -		return $this->getStyle()->getBackImageUrl();
    -	}
    -
    -	/**
    -	 * Sets the URL of the background image for the panel component.
    -	 * @param string the URL
    -	 */
    -	public function setBackImageUrl($value)
    -	{
    -		$this->getStyle()->setBackImageUrl($value);
    -	}
    -
    -	/**
    -	 * @return string alignment of the content in the panel. Defaults to 'NotSet'.
    -	 */
    -	public function getDirection()
    -	{
    -		return $this->getStyle()->getDirection();
    -	}
    -
    -	/**
    -	 * @param string alignment of the content in the panel.
    -	 * Valid values include 'NotSet', 'LeftToRight', 'RightToLeft'.
    -	 */
    -	public function setDirection($value)
    -	{
    -		$this->getStyle()->setDirection($value);
    -	}
    -
    -	/**
    -	 * @return string the ID path to the default button. Defaults to empty.
    -	 */
    -	public function getDefaultButton()
    -	{
    -		return $this->_defaultButton;
    -	}
    -
    -	/**
    -	 * Specifies the default button for the panel.
    -	 * The default button will be fired (clicked) whenever a user enters 'return'
    -	 * key within the panel.
    -	 * The button must be locatable via the function call {@link TControl::findControl findControl}.
    -	 * @param string the ID path to the default button.
    -	 */
    -	public function setDefaultButton($value)
    -	{
    -		$this->_defaultButton=$value;
    -	}
    -
    -	/**
    -	 * @return string the legend text when the panel is used as a fieldset. Defaults to empty.
    -	 */
    -	public function getGroupingText()
    -	{
    -		return $this->getViewState('GroupingText','');
    -	}
    -
    -	/**
    -	 * @param string the legend text. If this value is not empty, the panel will be rendered as a fieldset.
    -	 */
    -	public function setGroupingText($value)
    -	{
    -		$this->setViewState('GroupingText',$value,'');
    -	}
    -
    -	/**
    -	 * @return string the visibility and position of scroll bars in a panel control, defaults to None.
    -	 */
    -	public function getScrollBars()
    -	{
    -		return $this->getStyle()->getScrollBars();
    -	}
    -
    -	/**
    -	 * @param string the visibility and position of scroll bars in a panel control.
    -	 * Valid values include None, Auto, Both, Horizontal and Vertical.
    -	 */
    -	public function setScrollBars($value)
    -	{
    -		$this->getStyle()->setScrollBars($value);
    -	}
    -
    -	/**
    -	 * Renders the openning tag for the control (including attributes)
    -	 * @param THtmlWriter the writer used for the rendering purpose
    -	 */
    -	public function renderBeginTag($writer)
    -	{
    -		parent::renderBeginTag($writer);
    -		if(($text=$this->getGroupingText())!=='')
    -		{
    -			$writer->renderBeginTag('fieldset');
    -			$writer->renderBeginTag('legend');
    -			$writer->write($text);
    -			$writer->renderEndTag();
    -		}
    -	}
    -
    -	/**
    -	 * Renders the closing tag for the control
    -	 * @param THtmlWriter the writer used for the rendering purpose
    -	 */
    -	public function renderEndTag($writer)
    -	{
    -		if($this->getGroupingText()!=='')
    -			$writer->renderEndTag();
    -		parent::renderEndTag($writer);
    -	}
    -
    -	public function render($writer)
    -	{
    -		parent::render($writer);
    -
    -		if(($butt=$this->getDefaultButton())!=='')
    -		{
    -			$buttons = $this->findControlsByID($butt);
    -			if (count($buttons)>0)
    -				$button = reset($buttons);
    -			else
    -				$button = null;
    -			if($button===null)
    -				throw new TInvalidDataValueException('panel_defaultbutton_invalid',$butt);
    -			else
    -				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
    -		}
    -	}
    -}
    -
    +
    + * @link http://www.pradosoft.com/
    + * @copyright Copyright © 2005-2012 PradoSoft
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Includes TPanelStyle class file
    + */
    +Prado::using('System.Web.UI.WebControls.TPanelStyle');
    +
    +/**
    + * TPanel class
    + *
    + * TPanel represents a component that acts as a container for other component.
    + * It is especially useful when you want to generate components programmatically
    + * or hide/show a group of components.
    + *
    + * By default, TPanel displays a <div> element on a page.
    + * Children of TPanel are displayed as the body content of the element.
    + * The property {@link setWrap Wrap} can be used to set whether the body content
    + * should wrap or not. {@link setHorizontalAlign HorizontalAlign} governs how
    + * the content is aligned horizontally, and {@link getDirection Direction} indicates
    + * the content direction (left to right or right to left). You can set
    + * {@link setBackImageUrl BackImageUrl} to give a background image to the panel,
    + * and you can ste {@link setGroupingText GroupingText} so that the panel is
    + * displayed as a field set with a legend text. Finally, you can specify
    + * a default button to be fired when users press 'return' key within the panel
    + * by setting the {@link setDefaultButton DefaultButton} property.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TPanel extends TWebControl
    +{
    +	/**
    +	 * @var string ID path to the default button
    +	 */
    +	private $_defaultButton='';
    +
    +	/**
    +	 * @return string tag name of the panel
    +	 */
    +	protected function getTagName()
    +	{
    +		return 'div';
    +	}
    +
    +	/**
    +	 * Creates a style object to be used by the control.
    +	 * This method overrides the parent impementation by creating a TPanelStyle object.
    +	 * @return TPanelStyle the style used by TPanel.
    +	 */
    +	protected function createStyle()
    +	{
    +		return new TPanelStyle;
    +	}
    +
    +	/**
    +	 * Adds attributes to renderer.
    +	 * @param THtmlWriter the renderer
    +	 * @throws TInvalidDataValueException if default button is not right.
    +	 */
    +	protected function addAttributesToRender($writer)
    +	{
    +		parent::addAttributesToRender($writer);
    +		if(($butt=$this->getDefaultButton())!=='')
    +			$writer->addAttribute('id',$this->getClientID());
    +	}
    +
    +	/**
    +	 * @return boolean whether the content wraps within the panel. Defaults to true.
    +	 */
    +	public function getWrap()
    +	{
    +		return $this->getStyle()->getWrap();
    +	}
    +
    +	/**
    +	 * Sets the value indicating whether the content wraps within the panel.
    +	 * @param boolean whether the content wraps within the panel.
    +	 */
    +	public function setWrap($value)
    +	{
    +		$this->getStyle()->setWrap($value);
    +	}
    +
    +	/**
    +	 * @return string the horizontal alignment of the contents within the panel, defaults to 'NotSet'.
    +	 */
    +	public function getHorizontalAlign()
    +	{
    +		return $this->getStyle()->getHorizontalAlign();
    +	}
    +
    +	/**
    +	 * Sets the horizontal alignment of the contents within the panel.
    +     * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center'
    +	 * @param string the horizontal alignment
    +	 */
    +	public function setHorizontalAlign($value)
    +	{
    +		$this->getStyle()->setHorizontalAlign($value);
    +	}
    +
    +	/**
    +	 * @return string the URL of the background image for the panel component.
    +	 */
    +	public function getBackImageUrl()
    +	{
    +		return $this->getStyle()->getBackImageUrl();
    +	}
    +
    +	/**
    +	 * Sets the URL of the background image for the panel component.
    +	 * @param string the URL
    +	 */
    +	public function setBackImageUrl($value)
    +	{
    +		$this->getStyle()->setBackImageUrl($value);
    +	}
    +
    +	/**
    +	 * @return string alignment of the content in the panel. Defaults to 'NotSet'.
    +	 */
    +	public function getDirection()
    +	{
    +		return $this->getStyle()->getDirection();
    +	}
    +
    +	/**
    +	 * @param string alignment of the content in the panel.
    +	 * Valid values include 'NotSet', 'LeftToRight', 'RightToLeft'.
    +	 */
    +	public function setDirection($value)
    +	{
    +		$this->getStyle()->setDirection($value);
    +	}
    +
    +	/**
    +	 * @return string the ID path to the default button. Defaults to empty.
    +	 */
    +	public function getDefaultButton()
    +	{
    +		return $this->_defaultButton;
    +	}
    +
    +	/**
    +	 * Specifies the default button for the panel.
    +	 * The default button will be fired (clicked) whenever a user enters 'return'
    +	 * key within the panel.
    +	 * The button must be locatable via the function call {@link TControl::findControl findControl}.
    +	 * @param string the ID path to the default button.
    +	 */
    +	public function setDefaultButton($value)
    +	{
    +		$this->_defaultButton=$value;
    +	}
    +
    +	/**
    +	 * @return string the legend text when the panel is used as a fieldset. Defaults to empty.
    +	 */
    +	public function getGroupingText()
    +	{
    +		return $this->getViewState('GroupingText','');
    +	}
    +
    +	/**
    +	 * @param string the legend text. If this value is not empty, the panel will be rendered as a fieldset.
    +	 */
    +	public function setGroupingText($value)
    +	{
    +		$this->setViewState('GroupingText',$value,'');
    +	}
    +
    +	/**
    +	 * @return string the visibility and position of scroll bars in a panel control, defaults to None.
    +	 */
    +	public function getScrollBars()
    +	{
    +		return $this->getStyle()->getScrollBars();
    +	}
    +
    +	/**
    +	 * @param string the visibility and position of scroll bars in a panel control.
    +	 * Valid values include None, Auto, Both, Horizontal and Vertical.
    +	 */
    +	public function setScrollBars($value)
    +	{
    +		$this->getStyle()->setScrollBars($value);
    +	}
    +
    +	/**
    +	 * Renders the openning tag for the control (including attributes)
    +	 * @param THtmlWriter the writer used for the rendering purpose
    +	 */
    +	public function renderBeginTag($writer)
    +	{
    +		parent::renderBeginTag($writer);
    +		if(($text=$this->getGroupingText())!=='')
    +		{
    +			$writer->renderBeginTag('fieldset');
    +			$writer->renderBeginTag('legend');
    +			$writer->write($text);
    +			$writer->renderEndTag();
    +		}
    +	}
    +
    +	/**
    +	 * Renders the closing tag for the control
    +	 * @param THtmlWriter the writer used for the rendering purpose
    +	 */
    +	public function renderEndTag($writer)
    +	{
    +		if($this->getGroupingText()!=='')
    +			$writer->renderEndTag();
    +		parent::renderEndTag($writer);
    +	}
    +
    +	public function render($writer)
    +	{
    +		parent::render($writer);
    +
    +		if(($butt=$this->getDefaultButton())!=='')
    +		{
    +			$buttons = $this->findControlsByID($butt);
    +			if (count($buttons)>0)
    +				$button = reset($buttons);
    +			else
    +				$button = null;
    +			if($button===null)
    +				throw new TInvalidDataValueException('panel_defaultbutton_invalid',$butt);
    +			else
    +				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
    +		}
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TPanelStyle.php b/framework/Web/UI/WebControls/TPanelStyle.php
    index 16169749..5e5db6e4 100644
    --- a/framework/Web/UI/WebControls/TPanelStyle.php
    +++ b/framework/Web/UI/WebControls/TPanelStyle.php
    @@ -1,278 +1,278 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Includes TStyle class file
    - */
    -Prado::using('System.Web.UI.WebControls.TStyle');
    -
    -/**
    - * TPanelStyle class.
    - * TPanelStyle represents the CSS style specific for panel HTML tag.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TPanelStyle extends TStyle
    -{
    -	/**
    -	 * @var string the URL of the background image for the panel component
    -	 */
    -	private $_backImageUrl=null;
    -	/**
    -	 * @var string alignment of the content in the panel.
    -	 */
    -	private $_direction=null;
    -	/**
    -	 * @var string horizontal alignment of the contents within the panel
    -	 */
    -	private $_horizontalAlign=null;
    -	/**
    -	 * @var string visibility and position of scroll bars
    -	 */
    -	private $_scrollBars=null;
    -	/**
    -	 * @var boolean whether the content wraps within the panel
    -	 */
    -	private $_wrap=null;
    -
    -	/**
    -	 * Adds attributes related to CSS styles to renderer.
    -	 * This method overrides the parent implementation.
    -	 * @param THtmlWriter the writer used for the rendering purpose
    -	 */
    -	public function addAttributesToRender($writer)
    -	{
    -		if(($url=trim($this->getBackImageUrl()))!=='')
    -			$this->setStyleField('background-image','url('.$url.')');
    -
    -		switch($this->getScrollBars())
    -		{
    -			case TScrollBars::Horizontal: $this->setStyleField('overflow-x','scroll'); break;
    -			case TScrollBars::Vertical: $this->setStyleField('overflow-y','scroll'); break;
    -			case TScrollBars::Both: $this->setStyleField('overflow','scroll'); break;
    -			case TScrollBars::Auto: $this->setStyleField('overflow','auto'); break;
    -		}
    -
    -		if(($align=$this->getHorizontalAlign())!==THorizontalAlign::NotSet)
    -			$this->setStyleField('text-align',strtolower($align));
    -
    -		if(!$this->getWrap())
    -			$this->setStyleField('white-space','nowrap');
    -
    -		if(($direction=$this->getDirection())!==TContentDirection::NotSet)
    -		{
    -			if($direction===TContentDirection::LeftToRight)
    -				$this->setStyleField('direction','ltr');
    -			else
    -				$this->setStyleField('direction','rtl');
    -		}
    -
    -		parent::addAttributesToRender($writer);
    -	}
    -
    -	/**
    -	 * @return string the URL of the background image for the panel component.
    -	 */
    -	public function getBackImageUrl()
    -	{
    -		return $this->_backImageUrl===null?'':$this->_backImageUrl;
    -	}
    -
    -	/**
    -	 * Sets the URL of the background image for the panel component.
    -	 * @param string the URL
    -	 */
    -	public function setBackImageUrl($value)
    -	{
    -		$this->_backImageUrl=$value;
    -	}
    -
    -	/**
    -	 * @return TContentDirection alignment of the content in the panel. Defaults to TContentDirection::NotSet.
    -	 */
    -	public function getDirection()
    -	{
    -		return $this->_direction===null?TContentDirection::NotSet:$this->_direction;
    -	}
    -
    -	/**
    -	 * @param TContentDirection alignment of the content in the panel.
    -	 */
    -	public function setDirection($value)
    -	{
    -		$this->_direction=TPropertyValue::ensureEnum($value,'TContentDirection');
    -	}
    -
    -	/**
    -	 * @return boolean whether the content wraps within the panel. Defaults to true.
    -	 */
    -	public function getWrap()
    -	{
    -		return $this->_wrap===null?true:$this->_wrap;
    -	}
    -
    -	/**
    -	 * Sets the value indicating whether the content wraps within the panel.
    -	 * @param boolean whether the content wraps within the panel.
    -	 */
    -	public function setWrap($value)
    -	{
    -		$this->_wrap=TPropertyValue::ensureBoolean($value);
    -	}
    -
    -	/**
    -	 * @return THorizontalAlign the horizontal alignment of the contents within the panel, defaults to THorizontalAlign::NotSet.
    -	 */
    -	public function getHorizontalAlign()
    -	{
    -		return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign;
    -	}
    -
    -	/**
    -	 * Sets the horizontal alignment of the contents within the panel.
    -	 * @param THorizontalAlign the horizontal alignment
    -	 */
    -	public function setHorizontalAlign($value)
    -	{
    -		$this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign');
    -	}
    -
    -	/**
    -	 * @return TScrollBars the visibility and position of scroll bars in a panel control, defaults to TScrollBars::None.
    -	 */
    -	public function getScrollBars()
    -	{
    -		return $this->_scrollBars===null?TScrollBars::None:$this->_scrollBars;
    -	}
    -
    -	/**
    -	 * @param TScrollBars the visibility and position of scroll bars in a panel control.
    -	 */
    -	public function setScrollBars($value)
    -	{
    -		$this->_scrollBars=TPropertyValue::ensureEnum($value,'TScrollBars');
    -	}
    -
    -	/**
    -	 * Sets the style attributes to default values.
    -	 * This method overrides the parent implementation by
    -	 * resetting additional TPanelStyle specific attributes.
    -	 */
    -	public function reset()
    -	{
    -		parent::reset();
    -		$this->_backImageUrl=null;
    -		$this->_direction=null;
    -		$this->_horizontalAlign=null;
    -		$this->_scrollBars=null;
    -		$this->_wrap=null;
    -	}
    -
    -	/**
    -	 * Copies the fields in a new style to this style.
    -	 * If a style field is set in the new style, the corresponding field
    -	 * in this style will be overwritten.
    -	 * @param TStyle the new style
    -	 */
    -	public function copyFrom($style)
    -	{
    -		parent::copyFrom($style);
    -		if($style instanceof TPanelStyle)
    -		{
    -			if($style->_backImageUrl!==null)
    -				$this->_backImageUrl=$style->_backImageUrl;
    -			if($style->_direction!==null)
    -				$this->_direction=$style->_direction;
    -			if($style->_horizontalAlign!==null)
    -				$this->_horizontalAlign=$style->_horizontalAlign;
    -			if($style->_scrollBars!==null)
    -				$this->_scrollBars=$style->_scrollBars;
    -			if($style->_wrap!==null)
    -				$this->_wrap=$style->_wrap;
    -		}
    -	}
    -
    -	/**
    -	 * Merges the style with a new one.
    -	 * If a style field is not set in this style, it will be overwritten by
    -	 * the new one.
    -	 * @param TStyle the new style
    -	 */
    -	public function mergeWith($style)
    -	{
    -		parent::mergeWith($style);
    -		if($style instanceof TPanelStyle)
    -		{
    -			if($this->_backImageUrl===null && $style->_backImageUrl!==null)
    -				$this->_backImageUrl=$style->_backImageUrl;
    -			if($this->_direction===null && $style->_direction!==null)
    -				$this->_direction=$style->_direction;
    -			if($this->_horizontalAlign===null && $style->_horizontalAlign!==null)
    -				$this->_horizontalAlign=$style->_horizontalAlign;
    -			if($this->_scrollBars===null && $style->_scrollBars!==null)
    -				$this->_scrollBars=$style->_scrollBars;
    -			if($this->_wrap===null && $style->_wrap!==null)
    -				$this->_wrap=$style->_wrap;
    -		}
    -	}
    -}
    -
    -/**
    - * TContentDirection class.
    - * TContentDirection defines the enumerable type for the possible directions that a panel can be at.
    - *
    - * The following enumerable values are defined:
    - * - NotSet: the direction is not specified
    - * - LeftToRight: content in a panel is left to right
    - * - RightToLeft: content in a panel is right to left
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.4
    - */
    -class TContentDirection extends TEnumerable
    -{
    -	const NotSet='NotSet';
    -	const LeftToRight='LeftToRight';
    -	const RightToLeft='RightToLeft';
    -}
    -
    -/**
    - * TScrollBars class.
    - * TScrollBars defines the enumerable type for the possible scroll bar mode
    - * that a {@link TPanel} control could use.
    - *
    - * The following enumerable values are defined:
    - * - None: no scroll bars.
    - * - Auto: scroll bars automatically appeared when needed.
    - * - Both: show both horizontal and vertical scroll bars all the time.
    - * - Horizontal: horizontal scroll bar only
    - * - Vertical: vertical scroll bar only
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.4
    - */
    -class TScrollBars extends TEnumerable
    -{
    -	const None='None';
    -	const Auto='Auto';
    -	const Both='Both';
    -	const Horizontal='Horizontal';
    -	const Vertical='Vertical';
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Includes TStyle class file
    + */
    +Prado::using('System.Web.UI.WebControls.TStyle');
    +
    +/**
    + * TPanelStyle class.
    + * TPanelStyle represents the CSS style specific for panel HTML tag.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TPanelStyle extends TStyle
    +{
    +	/**
    +	 * @var string the URL of the background image for the panel component
    +	 */
    +	private $_backImageUrl=null;
    +	/**
    +	 * @var string alignment of the content in the panel.
    +	 */
    +	private $_direction=null;
    +	/**
    +	 * @var string horizontal alignment of the contents within the panel
    +	 */
    +	private $_horizontalAlign=null;
    +	/**
    +	 * @var string visibility and position of scroll bars
    +	 */
    +	private $_scrollBars=null;
    +	/**
    +	 * @var boolean whether the content wraps within the panel
    +	 */
    +	private $_wrap=null;
    +
    +	/**
    +	 * Adds attributes related to CSS styles to renderer.
    +	 * This method overrides the parent implementation.
    +	 * @param THtmlWriter the writer used for the rendering purpose
    +	 */
    +	public function addAttributesToRender($writer)
    +	{
    +		if(($url=trim($this->getBackImageUrl()))!=='')
    +			$this->setStyleField('background-image','url('.$url.')');
    +
    +		switch($this->getScrollBars())
    +		{
    +			case TScrollBars::Horizontal: $this->setStyleField('overflow-x','scroll'); break;
    +			case TScrollBars::Vertical: $this->setStyleField('overflow-y','scroll'); break;
    +			case TScrollBars::Both: $this->setStyleField('overflow','scroll'); break;
    +			case TScrollBars::Auto: $this->setStyleField('overflow','auto'); break;
    +		}
    +
    +		if(($align=$this->getHorizontalAlign())!==THorizontalAlign::NotSet)
    +			$this->setStyleField('text-align',strtolower($align));
    +
    +		if(!$this->getWrap())
    +			$this->setStyleField('white-space','nowrap');
    +
    +		if(($direction=$this->getDirection())!==TContentDirection::NotSet)
    +		{
    +			if($direction===TContentDirection::LeftToRight)
    +				$this->setStyleField('direction','ltr');
    +			else
    +				$this->setStyleField('direction','rtl');
    +		}
    +
    +		parent::addAttributesToRender($writer);
    +	}
    +
    +	/**
    +	 * @return string the URL of the background image for the panel component.
    +	 */
    +	public function getBackImageUrl()
    +	{
    +		return $this->_backImageUrl===null?'':$this->_backImageUrl;
    +	}
    +
    +	/**
    +	 * Sets the URL of the background image for the panel component.
    +	 * @param string the URL
    +	 */
    +	public function setBackImageUrl($value)
    +	{
    +		$this->_backImageUrl=$value;
    +	}
    +
    +	/**
    +	 * @return TContentDirection alignment of the content in the panel. Defaults to TContentDirection::NotSet.
    +	 */
    +	public function getDirection()
    +	{
    +		return $this->_direction===null?TContentDirection::NotSet:$this->_direction;
    +	}
    +
    +	/**
    +	 * @param TContentDirection alignment of the content in the panel.
    +	 */
    +	public function setDirection($value)
    +	{
    +		$this->_direction=TPropertyValue::ensureEnum($value,'TContentDirection');
    +	}
    +
    +	/**
    +	 * @return boolean whether the content wraps within the panel. Defaults to true.
    +	 */
    +	public function getWrap()
    +	{
    +		return $this->_wrap===null?true:$this->_wrap;
    +	}
    +
    +	/**
    +	 * Sets the value indicating whether the content wraps within the panel.
    +	 * @param boolean whether the content wraps within the panel.
    +	 */
    +	public function setWrap($value)
    +	{
    +		$this->_wrap=TPropertyValue::ensureBoolean($value);
    +	}
    +
    +	/**
    +	 * @return THorizontalAlign the horizontal alignment of the contents within the panel, defaults to THorizontalAlign::NotSet.
    +	 */
    +	public function getHorizontalAlign()
    +	{
    +		return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign;
    +	}
    +
    +	/**
    +	 * Sets the horizontal alignment of the contents within the panel.
    +	 * @param THorizontalAlign the horizontal alignment
    +	 */
    +	public function setHorizontalAlign($value)
    +	{
    +		$this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign');
    +	}
    +
    +	/**
    +	 * @return TScrollBars the visibility and position of scroll bars in a panel control, defaults to TScrollBars::None.
    +	 */
    +	public function getScrollBars()
    +	{
    +		return $this->_scrollBars===null?TScrollBars::None:$this->_scrollBars;
    +	}
    +
    +	/**
    +	 * @param TScrollBars the visibility and position of scroll bars in a panel control.
    +	 */
    +	public function setScrollBars($value)
    +	{
    +		$this->_scrollBars=TPropertyValue::ensureEnum($value,'TScrollBars');
    +	}
    +
    +	/**
    +	 * Sets the style attributes to default values.
    +	 * This method overrides the parent implementation by
    +	 * resetting additional TPanelStyle specific attributes.
    +	 */
    +	public function reset()
    +	{
    +		parent::reset();
    +		$this->_backImageUrl=null;
    +		$this->_direction=null;
    +		$this->_horizontalAlign=null;
    +		$this->_scrollBars=null;
    +		$this->_wrap=null;
    +	}
    +
    +	/**
    +	 * Copies the fields in a new style to this style.
    +	 * If a style field is set in the new style, the corresponding field
    +	 * in this style will be overwritten.
    +	 * @param TStyle the new style
    +	 */
    +	public function copyFrom($style)
    +	{
    +		parent::copyFrom($style);
    +		if($style instanceof TPanelStyle)
    +		{
    +			if($style->_backImageUrl!==null)
    +				$this->_backImageUrl=$style->_backImageUrl;
    +			if($style->_direction!==null)
    +				$this->_direction=$style->_direction;
    +			if($style->_horizontalAlign!==null)
    +				$this->_horizontalAlign=$style->_horizontalAlign;
    +			if($style->_scrollBars!==null)
    +				$this->_scrollBars=$style->_scrollBars;
    +			if($style->_wrap!==null)
    +				$this->_wrap=$style->_wrap;
    +		}
    +	}
    +
    +	/**
    +	 * Merges the style with a new one.
    +	 * If a style field is not set in this style, it will be overwritten by
    +	 * the new one.
    +	 * @param TStyle the new style
    +	 */
    +	public function mergeWith($style)
    +	{
    +		parent::mergeWith($style);
    +		if($style instanceof TPanelStyle)
    +		{
    +			if($this->_backImageUrl===null && $style->_backImageUrl!==null)
    +				$this->_backImageUrl=$style->_backImageUrl;
    +			if($this->_direction===null && $style->_direction!==null)
    +				$this->_direction=$style->_direction;
    +			if($this->_horizontalAlign===null && $style->_horizontalAlign!==null)
    +				$this->_horizontalAlign=$style->_horizontalAlign;
    +			if($this->_scrollBars===null && $style->_scrollBars!==null)
    +				$this->_scrollBars=$style->_scrollBars;
    +			if($this->_wrap===null && $style->_wrap!==null)
    +				$this->_wrap=$style->_wrap;
    +		}
    +	}
    +}
    +
    +/**
    + * TContentDirection class.
    + * TContentDirection defines the enumerable type for the possible directions that a panel can be at.
    + *
    + * The following enumerable values are defined:
    + * - NotSet: the direction is not specified
    + * - LeftToRight: content in a panel is left to right
    + * - RightToLeft: content in a panel is right to left
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.4
    + */
    +class TContentDirection extends TEnumerable
    +{
    +	const NotSet='NotSet';
    +	const LeftToRight='LeftToRight';
    +	const RightToLeft='RightToLeft';
    +}
    +
    +/**
    + * TScrollBars class.
    + * TScrollBars defines the enumerable type for the possible scroll bar mode
    + * that a {@link TPanel} control could use.
    + *
    + * The following enumerable values are defined:
    + * - None: no scroll bars.
    + * - Auto: scroll bars automatically appeared when needed.
    + * - Both: show both horizontal and vertical scroll bars all the time.
    + * - Horizontal: horizontal scroll bar only
    + * - Vertical: vertical scroll bar only
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.4
    + */
    +class TScrollBars extends TEnumerable
    +{
    +	const None='None';
    +	const Auto='Auto';
    +	const Both='Both';
    +	const Horizontal='Horizontal';
    +	const Vertical='Vertical';
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TPlaceHolder.php b/framework/Web/UI/WebControls/TPlaceHolder.php
    index ac67cef7..585e9e99 100644
    --- a/framework/Web/UI/WebControls/TPlaceHolder.php
    +++ b/framework/Web/UI/WebControls/TPlaceHolder.php
    @@ -1,28 +1,28 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * TPlaceHolder class
    - *
    - * TPlaceHolder reserves a place on a template, where static texts or controls
    - * may be inserted. You may add or remove texts or child controls of TPlaceHolder
    - * by manipulating the {@link TControl::getControls Controls} property.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TPlaceHolder extends TControl
    -{
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * TPlaceHolder class
    + *
    + * TPlaceHolder reserves a place on a template, where static texts or controls
    + * may be inserted. You may add or remove texts or child controls of TPlaceHolder
    + * by manipulating the {@link TControl::getControls Controls} property.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TPlaceHolder extends TControl
    +{
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TRadioButton.php b/framework/Web/UI/WebControls/TRadioButton.php
    index b90cfcf5..6a2347f4 100644
    --- a/framework/Web/UI/WebControls/TRadioButton.php
    +++ b/framework/Web/UI/WebControls/TRadioButton.php
    @@ -1,320 +1,320 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Using TCheckBox parent class
    - */
    -Prado::using('System.Web.UI.WebControls.TCheckBox');
    -/**
    - * Using TRadioButtonList class
    - */
    -Prado::using('System.Web.UI.WebControls.TRadioButtonList');
    -
    -/**
    - * TRadioButton class
    - *
    - * TRadioButton displays a radio button on the page.
    - * You can specify the caption to display beside the radio buttonby setting
    - * the {@link setText Text} property.  The caption can appear either on the right
    - * or left of the radio button, which is determined by the {@link setTextAlign TextAlign}
    - * property.
    - *
    - * To determine whether the TRadioButton component is checked, test the {@link getChecked Checked}
    - * property. The {@link onCheckedChanged OnCheckedChanged} event is raised when
    - * the {@link getChecked Checked} state of the TRadioButton component changes
    - * between posts to the server. You can provide an event handler for
    - * the {@link onCheckedChanged OnCheckedChanged} event to  to programmatically
    - * control the actions performed when the state of the TRadioButton component changes
    - * between posts to the server.
    - *
    - * TRadioButton uses {@link setGroupName GroupName} to group together a set of radio buttons.
    - * Once the {@link setGroupName GroupName} is set, you can use the {@link getRadioButtonsInGroup}
    - * method to get an array of TRadioButtons having the same group name.
    - *
    - * If {@link setAutoPostBack AutoPostBack} is set true, changing the radio button state
    - * will cause postback action. And if {@link setCausesValidation CausesValidation}
    - * is true, validation will also be processed, which can be further restricted within
    - * a {@link setValidationGroup ValidationGroup}.
    - *
    - * Note, {@link setText Text} is rendered as is. Make sure it does not contain unwanted characters
    - * that may bring security vulnerabilities.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TRadioButton extends TCheckBox
    -{
    -	/**
    -	 * @param array list of radio buttons that are on the current page hierarchy
    -	 */
    -	private static $_activeButtons=array();
    -	/**
    -	 * @var integer number of radio buttons created
    -	 */
    -	private static $_buttonCount=0;
    -	/**
    -	 * @var integer global ID of this radiobutton
    -	 */
    -	private $_globalID;
    -	/**
    -	 * @var string previous UniqueID (used to calculate UniqueGroup)
    -	 */
    -	private $_previousUniqueID=null;
    -	/**
    -	 * @var string the name used to fetch radiobutton post data
    -	 */
    -	private $_uniqueGroupName=null;
    -
    -	/**
    -	 * Constructor.
    -	 * Registers the radiobutton in a global radiobutton collection.
    -	 * If overridden, the parent implementation must be invoked first.
    -	 */
    -	public function __construct()
    -	{
    -		parent::__construct();
    -		$this->_globalID = self::$_buttonCount++;
    -	}
    -
    -	/**
    -	 * Registers the radio button groupings. If overriding onInit method,
    -	 * ensure to call parent implemenation.
    -	 * @param TEventParameter event parameter to be passed to the event handlers
    -	 */
    -	public function onInit($param)
    -	{
    -		parent::onInit($param);
    -		self::$_activeButtons[$this->_globalID]=$this;
    -	}
    -
    -	/**
    -	 * Unregisters the radio button groupings. If overriding onInit method,
    -	 * ensure to call parent implemenation.
    -	 * @param TEventParameter event parameter to be passed to the event handlers
    -	 */
    -	public function onUnLoad($param)
    -	{
    -		unset(self::$_activeButtons[$this->_globalID]);
    -		parent::onUnLoad($param);
    -	}
    -
    -	/**
    -	 * Loads user input data.
    -	 * This method is primarly used by framework developers.
    -	 * @param string the key that can be used to retrieve data from the input data collection
    -	 * @param array the input data collection
    -	 * @return boolean whether the data of the control has been changed
    -	 */
    -	public function loadPostData($key,$values)
    -	{
    -		$uniqueGroupName=$this->getUniqueGroupName();
    -		$value=isset($values[$uniqueGroupName])?$values[$uniqueGroupName]:null;
    -		if($value!==null && $value===$this->getValueAttribute())
    -		{
    -			if(!$this->getChecked())
    -			{
    -				$this->setChecked(true);
    -				return true;
    -			}
    -			else
    -				return false;
    -		}
    -		else if($this->getChecked())
    -			$this->setChecked(false);
    -		return false;
    -	}
    -
    -	/**
    -	 * @return string the name of the group that the radio button belongs to. Defaults to empty.
    -	 */
    -	public function getGroupName()
    -	{
    -		return $this->getViewState('GroupName','');
    -	}
    -
    -	/**
    -	 * Sets the name of the group that the radio button belongs to.
    -	 * The group is unique among the control's naming container.
    -	 * @param string the group name
    -	 * @see setUniqueGroupName
    -	 */
    -	public function setGroupName($value)
    -	{
    -		$this->setViewState('GroupName',$value,'');
    -		$this->_uniqueGroupName=null;
    -	}
    -
    -	/**
    -	 * Add the group name as post data loader if group name is set.
    -	 */
    -	protected function addToPostDataLoader()
    -	{
    -		parent::addToPostDataLoader();
    -		$group = $this->getGroupName();
    -		if(!empty($group) || $this->getViewState('UniqueGroupName','') !== '')
    -			$this->getPage()->registerPostDataLoader($this->getUniqueGroupName());
    -	}
    -	/**
    -	 * @return string the name used to fetch radiobutton post data
    -	 */
    -	public function getUniqueGroupName()
    -	{
    -		if(($groupName=$this->getViewState('UniqueGroupName',''))!=='')
    -			return $groupName;
    -		else if(($uniqueID=$this->getUniqueID())!==$this->_previousUniqueID || $this->_uniqueGroupName===null)
    -		{
    -			$groupName=$this->getGroupName();
    -			$this->_previousUniqueID=$uniqueID;
    -			if($uniqueID!=='')
    -			{
    -				if(($pos=strrpos($uniqueID,TControl::ID_SEPARATOR))!==false)
    -				{
    -					if($groupName!=='')
    -						$groupName=substr($uniqueID,0,$pos+1).$groupName;
    -					else if($this->getNamingContainer() instanceof TRadioButtonList)
    -						$groupName=substr($uniqueID,0,$pos);
    -				}
    -				if($groupName==='')
    -					$groupName=$uniqueID;
    -			}
    -			$this->_uniqueGroupName=$groupName;
    -		}
    -		return $this->_uniqueGroupName;
    -	}
    -
    -	/**
    -	 * Sets the unique group name that the radio button belongs to.
    -	 * A unique group is a radiobutton group unique among the whole page hierarchy,
    -	 * while the {@link setGroupName GroupName} specifies a group that is unique
    -	 * among the control's naming container only.
    -	 * For example, each cell of a {@link TDataGrid} is a naming container.
    -	 * If you specify {@link setGroupName GroupName} for a radiobutton in a cell,
    -	 * it groups together radiobutton within a cell, but not the other, even though
    -	 * they have the same {@link setGroupName GroupName}.
    -	 * On the contratry, if {@link setUniqueGroupName UniqueGroupName} is used instead,
    -	 * it will group all appropriate radio buttons on the whole page hierarchy.
    -	 * Note, when both {@link setUniqueGroupName UniqueGroupName} and
    -	 * {@link setGroupName GroupName}, the former takes precedence.
    -	 * @param string the group name
    -	 * @see setGroupName
    -	 */
    -	public function setUniqueGroupName($value)
    -	{
    -		$this->setViewState('UniqueGroupName',$value,'');
    -	}
    -
    -	/**
    -	 * Gets an array of radiobuttons whose group name is the same as this radiobutton's.
    -	 * Note, only those radiobuttons that are on the current page hierarchy may be
    -	 * returned in the result.
    -	 * @return array list of TRadioButton with the same group
    -	 */
    -	public function getRadioButtonsInGroup()
    -	{
    -		$group = $this->getUniqueGroupName();
    -		$buttons = array();
    -		foreach(self::$_activeButtons as $control)
    -		{
    -			if($control->getUniqueGroupName() === $group)
    -				$buttons[] = $control;
    -		}
    -		return $buttons;
    -	}
    -
    -	/**
    -	 * @return string the value attribute to be rendered
    -	 */
    -	protected function getValueAttribute()
    -	{
    -		if(($value=parent::getValueAttribute())==='')
    -			return $this->getUniqueID();
    -		else
    -			return $value;
    -	}
    -
    -	/**
    -	 * @return boolean whether to render javascript.
    -	 */
    -	public function getEnableClientScript()
    -	{
    -		return $this->getViewState('EnableClientScript',true);
    -	}
    -
    -	/**
    -	 * @param boolean whether to render javascript.
    -	 */
    -	public function setEnableClientScript($value)
    -	{
    -		$this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
    -	}
    -
    -	/**
    -	 * Renders a radiobutton input element.
    -	 * @param THtmlWriter the writer for the rendering purpose
    -	 * @param string checkbox id
    -	 * @param string onclick js
    -	 */
    -	protected function renderInputTag($writer,$clientID,$onclick)
    -	{
    -		if($clientID!=='')
    -			$writer->addAttribute('id',$clientID);
    -		$writer->addAttribute('type','radio');
    -		$writer->addAttribute('name',$this->getUniqueGroupName());
    -		$writer->addAttribute('value',$this->getValueAttribute());
    -		if(!empty($onclick))
    -			$writer->addAttribute('onclick',$onclick);
    -		if($this->getChecked())
    -			$writer->addAttribute('checked','checked');
    -		if(!$this->getEnabled(true))
    -			$writer->addAttribute('disabled','disabled');
    -
    -		$page=$this->getPage();
    -		if($this->getEnabled(true)
    -			&& $this->getEnableClientScript()
    -			&& $this->getAutoPostBack()
    -			&& $page->getClientSupportsJavaScript())
    -		{
    -			$this->renderClientControlScript($writer);
    -		}
    -
    -		if(($accesskey=$this->getAccessKey())!=='')
    -			$writer->addAttribute('accesskey',$accesskey);
    -		if(($tabindex=$this->getTabIndex())>0)
    -			$writer->addAttribute('tabindex',"$tabindex");
    -		if($attributes=$this->getViewState('InputAttributes',null))
    -			$writer->addAttributes($attributes);
    -		$writer->renderBeginTag('input');
    -		$writer->renderEndTag();
    -	}
    -
    -	/**
    -	 * Renders the client-script code.
    -	 */
    -	protected function renderClientControlScript($writer)
    -	{
    -		$cs = $this->getPage()->getClientScript();
    -		$cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions());
    -	}
    -
    -	/**
    -	 * 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.TRadioButton';
    -	}
    -}
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Using TCheckBox parent class
    + */
    +Prado::using('System.Web.UI.WebControls.TCheckBox');
    +/**
    + * Using TRadioButtonList class
    + */
    +Prado::using('System.Web.UI.WebControls.TRadioButtonList');
    +
    +/**
    + * TRadioButton class
    + *
    + * TRadioButton displays a radio button on the page.
    + * You can specify the caption to display beside the radio buttonby setting
    + * the {@link setText Text} property.  The caption can appear either on the right
    + * or left of the radio button, which is determined by the {@link setTextAlign TextAlign}
    + * property.
    + *
    + * To determine whether the TRadioButton component is checked, test the {@link getChecked Checked}
    + * property. The {@link onCheckedChanged OnCheckedChanged} event is raised when
    + * the {@link getChecked Checked} state of the TRadioButton component changes
    + * between posts to the server. You can provide an event handler for
    + * the {@link onCheckedChanged OnCheckedChanged} event to  to programmatically
    + * control the actions performed when the state of the TRadioButton component changes
    + * between posts to the server.
    + *
    + * TRadioButton uses {@link setGroupName GroupName} to group together a set of radio buttons.
    + * Once the {@link setGroupName GroupName} is set, you can use the {@link getRadioButtonsInGroup}
    + * method to get an array of TRadioButtons having the same group name.
    + *
    + * If {@link setAutoPostBack AutoPostBack} is set true, changing the radio button state
    + * will cause postback action. And if {@link setCausesValidation CausesValidation}
    + * is true, validation will also be processed, which can be further restricted within
    + * a {@link setValidationGroup ValidationGroup}.
    + *
    + * Note, {@link setText Text} is rendered as is. Make sure it does not contain unwanted characters
    + * that may bring security vulnerabilities.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TRadioButton extends TCheckBox
    +{
    +	/**
    +	 * @param array list of radio buttons that are on the current page hierarchy
    +	 */
    +	private static $_activeButtons=array();
    +	/**
    +	 * @var integer number of radio buttons created
    +	 */
    +	private static $_buttonCount=0;
    +	/**
    +	 * @var integer global ID of this radiobutton
    +	 */
    +	private $_globalID;
    +	/**
    +	 * @var string previous UniqueID (used to calculate UniqueGroup)
    +	 */
    +	private $_previousUniqueID=null;
    +	/**
    +	 * @var string the name used to fetch radiobutton post data
    +	 */
    +	private $_uniqueGroupName=null;
    +
    +	/**
    +	 * Constructor.
    +	 * Registers the radiobutton in a global radiobutton collection.
    +	 * If overridden, the parent implementation must be invoked first.
    +	 */
    +	public function __construct()
    +	{
    +		parent::__construct();
    +		$this->_globalID = self::$_buttonCount++;
    +	}
    +
    +	/**
    +	 * Registers the radio button groupings. If overriding onInit method,
    +	 * ensure to call parent implemenation.
    +	 * @param TEventParameter event parameter to be passed to the event handlers
    +	 */
    +	public function onInit($param)
    +	{
    +		parent::onInit($param);
    +		self::$_activeButtons[$this->_globalID]=$this;
    +	}
    +
    +	/**
    +	 * Unregisters the radio button groupings. If overriding onInit method,
    +	 * ensure to call parent implemenation.
    +	 * @param TEventParameter event parameter to be passed to the event handlers
    +	 */
    +	public function onUnLoad($param)
    +	{
    +		unset(self::$_activeButtons[$this->_globalID]);
    +		parent::onUnLoad($param);
    +	}
    +
    +	/**
    +	 * Loads user input data.
    +	 * This method is primarly used by framework developers.
    +	 * @param string the key that can be used to retrieve data from the input data collection
    +	 * @param array the input data collection
    +	 * @return boolean whether the data of the control has been changed
    +	 */
    +	public function loadPostData($key,$values)
    +	{
    +		$uniqueGroupName=$this->getUniqueGroupName();
    +		$value=isset($values[$uniqueGroupName])?$values[$uniqueGroupName]:null;
    +		if($value!==null && $value===$this->getValueAttribute())
    +		{
    +			if(!$this->getChecked())
    +			{
    +				$this->setChecked(true);
    +				return true;
    +			}
    +			else
    +				return false;
    +		}
    +		else if($this->getChecked())
    +			$this->setChecked(false);
    +		return false;
    +	}
    +
    +	/**
    +	 * @return string the name of the group that the radio button belongs to. Defaults to empty.
    +	 */
    +	public function getGroupName()
    +	{
    +		return $this->getViewState('GroupName','');
    +	}
    +
    +	/**
    +	 * Sets the name of the group that the radio button belongs to.
    +	 * The group is unique among the control's naming container.
    +	 * @param string the group name
    +	 * @see setUniqueGroupName
    +	 */
    +	public function setGroupName($value)
    +	{
    +		$this->setViewState('GroupName',$value,'');
    +		$this->_uniqueGroupName=null;
    +	}
    +
    +	/**
    +	 * Add the group name as post data loader if group name is set.
    +	 */
    +	protected function addToPostDataLoader()
    +	{
    +		parent::addToPostDataLoader();
    +		$group = $this->getGroupName();
    +		if(!empty($group) || $this->getViewState('UniqueGroupName','') !== '')
    +			$this->getPage()->registerPostDataLoader($this->getUniqueGroupName());
    +	}
    +	/**
    +	 * @return string the name used to fetch radiobutton post data
    +	 */
    +	public function getUniqueGroupName()
    +	{
    +		if(($groupName=$this->getViewState('UniqueGroupName',''))!=='')
    +			return $groupName;
    +		else if(($uniqueID=$this->getUniqueID())!==$this->_previousUniqueID || $this->_uniqueGroupName===null)
    +		{
    +			$groupName=$this->getGroupName();
    +			$this->_previousUniqueID=$uniqueID;
    +			if($uniqueID!=='')
    +			{
    +				if(($pos=strrpos($uniqueID,TControl::ID_SEPARATOR))!==false)
    +				{
    +					if($groupName!=='')
    +						$groupName=substr($uniqueID,0,$pos+1).$groupName;
    +					else if($this->getNamingContainer() instanceof TRadioButtonList)
    +						$groupName=substr($uniqueID,0,$pos);
    +				}
    +				if($groupName==='')
    +					$groupName=$uniqueID;
    +			}
    +			$this->_uniqueGroupName=$groupName;
    +		}
    +		return $this->_uniqueGroupName;
    +	}
    +
    +	/**
    +	 * Sets the unique group name that the radio button belongs to.
    +	 * A unique group is a radiobutton group unique among the whole page hierarchy,
    +	 * while the {@link setGroupName GroupName} specifies a group that is unique
    +	 * among the control's naming container only.
    +	 * For example, each cell of a {@link TDataGrid} is a naming container.
    +	 * If you specify {@link setGroupName GroupName} for a radiobutton in a cell,
    +	 * it groups together radiobutton within a cell, but not the other, even though
    +	 * they have the same {@link setGroupName GroupName}.
    +	 * On the contratry, if {@link setUniqueGroupName UniqueGroupName} is used instead,
    +	 * it will group all appropriate radio buttons on the whole page hierarchy.
    +	 * Note, when both {@link setUniqueGroupName UniqueGroupName} and
    +	 * {@link setGroupName GroupName}, the former takes precedence.
    +	 * @param string the group name
    +	 * @see setGroupName
    +	 */
    +	public function setUniqueGroupName($value)
    +	{
    +		$this->setViewState('UniqueGroupName',$value,'');
    +	}
    +
    +	/**
    +	 * Gets an array of radiobuttons whose group name is the same as this radiobutton's.
    +	 * Note, only those radiobuttons that are on the current page hierarchy may be
    +	 * returned in the result.
    +	 * @return array list of TRadioButton with the same group
    +	 */
    +	public function getRadioButtonsInGroup()
    +	{
    +		$group = $this->getUniqueGroupName();
    +		$buttons = array();
    +		foreach(self::$_activeButtons as $control)
    +		{
    +			if($control->getUniqueGroupName() === $group)
    +				$buttons[] = $control;
    +		}
    +		return $buttons;
    +	}
    +
    +	/**
    +	 * @return string the value attribute to be rendered
    +	 */
    +	protected function getValueAttribute()
    +	{
    +		if(($value=parent::getValueAttribute())==='')
    +			return $this->getUniqueID();
    +		else
    +			return $value;
    +	}
    +
    +	/**
    +	 * @return boolean whether to render javascript.
    +	 */
    +	public function getEnableClientScript()
    +	{
    +		return $this->getViewState('EnableClientScript',true);
    +	}
    +
    +	/**
    +	 * @param boolean whether to render javascript.
    +	 */
    +	public function setEnableClientScript($value)
    +	{
    +		$this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
    +	}
    +
    +	/**
    +	 * Renders a radiobutton input element.
    +	 * @param THtmlWriter the writer for the rendering purpose
    +	 * @param string checkbox id
    +	 * @param string onclick js
    +	 */
    +	protected function renderInputTag($writer,$clientID,$onclick)
    +	{
    +		if($clientID!=='')
    +			$writer->addAttribute('id',$clientID);
    +		$writer->addAttribute('type','radio');
    +		$writer->addAttribute('name',$this->getUniqueGroupName());
    +		$writer->addAttribute('value',$this->getValueAttribute());
    +		if(!empty($onclick))
    +			$writer->addAttribute('onclick',$onclick);
    +		if($this->getChecked())
    +			$writer->addAttribute('checked','checked');
    +		if(!$this->getEnabled(true))
    +			$writer->addAttribute('disabled','disabled');
    +
    +		$page=$this->getPage();
    +		if($this->getEnabled(true)
    +			&& $this->getEnableClientScript()
    +			&& $this->getAutoPostBack()
    +			&& $page->getClientSupportsJavaScript())
    +		{
    +			$this->renderClientControlScript($writer);
    +		}
    +
    +		if(($accesskey=$this->getAccessKey())!=='')
    +			$writer->addAttribute('accesskey',$accesskey);
    +		if(($tabindex=$this->getTabIndex())>0)
    +			$writer->addAttribute('tabindex',"$tabindex");
    +		if($attributes=$this->getViewState('InputAttributes',null))
    +			$writer->addAttributes($attributes);
    +		$writer->renderBeginTag('input');
    +		$writer->renderEndTag();
    +	}
    +
    +	/**
    +	 * Renders the client-script code.
    +	 */
    +	protected function renderClientControlScript($writer)
    +	{
    +		$cs = $this->getPage()->getClientScript();
    +		$cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions());
    +	}
    +
    +	/**
    +	 * 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.TRadioButton';
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TRangeValidator.php b/framework/Web/UI/WebControls/TRangeValidator.php
    index d8d2d20d..57538e88 100644
    --- a/framework/Web/UI/WebControls/TRangeValidator.php
    +++ b/framework/Web/UI/WebControls/TRangeValidator.php
    @@ -1,360 +1,360 @@
    -
    - * @link http://www.pradosoft.com/
    +
    + * @link http://www.pradosoft.com/
      * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Using TBaseValidator class
    - */
    -Prado::using('System.Web.UI.WebControls.TBaseValidator');
    -
    -/**
    - * TRangeValidator class
    - *
    - * TRangeValidator tests whether an input value is within a specified range.
    - *
    - * TRangeValidator uses three key properties to perform its validation.
    - * The {@link setMinValue MinValue} and {@link setMaxValue MaxValue}
    - * properties specify the minimum and maximum values of the valid range.
    - * The {@link setDataType DataType} property is used to specify the
    - * data type of the value and the minimum and maximum range values.
    - * These values are converted to this data type before the validation
    - * operation is performed. The following value types are supported:
    - * - Integer A 32-bit signed integer data type.
    - * - Float A double-precision floating point number data type.
    - * - Date A date data type. The date format can be specified by
    - *   setting {@link setDateFormat DateFormat} property, which must be recognizable
    - *   by {@link TSimpleDateFormatter}. If the property is not set,
    - *   the GNU date syntax is assumed.
    - * - String A string data type.
    - * - StringLength check for string length.
    - *
    - * If {@link setStrictComparison StrictComparison} is true, then the ranges
    - * are compared as strictly less than the max value and/or strictly greater than the min value.
    - *
    - * The TRangeValidator allows a special DataType "StringLength" that
    - * can be used to verify minimum and maximum string length. The
    - * {@link setCharset Charset} property can be used to force a particular
    - * charset for comparison. Otherwise, the application charset is used and is
    - * defaulted as UTF-8.
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TRangeValidator extends TBaseValidator
    -{
    -	/**
    -	 * Gets the name of the javascript class responsible for performing validation for this control.
    -	 * This method overrides the parent implementation.
    -	 * @return string the javascript class name
    -	 */
    -	protected function getClientClassName()
    -	{
    -		return 'Prado.WebUI.TRangeValidator';
    -	}
    -
    -	/**
    -	 * @return string the minimum value of the validation range.
    -	 */
    -	public function getMinValue()
    -	{
    -		return $this->getViewState('MinValue','');
    -	}
    -
    -	/**
    -	 * Sets the minimum value of the validation range.
    -	 * @param string the minimum value
    -	 */
    -	public function setMinValue($value)
    -	{
    -		$this->setViewState('MinValue',TPropertyValue::ensureString($value),'');
    -	}
    -
    -	/**
    -	 * @return string the maximum value of the validation range.
    -	 */
    -	public function getMaxValue()
    -	{
    -		return $this->getViewState('MaxValue','');
    -	}
    -
    -	/**
    -	 * Sets the maximum value of the validation range.
    -	 * @param string the maximum value
    -	 */
    -	public function setMaxValue($value)
    -	{
    -		$this->setViewState('MaxValue',TPropertyValue::ensureString($value),'');
    -	}
    -
    -	/**
    -	 * @param boolean true to perform strict comparison (i.e. strictly less than max and/or strictly greater than min).
    -	 */
    -	public function setStrictComparison($value)
    -	{
    -		$this->setViewState('StrictComparison', TPropertyValue::ensureBoolean($value),false);
    -	}
    -
    -	/**
    -	 * @return boolean true to perform strict comparison.
    -	 */
    -	public function getStrictComparison()
    -	{
    -		return $this->getViewState('StrictComparison', false);
    -	}
    -
    -	/**
    -	 * @return TRangeValidationDataType the data type that the values being compared are
    -	 * converted to before the comparison is made. Defaults to TRangeValidationDataType::String.
    -	 */
    -	public function getDataType()
    -	{
    -		return $this->getViewState('DataType',TRangeValidationDataType::String);
    -	}
    -
    -	/**
    -	 * Sets the data type that the values being compared are converted to before the comparison is made.
    -	 * @param TRangeValidationDataType the data type
    -	 */
    -	public function setDataType($value)
    -	{
    -		$this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TRangeValidationDataType'),TRangeValidationDataType::String);
    -	}
    -
    -	/**
    -     * Sets the date format for a date validation
    -     * @param string the date format value
    -     */
    -	public function setDateFormat($value)
    -	{
    -		$this->setViewState('DateFormat', $value, '');
    -	}
    -
    -	/**
    -	 * @return string the date validation date format if any
    -	 */
    -	public function getDateFormat()
    -	{
    -		return $this->getViewState('DateFormat', '');
    -	}
    -
    -	/**
    -	 * @param string charset for string length comparison.
    -	 */
    -	public function setCharset($value)
    -	{
    -		$this->setViewState('Charset', $value, '');
    -	}
    -
    -	/**
    -	 * @return string charset for string length comparison.
    -	 */
    -	public function getCharset()
    -	{
    -		return $this->getViewState('Charset', '');
    -	}
    -
    -	/**
    -	 * This method overrides the parent's implementation.
    -	 * The validation succeeds if the input data is within the range.
    -	 * The validation always succeeds if the input data is empty.
    -	 * @return boolean whether the validation succeeds
    -	 */
    -	protected function evaluateIsValid()
    -	{
    -		$value=$this->getValidationValue($this->getValidationTarget());
    -		if($value==='')
    -			return true;
    -
    -		switch($this->getDataType())
    -		{
    -			case TRangeValidationDataType::Integer:
    -				return $this->isValidInteger($value);
    -			case TRangeValidationDataType::Float:
    -				return $this->isValidFloat($value);
    -			case TRangeValidationDataType::Date:
    -				return $this->isValidDate($value);
    -			case TRangeValidationDataType::StringLength:
    -				return $this->isValidStringLength($value);
    -			default:
    -				return $this->isValidString($value);
    -		}
    -	}
    -
    -	/**
    -	* Determine if the value is within the integer range.
    -	* @param string value to validate true
    -	* @return boolean true if within integer range.
    -	*/
    -	protected function isValidInteger($value)
    -	{
    -		$minValue=$this->getMinValue();
    -		$maxValue=$this->getMaxValue();
    -
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Using TBaseValidator class
    + */
    +Prado::using('System.Web.UI.WebControls.TBaseValidator');
    +
    +/**
    + * TRangeValidator class
    + *
    + * TRangeValidator tests whether an input value is within a specified range.
    + *
    + * TRangeValidator uses three key properties to perform its validation.
    + * The {@link setMinValue MinValue} and {@link setMaxValue MaxValue}
    + * properties specify the minimum and maximum values of the valid range.
    + * The {@link setDataType DataType} property is used to specify the
    + * data type of the value and the minimum and maximum range values.
    + * These values are converted to this data type before the validation
    + * operation is performed. The following value types are supported:
    + * - Integer A 32-bit signed integer data type.
    + * - Float A double-precision floating point number data type.
    + * - Date A date data type. The date format can be specified by
    + *   setting {@link setDateFormat DateFormat} property, which must be recognizable
    + *   by {@link TSimpleDateFormatter}. If the property is not set,
    + *   the GNU date syntax is assumed.
    + * - String A string data type.
    + * - StringLength check for string length.
    + *
    + * If {@link setStrictComparison StrictComparison} is true, then the ranges
    + * are compared as strictly less than the max value and/or strictly greater than the min value.
    + *
    + * The TRangeValidator allows a special DataType "StringLength" that
    + * can be used to verify minimum and maximum string length. The
    + * {@link setCharset Charset} property can be used to force a particular
    + * charset for comparison. Otherwise, the application charset is used and is
    + * defaulted as UTF-8.
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TRangeValidator extends TBaseValidator
    +{
    +	/**
    +	 * Gets the name of the javascript class responsible for performing validation for this control.
    +	 * This method overrides the parent implementation.
    +	 * @return string the javascript class name
    +	 */
    +	protected function getClientClassName()
    +	{
    +		return 'Prado.WebUI.TRangeValidator';
    +	}
    +
    +	/**
    +	 * @return string the minimum value of the validation range.
    +	 */
    +	public function getMinValue()
    +	{
    +		return $this->getViewState('MinValue','');
    +	}
    +
    +	/**
    +	 * Sets the minimum value of the validation range.
    +	 * @param string the minimum value
    +	 */
    +	public function setMinValue($value)
    +	{
    +		$this->setViewState('MinValue',TPropertyValue::ensureString($value),'');
    +	}
    +
    +	/**
    +	 * @return string the maximum value of the validation range.
    +	 */
    +	public function getMaxValue()
    +	{
    +		return $this->getViewState('MaxValue','');
    +	}
    +
    +	/**
    +	 * Sets the maximum value of the validation range.
    +	 * @param string the maximum value
    +	 */
    +	public function setMaxValue($value)
    +	{
    +		$this->setViewState('MaxValue',TPropertyValue::ensureString($value),'');
    +	}
    +
    +	/**
    +	 * @param boolean true to perform strict comparison (i.e. strictly less than max and/or strictly greater than min).
    +	 */
    +	public function setStrictComparison($value)
    +	{
    +		$this->setViewState('StrictComparison', TPropertyValue::ensureBoolean($value),false);
    +	}
    +
    +	/**
    +	 * @return boolean true to perform strict comparison.
    +	 */
    +	public function getStrictComparison()
    +	{
    +		return $this->getViewState('StrictComparison', false);
    +	}
    +
    +	/**
    +	 * @return TRangeValidationDataType the data type that the values being compared are
    +	 * converted to before the comparison is made. Defaults to TRangeValidationDataType::String.
    +	 */
    +	public function getDataType()
    +	{
    +		return $this->getViewState('DataType',TRangeValidationDataType::String);
    +	}
    +
    +	/**
    +	 * Sets the data type that the values being compared are converted to before the comparison is made.
    +	 * @param TRangeValidationDataType the data type
    +	 */
    +	public function setDataType($value)
    +	{
    +		$this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TRangeValidationDataType'),TRangeValidationDataType::String);
    +	}
    +
    +	/**
    +     * Sets the date format for a date validation
    +     * @param string the date format value
    +     */
    +	public function setDateFormat($value)
    +	{
    +		$this->setViewState('DateFormat', $value, '');
    +	}
    +
    +	/**
    +	 * @return string the date validation date format if any
    +	 */
    +	public function getDateFormat()
    +	{
    +		return $this->getViewState('DateFormat', '');
    +	}
    +
    +	/**
    +	 * @param string charset for string length comparison.
    +	 */
    +	public function setCharset($value)
    +	{
    +		$this->setViewState('Charset', $value, '');
    +	}
    +
    +	/**
    +	 * @return string charset for string length comparison.
    +	 */
    +	public function getCharset()
    +	{
    +		return $this->getViewState('Charset', '');
    +	}
    +
    +	/**
    +	 * This method overrides the parent's implementation.
    +	 * The validation succeeds if the input data is within the range.
    +	 * The validation always succeeds if the input data is empty.
    +	 * @return boolean whether the validation succeeds
    +	 */
    +	protected function evaluateIsValid()
    +	{
    +		$value=$this->getValidationValue($this->getValidationTarget());
    +		if($value==='')
    +			return true;
    +
    +		switch($this->getDataType())
    +		{
    +			case TRangeValidationDataType::Integer:
    +				return $this->isValidInteger($value);
    +			case TRangeValidationDataType::Float:
    +				return $this->isValidFloat($value);
    +			case TRangeValidationDataType::Date:
    +				return $this->isValidDate($value);
    +			case TRangeValidationDataType::StringLength:
    +				return $this->isValidStringLength($value);
    +			default:
    +				return $this->isValidString($value);
    +		}
    +	}
    +
    +	/**
    +	* Determine if the value is within the integer range.
    +	* @param string value to validate true
    +	* @return boolean true if within integer range.
    +	*/
    +	protected function isValidInteger($value)
    +	{
    +		$minValue=$this->getMinValue();
    +		$maxValue=$this->getMaxValue();
    +
     		$valid=preg_match('/^[-+]?[0-9]+$/',trim($value));
    -		$value=intval($value);
    -		if($minValue!=='')
    -			$valid=$valid && $this->isGreaterThan($value, intval($minValue));
    -		if($maxValue!=='')
    -			$valid=$valid && $this->isLessThan($value,intval($maxValue));
    -		return $valid;
    -	}
    -
    -	protected function isLessThan($left,$right)
    -	{
    -		return $this->getStrictComparison() ? $left < $right : $left <= $right;
    -	}
    -
    -	protected function isGreaterThan($left, $right)
    -	{
    -		return $this->getStrictComparison() ? $left > $right : $left >= $right;
    -	}
    -
    -	/**
    -	 * Determine if the value is within the specified float range.
    -	 * @param string value to validate
    -	 * @return boolean true if within range.
    -	 */
    -	protected function isValidFloat($value)
    -	{
    -		$minValue=$this->getMinValue();
    -		$maxValue=$this->getMaxValue();
    -
    -		$valid=preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value));
    -		$value=floatval($value);
    -		if($minValue!=='')
    -			$valid=$valid && $this->isGreaterThan($value,floatval($minValue));
    -		if($maxValue!=='')
    -			$valid=$valid && $this->isLessThan($value,floatval($maxValue));
    -		return $valid;
    -	}
    -
    -	/**
    -	 * Determine if the date is within the specified range.
    -	 * Uses pradoParseDate and strtotime to get the date from string.
    -	 * @param string date as string to validate
    -	 * @return boolean true if within range.
    -	 */
    -	protected function isValidDate($value)
    -	{
    -		$minValue=$this->getMinValue();
    -		$maxValue=$this->getMaxValue();
    -
    -		$valid=true;
    -
    -		$dateFormat = $this->getDateFormat();
    -		if($dateFormat!=='')
    -		{
    -			$formatter=Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat);
    -			$value = $formatter->parse($value, $dateFormat);
    -			if($minValue!=='')
    -				$valid=$valid && $this->isGreaterThan($value,$formatter->parse($minValue));
    -			if($maxValue!=='')
    -				$valid=$valid && $this->isLessThan($value,$formatter->parse($maxValue));
    -			return $valid;
    -		}
    -		else
    -		{
    -			$value=strtotime($value);
    -			if($minValue!=='')
    -				$valid=$valid && $this->isGreaterThan($value,strtotime($minValue));
    -			if($maxValue!=='')
    -				$valid=$valid && $this->isLessThan($value,strtotime($maxValue));
    -			return $valid;
    -		}
    -	}
    -
    -	/**
    -	 * Compare the string with a minimum and a maxiumum value.
    -	 * Uses strcmp for comparision.
    -	 * @param string value to compare with.
    -	 * @return boolean true if the string is within range.
    -	 */
    -	protected function isValidString($value)
    -	{
    -		$minValue=$this->getMinValue();
    -		$maxValue=$this->getMaxValue();
    -
    -		$valid=true;
    -		if($minValue!=='')
    -			$valid=$valid && $this->isGreaterThan(strcmp($value,$minValue),0);
    -		if($maxValue!=='')
    -			$valid=$valid && $this->isLessThan(strcmp($value,$maxValue),0);
    -		return $valid;
    -	}
    -
    -	/**
    -	 * @param string string for comparision
    -	 * @return boolean true if min and max string length are satisfied.
    -	 */
    -	protected function isValidStringLength($value)
    -	{
    -		$minValue=$this->getMinValue();
    -		$maxValue=$this->getMaxValue();
    -
    -		$valid=true;
    -		$charset = $this->getCharset();
    -		if($charset==='')
    -		{
    -			$app= $this->getApplication()->getGlobalization();
    -			$charset = $app ? $app->getCharset() : null;
    -			if(!$charset)
    -				$charset = 'UTF-8';
    -		}
    -
    -		$length = iconv_strlen($value, $charset);
    -		if($minValue!=='')
    -			$valid = $valid && $this->isGreaterThan($length,intval($minValue));
    -		if($maxValue!=='')
    -			$valid = $valid && $this->isLessThan($length,intval($maxValue));
    -		return $valid;
    -	}
    -
    -	/**
    -	 * Returns an array of javascript validator options.
    -	 * @return array javascript validator options.
    -	 */
    -	protected function getClientScriptOptions()
    -	{
    -		$options=parent::getClientScriptOptions();
    -		$options['MinValue']=$this->getMinValue();
    -		$options['MaxValue']=$this->getMaxValue();
    -		$options['DataType']=$this->getDataType();
    -		$options['StrictComparison']=$this->getStrictComparison();
    -		if(($dateFormat=$this->getDateFormat())!=='')
    -			$options['DateFormat']=$dateFormat;
    -		return $options;
    -	}
    -}
    -
    -
    -/**
    - * TRangeValidationDataType class.
    - * TRangeValidationDataType defines the enumerable type for the possible data types that
    - * a range validator can validate upon.
    - *
    - * The following enumerable values are defined:
    - * - Integer
    - * - Float
    - * - Date
    - * - String
    - * - StringLength
    - *
    - * @author Qiang Xue 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0.4
    - */
    -class TRangeValidationDataType extends TValidationDataType
    -{
    -	const StringLength='StringLength';
    -}
    +		$value=intval($value);
    +		if($minValue!=='')
    +			$valid=$valid && $this->isGreaterThan($value, intval($minValue));
    +		if($maxValue!=='')
    +			$valid=$valid && $this->isLessThan($value,intval($maxValue));
    +		return $valid;
    +	}
    +
    +	protected function isLessThan($left,$right)
    +	{
    +		return $this->getStrictComparison() ? $left < $right : $left <= $right;
    +	}
    +
    +	protected function isGreaterThan($left, $right)
    +	{
    +		return $this->getStrictComparison() ? $left > $right : $left >= $right;
    +	}
    +
    +	/**
    +	 * Determine if the value is within the specified float range.
    +	 * @param string value to validate
    +	 * @return boolean true if within range.
    +	 */
    +	protected function isValidFloat($value)
    +	{
    +		$minValue=$this->getMinValue();
    +		$maxValue=$this->getMaxValue();
    +
    +		$valid=preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value));
    +		$value=floatval($value);
    +		if($minValue!=='')
    +			$valid=$valid && $this->isGreaterThan($value,floatval($minValue));
    +		if($maxValue!=='')
    +			$valid=$valid && $this->isLessThan($value,floatval($maxValue));
    +		return $valid;
    +	}
    +
    +	/**
    +	 * Determine if the date is within the specified range.
    +	 * Uses pradoParseDate and strtotime to get the date from string.
    +	 * @param string date as string to validate
    +	 * @return boolean true if within range.
    +	 */
    +	protected function isValidDate($value)
    +	{
    +		$minValue=$this->getMinValue();
    +		$maxValue=$this->getMaxValue();
    +
    +		$valid=true;
    +
    +		$dateFormat = $this->getDateFormat();
    +		if($dateFormat!=='')
    +		{
    +			$formatter=Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat);
    +			$value = $formatter->parse($value, $dateFormat);
    +			if($minValue!=='')
    +				$valid=$valid && $this->isGreaterThan($value,$formatter->parse($minValue));
    +			if($maxValue!=='')
    +				$valid=$valid && $this->isLessThan($value,$formatter->parse($maxValue));
    +			return $valid;
    +		}
    +		else
    +		{
    +			$value=strtotime($value);
    +			if($minValue!=='')
    +				$valid=$valid && $this->isGreaterThan($value,strtotime($minValue));
    +			if($maxValue!=='')
    +				$valid=$valid && $this->isLessThan($value,strtotime($maxValue));
    +			return $valid;
    +		}
    +	}
    +
    +	/**
    +	 * Compare the string with a minimum and a maxiumum value.
    +	 * Uses strcmp for comparision.
    +	 * @param string value to compare with.
    +	 * @return boolean true if the string is within range.
    +	 */
    +	protected function isValidString($value)
    +	{
    +		$minValue=$this->getMinValue();
    +		$maxValue=$this->getMaxValue();
    +
    +		$valid=true;
    +		if($minValue!=='')
    +			$valid=$valid && $this->isGreaterThan(strcmp($value,$minValue),0);
    +		if($maxValue!=='')
    +			$valid=$valid && $this->isLessThan(strcmp($value,$maxValue),0);
    +		return $valid;
    +	}
    +
    +	/**
    +	 * @param string string for comparision
    +	 * @return boolean true if min and max string length are satisfied.
    +	 */
    +	protected function isValidStringLength($value)
    +	{
    +		$minValue=$this->getMinValue();
    +		$maxValue=$this->getMaxValue();
    +
    +		$valid=true;
    +		$charset = $this->getCharset();
    +		if($charset==='')
    +		{
    +			$app= $this->getApplication()->getGlobalization();
    +			$charset = $app ? $app->getCharset() : null;
    +			if(!$charset)
    +				$charset = 'UTF-8';
    +		}
    +
    +		$length = iconv_strlen($value, $charset);
    +		if($minValue!=='')
    +			$valid = $valid && $this->isGreaterThan($length,intval($minValue));
    +		if($maxValue!=='')
    +			$valid = $valid && $this->isLessThan($length,intval($maxValue));
    +		return $valid;
    +	}
    +
    +	/**
    +	 * Returns an array of javascript validator options.
    +	 * @return array javascript validator options.
    +	 */
    +	protected function getClientScriptOptions()
    +	{
    +		$options=parent::getClientScriptOptions();
    +		$options['MinValue']=$this->getMinValue();
    +		$options['MaxValue']=$this->getMaxValue();
    +		$options['DataType']=$this->getDataType();
    +		$options['StrictComparison']=$this->getStrictComparison();
    +		if(($dateFormat=$this->getDateFormat())!=='')
    +			$options['DateFormat']=$dateFormat;
    +		return $options;
    +	}
    +}
    +
    +
    +/**
    + * TRangeValidationDataType class.
    + * TRangeValidationDataType defines the enumerable type for the possible data types that
    + * a range validator can validate upon.
    + *
    + * The following enumerable values are defined:
    + * - Integer
    + * - Float
    + * - Date
    + * - String
    + * - StringLength
    + *
    + * @author Qiang Xue 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0.4
    + */
    +class TRangeValidationDataType extends TValidationDataType
    +{
    +	const StringLength='StringLength';
    +}
    diff --git a/framework/Web/UI/WebControls/TRatingList.php b/framework/Web/UI/WebControls/TRatingList.php
    index dfb11371..cc813755 100644
    --- a/framework/Web/UI/WebControls/TRatingList.php
    +++ b/framework/Web/UI/WebControls/TRatingList.php
    @@ -1,359 +1,359 @@
    -
    - * @link http://www.pradosoft.com/
    - * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Includes TRadioButtonList class
    - */
    -Prado::using('System.Web.UI.WebControls.TRadioButtonList');
    -
    -/**
    - * TRatingList class.
    - *
    - * This class is EXPERIMENTAL.
    - *
    - * @author Wei Zhuo 
    - * @author Bradley Booms 
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - * @since 3.0
    - */
    -class TRatingList extends TRadioButtonList
    -{
    -	/**
    -	 * Script path relative to the TClientScriptManager::SCRIPT_PATH
    -	 */
    -	const SCRIPT_PATH='prado/ratings';
    -
    -	/**
    -	 * @var array list of published rating images.
    -	 */
    -	private $_ratingImages = array();
    -
    -	/**
    -	 * Sets the default repeat direction to horizontal.
    -	 */
    -	public function __construct()
    -	{
    -		parent::__construct();
    -		$this->setRepeatDirection(TRepeatDirection::Horizontal);
    -	}
    -
    -	/**
    -	 * @return boolean whether the items in the column can be edited. Defaults to false.
    -	 */
    -	public function getReadOnly()
    -	{
    -		return $this->getViewState('ReadOnly',false);
    -	}
    -
    -	/**
    -	 * @param boolean whether the items in the column can be edited
    -	 */
    -	public function setReadOnly($value)
    -	{
    -		$this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false);
    -	}
    -
    -	/**
    -	 * Wrapper for {@link setReadOnly ReadOnly} property.
    -	 * @return boolean whether the rating list can be edited. Defaults to true.
    -	 */
    -	public function getAllowInput()
    -	{
    -		return !$this->getReadOnly();
    -	}
    -
    -	/**
    -	 * Wrapper for {@link setReadOnly ReadOnly} property.
    -	 * @param boolean whether the rating list can be edited
    -	 */
    -	public function setAllowInput($value)
    -	{
    -		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
    -	}
    -
    -	/**
    -	 * Wrapper for {@link setReadOnly ReadOnly} property.
    -	 * @param boolean whether the rating list can be edited
    -	 */
    -	public function setEnabled($value)
    -	{
    -		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
    -	}
    -
    -	/**
    -	 * The repeat layout must be Table.
    -	 * @param string repeat layout type
    -	 * @throws TInvaliddataValueException when repeat layout is not Table.
    -	 */
    -	public function setRepeatLayout($value)
    -	{
    -		if($value!==TRepeatLayout::Table)
    -			throw new TInvalidDataValueException('ratinglist_table_layout_only');
    -		else
    -			parent::setRepeatLayout($value);
    -	}
    -
    -	/**
    -	 * @return float rating value.
    -	 */
    -	public function getRating()
    -	{
    -		$rating = $this->getViewState('Rating', null);
    -		if ($rating === null)
    -			return $this->getSelectedIndex()+1;
    -		else
    -			return $rating;
    -	}
    -
    -	/**
    -	 * @param float rating value, also sets the selected Index
    -	 */
    -	public function setRating($value)
    -	{
    -		$value = TPropertyValue::ensureFloat($value);
    -		$this->setViewState('Rating', $value, null);
    -		$index = $this->getRatingIndex($value);
    -		parent::setSelectedIndex($index);
    -	}
    -	
    -	public function setSelectedIndex($value)
    -	{
    -		$this->setRating($value+1);
    -		parent::setSelectedIndex($value);
    -	}
    -
    -	/**
    -	 * @param float rating value
    -	 * @return int rating as integer
    -	 */
    -	protected function getRatingIndex($rating)
    -	{
    -		$interval = $this->getHalfRatingInterval();
    -		$base = intval($rating)-1;
    -		$remainder = $rating-$base-1;
    -		return $remainder > $interval[1] ? $base+1 : $base;
    -	}
    -
    -	/**
    -	 * @param int change the rating selection index
    -	 */
    -	public function onSelectedIndexChanged($param)
    -	{
    -		$value = $this->getRating();
    -		$value = TPropertyValue::ensureInteger($value);
    -		$this->setRating($value);
    -		parent::onSelectedIndexChanged($param);
    -	}
    -
    -	/**
    -	 * @return string control or html element ID for displaying a caption.
    -	 */
    -	public function getCaptionID()
    -	{
    -		return $this->getViewState('CaptionID', '');
    -	}
    -
    -	/**
    -	 * @param string control or html element ID for displaying a caption.
    -	 */
    -	public function setCaptionID($value)
    -	{
    -		$this->setViewState('CaptionID', $value, '');
    -	}
    -
    -	protected function getCaptionControl()
    -	{
    -		if(($id=$this->getCaptionID())!=='')
    -		{
    -			if($control=$this->getParent()->findControl($id))
    -				return $control;
    -		}
    -		throw new TInvalidDataValueException(
    -			'ratinglist_invalid_caption_id',$id,$this->getID());
    -	}
    -
    -	/**
    -	 * @return string caption text. Default is "Rate It:".
    -	 */
    -	public function getCaption()
    -	{
    -		return $this->getCaptionControl()->getText();
    -	}
    -
    -	/**
    -	 * @return TRatingListStyle current rating style
    -	 */
    - 	public function setCaption($value)
    - 	{
    -		$this->getCaptionControl()->setText($value);
    - 	}
    -
    -	/**
    -	 * @param string set the rating style, default is "default"
    -	 */
    -	public function setRatingStyle($value)
    - 	{
    -	   $this->setViewState('RatingStyle', $value, 'default');
    - 	}
    -
    -	/**
    -	 * @return TRatingListStyle current rating style
    -	 */
    -	public function getRatingStyle()
    - 	{
    -	   return $this->getViewState('RatingStyle', 'default');
    - 	}
    - 
    - 	/**
    -	 * @return string rating style css class name.
    - 	 */
    -	protected function getRatingStyleCssClass()
    - 	{
    -		return 'TRatingList_'.$this->getRatingStyle();
    - 	}
    -
    -	/**
    -	 * Sets the interval such that those rating values within the interval
    -	 * will be considered as a half star rating.
    -	 * @param array rating display half value interval, default is array(0.3, 0.7);
    -	 */
    -	public function setHalfRatingInterval($value)
    - 	{
    -		$this->setViewState('HalfRating',
    -				TPropertyValue::ensureArray($value), array(0.3, 0.7));
    - 	}
    -
    -	/**
    -	 * @return array rating display half value interval, default is array(0.3, 0.7);
    -	 */
    -	public function getHalfRatingInterval()
    - 	{
    -		return $this->getViewState('HalfRating', array(0.3, 0.7));
    - 	}
    -
    -	/**
    -	 * @return array list of post back options.
    -	 */
    -	protected function getPostBackOptions()
    - 	{
    -		$options = parent::getPostBackOptions();
    -		$options['AutoPostBack'] = $this->getAutoPostBack();
    -		$options['ReadOnly'] = $this->getReadOnly();
    -		$options['Style'] = $this->getRatingStyleCssClass();
    -		$options['CaptionID'] = $this->getCaptionControlID();
    -		$options['SelectedIndex'] = $this->getSelectedIndex();
    -		$options['Rating'] = $this->getRating();
    -		$options['HalfRating'] = $this->getHalfRatingInterval();
    -		return $options;
    - 	}
    -
    - 	/**
    -	 * @return string find the client ID of the caption control.
    - 	 */
    -	protected function getCaptionControlID()
    - 	{
    -		if(($id=$this->getCaptionID())!=='')
    - 		{
    -			if($control=$this->getParent()->findControl($id))
    -			{
    -				if($control->getVisible(true))
    -					return $control->getClientID();
    -			}
    -			else
    -				return $id;
    - 		}
    -		return '';
    - 	}
    -
    -	/**
    -	 * Publish the the rating style css file and rating image files.
    -	 */
    -	public function onPreRender($param)
    - 	{
    -		parent::onPreRender($param);
    -		$this->publishStyle($this->getRatingStyle());
    -		$this->_ratingImages = $this->publishImages($this->getRatingStyle());
    - 		$this->registerClientScript();
    -	}
    -
    -	/**
    -	 * @param string rating style name
    -	 * @return string URL of the css style file
    -	 */
    -	protected function publishStyle($style)
    - 	{
    -		$cs = $this->getPage()->getClientScript();
    -		$url = $this->getAssetUrl($style.'.css');
    -		if(!$cs->isStyleSheetFileRegistered($url))
    -			$cs->registerStyleSheetFile($url, $url);
    -		return $url;
    - 	}
    -
    -	/**
    -	 * @param string rating style name
    -	 * @param string rating image file extension, default is '.gif'
    -	 * @return array URL of publish the rating images
    -	 */
    -	protected function publishImages($style, $fileExt='.gif')
    - 	{
    -		$types = array('blank', 'selected', 'half', 'combined');
    -		$files = array();
    -		foreach($types as $type)
    -			$files[$type] = $this->getAssetUrl("{$style}_{$type}{$fileExt}");
    -		return $files;
    - 	}
    -
    -	/**
    -	 * Registers the relevant JavaScript.
    -	 */
    -	protected function registerClientScript()
    -	{
    -		$cs=$this->getPage()->getClientScript();
    -		$cs->registerPradoScript('ratings');
    -	}
    -
    -	/**
    -	 * @param string asset file in the self::SCRIPT_PATH directory.
    -	 * @return string asset file url.
    -	 */
    -	protected function getAssetUrl($file='')
    - 	{
    -		$base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl();
    -		return $base.'/'.self::SCRIPT_PATH.'/'.$file;
    - 	}
    -
    -	/**
    -	 * Add rating style class name to the class attribute
    -	 * when {@link setReadOnly ReadOnly} property is true and when the
    -	 * {@link setCssClass CssClass} property is empty.
    -	 * @param THtmlWriter renderer
    -	 */
    -	public function render($writer)
    - 	{
    -		$writer->addAttribute('id',$this->getClientID());
    -		$this->getPage()->getClientScript()->registerPostBackControl(
    -			$this->getClientClassName(), $this->getPostBackOptions());
    -		parent::render($writer);
    - 	}
    -
    -	/**
    -	 * 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.TRatingList';
    -	}
    -}
    -
    +
    + * @link http://www.pradosoft.com/
    + * @copyright Copyright © 2005-2012 PradoSoft
    + * @license http://www.pradosoft.com/license/
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + */
    +
    +/**
    + * Includes TRadioButtonList class
    + */
    +Prado::using('System.Web.UI.WebControls.TRadioButtonList');
    +
    +/**
    + * TRatingList class.
    + *
    + * This class is EXPERIMENTAL.
    + *
    + * @author Wei Zhuo 
    + * @author Bradley Booms 
    + * @version $Id$
    + * @package System.Web.UI.WebControls
    + * @since 3.0
    + */
    +class TRatingList extends TRadioButtonList
    +{
    +	/**
    +	 * Script path relative to the TClientScriptManager::SCRIPT_PATH
    +	 */
    +	const SCRIPT_PATH='prado/ratings';
    +
    +	/**
    +	 * @var array list of published rating images.
    +	 */
    +	private $_ratingImages = array();
    +
    +	/**
    +	 * Sets the default repeat direction to horizontal.
    +	 */
    +	public function __construct()
    +	{
    +		parent::__construct();
    +		$this->setRepeatDirection(TRepeatDirection::Horizontal);
    +	}
    +
    +	/**
    +	 * @return boolean whether the items in the column can be edited. Defaults to false.
    +	 */
    +	public function getReadOnly()
    +	{
    +		return $this->getViewState('ReadOnly',false);
    +	}
    +
    +	/**
    +	 * @param boolean whether the items in the column can be edited
    +	 */
    +	public function setReadOnly($value)
    +	{
    +		$this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false);
    +	}
    +
    +	/**
    +	 * Wrapper for {@link setReadOnly ReadOnly} property.
    +	 * @return boolean whether the rating list can be edited. Defaults to true.
    +	 */
    +	public function getAllowInput()
    +	{
    +		return !$this->getReadOnly();
    +	}
    +
    +	/**
    +	 * Wrapper for {@link setReadOnly ReadOnly} property.
    +	 * @param boolean whether the rating list can be edited
    +	 */
    +	public function setAllowInput($value)
    +	{
    +		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
    +	}
    +
    +	/**
    +	 * Wrapper for {@link setReadOnly ReadOnly} property.
    +	 * @param boolean whether the rating list can be edited
    +	 */
    +	public function setEnabled($value)
    +	{
    +		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
    +	}
    +
    +	/**
    +	 * The repeat layout must be Table.
    +	 * @param string repeat layout type
    +	 * @throws TInvaliddataValueException when repeat layout is not Table.
    +	 */
    +	public function setRepeatLayout($value)
    +	{
    +		if($value!==TRepeatLayout::Table)
    +			throw new TInvalidDataValueException('ratinglist_table_layout_only');
    +		else
    +			parent::setRepeatLayout($value);
    +	}
    +
    +	/**
    +	 * @return float rating value.
    +	 */
    +	public function getRating()
    +	{
    +		$rating = $this->getViewState('Rating', null);
    +		if ($rating === null)
    +			return $this->getSelectedIndex()+1;
    +		else
    +			return $rating;
    +	}
    +
    +	/**
    +	 * @param float rating value, also sets the selected Index
    +	 */
    +	public function setRating($value)
    +	{
    +		$value = TPropertyValue::ensureFloat($value);
    +		$this->setViewState('Rating', $value, null);
    +		$index = $this->getRatingIndex($value);
    +		parent::setSelectedIndex($index);
    +	}
    +	
    +	public function setSelectedIndex($value)
    +	{
    +		$this->setRating($value+1);
    +		parent::setSelectedIndex($value);
    +	}
    +
    +	/**
    +	 * @param float rating value
    +	 * @return int rating as integer
    +	 */
    +	protected function getRatingIndex($rating)
    +	{
    +		$interval = $this->getHalfRatingInterval();
    +		$base = intval($rating)-1;
    +		$remainder = $rating-$base-1;
    +		return $remainder > $interval[1] ? $base+1 : $base;
    +	}
    +
    +	/**
    +	 * @param int change the rating selection index
    +	 */
    +	public function onSelectedIndexChanged($param)
    +	{
    +		$value = $this->getRating();
    +		$value = TPropertyValue::ensureInteger($value);
    +		$this->setRating($value);
    +		parent::onSelectedIndexChanged($param);
    +	}
    +
    +	/**
    +	 * @return string control or html element ID for displaying a caption.
    +	 */
    +	public function getCaptionID()
    +	{
    +		return $this->getViewState('CaptionID', '');
    +	}
    +
    +	/**
    +	 * @param string control or html element ID for displaying a caption.
    +	 */
    +	public function setCaptionID($value)
    +	{
    +		$this->setViewState('CaptionID', $value, '');
    +	}
    +
    +	protected function getCaptionControl()
    +	{
    +		if(($id=$this->getCaptionID())!=='')
    +		{
    +			if($control=$this->getParent()->findControl($id))
    +				return $control;
    +		}
    +		throw new TInvalidDataValueException(
    +			'ratinglist_invalid_caption_id',$id,$this->getID());
    +	}
    +
    +	/**
    +	 * @return string caption text. Default is "Rate It:".
    +	 */
    +	public function getCaption()
    +	{
    +		return $this->getCaptionControl()->getText();
    +	}
    +
    +	/**
    +	 * @return TRatingListStyle current rating style
    +	 */
    + 	public function setCaption($value)
    + 	{
    +		$this->getCaptionControl()->setText($value);
    + 	}
    +
    +	/**
    +	 * @param string set the rating style, default is "default"
    +	 */
    +	public function setRatingStyle($value)
    + 	{
    +	   $this->setViewState('RatingStyle', $value, 'default');
    + 	}
    +
    +	/**
    +	 * @return TRatingListStyle current rating style
    +	 */
    +	public function getRatingStyle()
    + 	{
    +	   return $this->getViewState('RatingStyle', 'default');
    + 	}
    + 
    + 	/**
    +	 * @return string rating style css class name.
    + 	 */
    +	protected function getRatingStyleCssClass()
    + 	{
    +		return 'TRatingList_'.$this->getRatingStyle();
    + 	}
    +
    +	/**
    +	 * Sets the interval such that those rating values within the interval
    +	 * will be considered as a half star rating.
    +	 * @param array rating display half value interval, default is array(0.3, 0.7);
    +	 */
    +	public function setHalfRatingInterval($value)
    + 	{
    +		$this->setViewState('HalfRating',
    +				TPropertyValue::ensureArray($value), array(0.3, 0.7));
    + 	}
    +
    +	/**
    +	 * @return array rating display half value interval, default is array(0.3, 0.7);
    +	 */
    +	public function getHalfRatingInterval()
    + 	{
    +		return $this->getViewState('HalfRating', array(0.3, 0.7));
    + 	}
    +
    +	/**
    +	 * @return array list of post back options.
    +	 */
    +	protected function getPostBackOptions()
    + 	{
    +		$options = parent::getPostBackOptions();
    +		$options['AutoPostBack'] = $this->getAutoPostBack();
    +		$options['ReadOnly'] = $this->getReadOnly();
    +		$options['Style'] = $this->getRatingStyleCssClass();
    +		$options['CaptionID'] = $this->getCaptionControlID();
    +		$options['SelectedIndex'] = $this->getSelectedIndex();
    +		$options['Rating'] = $this->getRating();
    +		$options['HalfRating'] = $this->getHalfRatingInterval();
    +		return $options;
    + 	}
    +
    + 	/**
    +	 * @return string find the client ID of the caption control.
    + 	 */
    +	protected function getCaptionControlID()
    + 	{
    +		if(($id=$this->getCaptionID())!=='')
    + 		{
    +			if($control=$this->getParent()->findControl($id))
    +			{
    +				if($control->getVisible(true))
    +					return $control->getClientID();
    +			}
    +			else
    +				return $id;
    + 		}
    +		return '';
    + 	}
    +
    +	/**
    +	 * Publish the the rating style css file and rating image files.
    +	 */
    +	public function onPreRender($param)
    + 	{
    +		parent::onPreRender($param);
    +		$this->publishStyle($this->getRatingStyle());
    +		$this->_ratingImages = $this->publishImages($this->getRatingStyle());
    + 		$this->registerClientScript();
    +	}
    +
    +	/**
    +	 * @param string rating style name
    +	 * @return string URL of the css style file
    +	 */
    +	protected function publishStyle($style)
    + 	{
    +		$cs = $this->getPage()->getClientScript();
    +		$url = $this->getAssetUrl($style.'.css');
    +		if(!$cs->isStyleSheetFileRegistered($url))
    +			$cs->registerStyleSheetFile($url, $url);
    +		return $url;
    + 	}
    +
    +	/**
    +	 * @param string rating style name
    +	 * @param string rating image file extension, default is '.gif'
    +	 * @return array URL of publish the rating images
    +	 */
    +	protected function publishImages($style, $fileExt='.gif')
    + 	{
    +		$types = array('blank', 'selected', 'half', 'combined');
    +		$files = array();
    +		foreach($types as $type)
    +			$files[$type] = $this->getAssetUrl("{$style}_{$type}{$fileExt}");
    +		return $files;
    + 	}
    +
    +	/**
    +	 * Registers the relevant JavaScript.
    +	 */
    +	protected function registerClientScript()
    +	{
    +		$cs=$this->getPage()->getClientScript();
    +		$cs->registerPradoScript('ratings');
    +	}
    +
    +	/**
    +	 * @param string asset file in the self::SCRIPT_PATH directory.
    +	 * @return string asset file url.
    +	 */
    +	protected function getAssetUrl($file='')
    + 	{
    +		$base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl();
    +		return $base.'/'.self::SCRIPT_PATH.'/'.$file;
    + 	}
    +
    +	/**
    +	 * Add rating style class name to the class attribute
    +	 * when {@link setReadOnly ReadOnly} property is true and when the
    +	 * {@link setCssClass CssClass} property is empty.
    +	 * @param THtmlWriter renderer
    +	 */
    +	public function render($writer)
    + 	{
    +		$writer->addAttribute('id',$this->getClientID());
    +		$this->getPage()->getClientScript()->registerPostBackControl(
    +			$this->getClientClassName(), $this->getPostBackOptions());
    +		parent::render($writer);
    + 	}
    +
    +	/**
    +	 * 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.TRatingList';
    +	}
    +}
    +
    diff --git a/framework/Web/UI/WebControls/TReCaptcha.php b/framework/Web/UI/WebControls/TReCaptcha.php
    index b9ba0070..cbdc2e36 100644
    --- a/framework/Web/UI/WebControls/TReCaptcha.php
    +++ b/framework/Web/UI/WebControls/TReCaptcha.php
    @@ -1,233 +1,233 @@
    -
    - * @link http://www.devworx.hu/
    - * @copyright Copyright © 2011 DevWorx
    - * @license http://www.pradosoft.com/license/
    - * @package System.Web.UI.WebControls
    - */
    -
    -	Prado::using('System.3rdParty.ReCaptcha.recaptchalib');
    -
    -/**
    - * TReCaptcha class.
    - *
    - * TReCaptcha displays a reCAPTCHA (a token displayed as an image) that can be used
    - * to determine if the input is entered by a real user instead of some program. It can
    - * also prevent multiple submits of the same form either by accident, or on purpose (ie. spamming).
    - *
    - * The reCAPTCHA to solve (a string consisting of two separate words) displayed is automatically
    - * generated by the reCAPTCHA system at recaptcha.net. However, in order to use the services
    - * of the site you will need to register and get a public and a private API key pair, and 
    - * supply those to the reCAPTCHA control through setting the {@link setPrivateKey PrivateKey} 
    - * and {@link setPublicKey PublicKey} properties. 
    - *
    - * Currently the reCAPTCHA API supports only one reCAPTCHA field per page, so you MUST make sure that all 
    - * your input is protected and validated by a single reCAPTCHA control. Placing more than one reCAPTCHA
    - * control on the page will lead to unpredictable results, and the user will most likely unable to solve 
    - * any of them successfully.
    - *
    - * Upon postback, user input can be validated by calling {@link validate()}.
    - * The {@link TReCaptchaValidator} control can also be used to do validation, which provides
    - * server-side validation. Calling (@link validate()) will invalidate the token supplied, so all consecutive
    - * calls to the method - without solving a new captcha - will return false. Therefore if implementing a multi-stage
    - * input process, you must make sure that you call validate() only once, either at the end of the input process, or 
    - * you store the result till the end of the processing.
    - *
    - * The following template shows a typical use of TReCaptcha control:
    - * 
    - * 
    - * 
    - * 
    - *
    - * @author Bérczi Gábor 
    - * @package System.Web.UI.WebControls
    - * @since 3.2
    - */
    -class TReCaptcha extends TWebControl implements IValidatable
    -{
    -	private $_isValid=true;
    -
    -	const ChallengeFieldName = 'recaptcha_challenge_field';
    -	const ResponseFieldName = 'recaptcha_response_field';
    -
    -	public function getTagName()
    -	{
    -		return 'span';
    -	}
    -	
    -	/**
    -	 * Returns true if this control validated successfully. 
    -	 * Defaults to true.
    -	 * @return bool wether this control validated successfully.
    -	 */
    -	public function getIsValid()
    -	{
    -		return $this->_isValid;
    -	}
    -	/**
    -	 * @param bool wether this control is valid.
    -	 */
    -	public function setIsValid($value)
    -	{
    -		$this->_isValid=TPropertyValue::ensureBoolean($value);
    -	}
    -	
    -	public function getValidationPropertyValue()
    -	{
    -		return $this->Request[$this->getChallengeFieldName()];
    -	}
    -
    -	public function getPublicKey()
    -	{
    -		return $this->getViewState('PublicKey');
    -	}
    -
    -	public function setPublicKey($value)
    -	{
    -		return $this->setViewState('PublicKey', TPropertyValue::ensureString($value));
    -	}
    -
    -	public function getPrivateKey()
    -	{
    -		return $this->getViewState('PrivateKey');
    -	}
    -
    -	public function setPrivateKey($value)
    -	{
    -		return $this->setViewState('PrivateKey', TPropertyValue::ensureString($value));
    -	}
    -	
    -	public function getThemeName()
    -	{
    -		return $this->getViewState('ThemeName');
    -	}
    -
    -	public function setThemeName($value)
    -	{
    -		return $this->setViewState('ThemeName', TPropertyValue::ensureString($value));
    -	}
    -
    -	public function getCustomTranslations()
    -	{
    -		return TPropertyValue::ensureArray($this->getViewState('CustomTranslations'));
    -	}
    -
    -	public function setCustomTranslations($value)
    -	{
    -		return $this->setViewState('CustomTranslations', TPropertyValue::ensureArray($value));
    -	}
    -
    -	public function getLanguage()
    -	{
    -		return $this->getViewState('Language');
    -	}
    -
    -	public function setLanguage($value)
    -	{
    -		return $this->setViewState('Language', TPropertyValue::ensureString($value));
    -	}
    -
    -	protected function getChallengeFieldName()
    -	{
    -		return /*$this->ClientID.'_'.*/self::ChallengeFieldName;
    -	}
    -	
    -	public function getResponseFieldName()
    -	{
    -		return /*$this->ClientID.'_'.*/self::ResponseFieldName;
    -	}
    -	
    -	public function getClientSideOptions()
    -	{
    -		$options = array();
    -		if ($theme = $this->getThemeName())
    -			$options['theme'] = $theme;
    -		if ($lang = $this->getLanguage())
    -			$options['lang'] = $lang;
    -		if ($trans = $this->getCustomTranslations())
    -			$options['custom_translations'] = $trans;
    -		return $options;
    -	}
    -
    -	public function validate()
    -	{
    -		if (!
    -		      (
    -			($challenge = @$_POST[$this->getChallengeFieldName()])
    -			and
    -			($response = @$_POST[$this->getResponseFieldName()])
    -		      )
    -                   )
    -		   return false;
    -
    -		$resp = recaptcha_check_answer(
    -			$this->getPrivateKey(),
    -			$_SERVER["REMOTE_ADDR"],
    -			$challenge,
    -			$response
    -		); 
    -		return ($resp->is_valid==1);
    -	}
    -
    -	/**
    -	 * Checks for API keys
    -	 * @param mixed event parameter
    -	 */
    -	public function onPreRender($param)
    -	{
    -		parent::onPreRender($param);
    -
    -		if("" == $this->getPublicKey())
    -			throw new TConfigurationException('recaptcha_publickey_unknown');
    -		if("" == $this->getPrivateKey())
    -			throw new TConfigurationException('recaptcha_privatekey_unknown');
    -
    -		// need to register captcha fields so they will be sent back also in callbacks 
    -		$page = $this->getPage();
    -		$page->registerRequiresPostData($this->getChallengeFieldName());
    -		$page->registerRequiresPostData($this->getResponseFieldName());
    -	}
    -
    -	protected function addAttributesToRender($writer)
    -	{
    -		parent::addAttributesToRender($writer);
    -		$writer->addAttribute('id',$this->getClientID());
    -	}
    -
    -	public function regenerateToken()
    -	{
    -		// if we're in a callback, then schedule re-rendering of the control 
    -		// if not, don't do anything, because a new challenge will be rendered anyway
    -		if ($this->Page->IsCallback)
    -			$this->Page->ClientScript->registerEndScript($this->getClientID().'::refresh','Recaptcha.reload();');
    -	}
    -
    -	public function renderContents($writer)
    -	{
    -		$writer->write(TJavaScript::renderScriptBlock(
    -			'var RecaptchaOptions = '.TJavaScript::jsonEncode($this->getClientSideOptions()).';'
    -		));
    -
    -		$html = recaptcha_get_html($this->getPublicKey());
    -		/*
    -		reCAPTCHA currently does not support multiple validations per page
    -		$html = str_replace(
    -			array(self::ChallengeFieldName,self::ResponseFieldName),
    -			array($this->getChallengeFieldName(),$this->getResponseFieldName()),
    -			$html
    -		);
    -		*/
    -		$writer->write($html);
    -	}
    -
    -}
    -
    +
    + * @link http://www.devworx.hu/
    + * @copyright Copyright © 2011 DevWorx
    + * @license http://www.pradosoft.com/license/
    + * @package System.Web.UI.WebControls
    + */
    +
    +	Prado::using('System.3rdParty.ReCaptcha.recaptchalib');
    +
    +/**
    + * TReCaptcha class.
    + *
    + * TReCaptcha displays a reCAPTCHA (a token displayed as an image) that can be used
    + * to determine if the input is entered by a real user instead of some program. It can
    + * also prevent multiple submits of the same form either by accident, or on purpose (ie. spamming).
    + *
    + * The reCAPTCHA to solve (a string consisting of two separate words) displayed is automatically
    + * generated by the reCAPTCHA system at recaptcha.net. However, in order to use the services
    + * of the site you will need to register and get a public and a private API key pair, and 
    + * supply those to the reCAPTCHA control through setting the {@link setPrivateKey PrivateKey} 
    + * and {@link setPublicKey PublicKey} properties. 
    + *
    + * Currently the reCAPTCHA API supports only one reCAPTCHA field per page, so you MUST make sure that all 
    + * your input is protected and validated by a single reCAPTCHA control. Placing more than one reCAPTCHA
    + * control on the page will lead to unpredictable results, and the user will most likely unable to solve 
    + * any of them successfully.
    + *
    + * Upon postback, user input can be validated by calling {@link validate()}.
    + * The {@link TReCaptchaValidator} control can also be used to do validation, which provides
    + * server-side validation. Calling (@link validate()) will invalidate the token supplied, so all consecutive
    + * calls to the method - without solving a new captcha - will return false. Therefore if implementing a multi-stage
    + * input process, you must make sure that you call validate() only once, either at the end of the input process, or 
    + * you store the result till the end of the processing.
    + *
    + * The following template shows a typical use of TReCaptcha control:
    + * 
    + * 
    + * 
    + * 
    + *
    + * @author Bérczi Gábor 
    + * @package System.Web.UI.WebControls
    + * @since 3.2
    + */
    +class TReCaptcha extends TWebControl implements IValidatable
    +{
    +	private $_isValid=true;
    +
    +	const ChallengeFieldName = 'recaptcha_challenge_field';
    +	const ResponseFieldName = 'recaptcha_response_field';
    +
    +	public function getTagName()
    +	{
    +		return 'span';
    +	}
    +	
    +	/**
    +	 * Returns true if this control validated successfully. 
    +	 * Defaults to true.
    +	 * @return bool wether this control validated successfully.
    +	 */
    +	public function getIsValid()
    +	{
    +		return $this->_isValid;
    +	}
    +	/**
    +	 * @param bool wether this control is valid.
    +	 */
    +	public function setIsValid($value)
    +	{
    +		$this->_isValid=TPropertyValue::ensureBoolean($value);
    +	}
    +	
    +	public function getValidationPropertyValue()
    +	{
    +		return $this->Request[$this->getChallengeFieldName()];
    +	}
    +
    +	public function getPublicKey()
    +	{
    +		return $this->getViewState('PublicKey');
    +	}
    +
    +	public function setPublicKey($value)
    +	{
    +		return $this->setViewState('PublicKey', TPropertyValue::ensureString($value));
    +	}
    +
    +	public function getPrivateKey()
    +	{
    +		return $this->getViewState('PrivateKey');
    +	}
    +
    +	public function setPrivateKey($value)
    +	{
    +		return $this->setViewState('PrivateKey', TPropertyValue::ensureString($value));
    +	}
    +	
    +	public function getThemeName()
    +	{
    +		return $this->getViewState('ThemeName');
    +	}
    +
    +	public function setThemeName($value)
    +	{
    +		return $this->setViewState('ThemeName', TPropertyValue::ensureString($value));
    +	}
    +
    +	public function getCustomTranslations()
    +	{
    +		return TPropertyValue::ensureArray($this->getViewState('CustomTranslations'));
    +	}
    +
    +	public function setCustomTranslations($value)
    +	{
    +		return $this->setViewState('CustomTranslations', TPropertyValue::ensureArray($value));
    +	}
    +
    +	public function getLanguage()
    +	{
    +		return $this->getViewState('Language');
    +	}
    +
    +	public function setLanguage($value)
    +	{
    +		return $this->setViewState('Language', TPropertyValue::ensureString($value));
    +	}
    +
    +	protected function getChallengeFieldName()
    +	{
    +		return /*$this->ClientID.'_'.*/self::ChallengeFieldName;
    +	}
    +	
    +	public function getResponseFieldName()
    +	{
    +		return /*$this->ClientID.'_'.*/self::ResponseFieldName;
    +	}
    +	
    +	public function getClientSideOptions()
    +	{
    +		$options = array();
    +		if ($theme = $this->getThemeName())
    +			$options['theme'] = $theme;
    +		if ($lang = $this->getLanguage())
    +			$options['lang'] = $lang;
    +		if ($trans = $this->getCustomTranslations())
    +			$options['custom_translations'] = $trans;
    +		return $options;
    +	}
    +
    +	public function validate()
    +	{
    +		if (!
    +		      (
    +			($challenge = @$_POST[$this->getChallengeFieldName()])
    +			and
    +			($response = @$_POST[$this->getResponseFieldName()])
    +		      )
    +                   )
    +		   return false;
    +
    +		$resp = recaptcha_check_answer(
    +			$this->getPrivateKey(),
    +			$_SERVER["REMOTE_ADDR"],
    +			$challenge,
    +			$response
    +		); 
    +		return ($resp->is_valid==1);
    +	}
    +
    +	/**
    +	 * Checks for API keys
    +	 * @param mixed event parameter
    +	 */
    +	public function onPreRender($param)
    +	{
    +		parent::onPreRender($param);
    +
    +		if("" == $this->getPublicKey())
    +			throw new TConfigurationException('recaptcha_publickey_unknown');
    +		if("" == $this->getPrivateKey())
    +			throw new TConfigurationException('recaptcha_privatekey_unknown');
    +
    +		// need to register captcha fields so they will be sent back also in callbacks 
    +		$page = $this->getPage();
    +		$page->registerRequiresPostData($this->getChallengeFieldName());
    +		$page->registerRequiresPostData($this->getResponseFieldName());
    +	}
    +
    +	protected function addAttributesToRender($writer)
    +	{
    +		parent::addAttributesToRender($writer);
    +		$writer->addAttribute('id',$this->getClientID());
    +	}
    +
    +	public function regenerateToken()
    +	{
    +		// if we're in a callback, then schedule re-rendering of the control 
    +		// if not, don't do anything, because a new challenge will be rendered anyway
    +		if ($this->Page->IsCallback)
    +			$this->Page->ClientScript->registerEndScript($this->getClientID().'::refresh','Recaptcha.reload();');
    +	}
    +
    +	public function renderContents($writer)
    +	{
    +		$writer->write(TJavaScript::renderScriptBlock(
    +			'var RecaptchaOptions = '.TJavaScript::jsonEncode($this->getClientSideOptions()).';'
    +		));
    +
    +		$html = recaptcha_get_html($this->getPublicKey());
    +		/*
    +		reCAPTCHA currently does not support multiple validations per page
    +		$html = str_replace(
    +			array(self::ChallengeFieldName,self::ResponseFieldName),
    +			array($this->getChallengeFieldName(),$this->getResponseFieldName()),
    +			$html
    +		);
    +		*/
    +		$writer->write($html);
    +	}
    +
    +}
    +
     ?>
    \ No newline at end of file
    diff --git a/framework/Web/UI/WebControls/TReCaptchaValidator.php b/framework/Web/UI/WebControls/TReCaptchaValidator.php
    index 7199e401..41abbc5a 100644
    --- a/framework/Web/UI/WebControls/TReCaptchaValidator.php
    +++ b/framework/Web/UI/WebControls/TReCaptchaValidator.php
    @@ -1,123 +1,123 @@
    -
    - * @link http://www.devworx.hu/
    - * @copyright Copyright © 2011 DevWorx
    - * @license http://www.pradosoft.com/license/
    - * @package System.Web.UI.WebControls
    - */
    -
    -Prado::using('System.Web.UI.WebControls.TBaseValidator');
    -Prado::using('System.Web.UI.WebControls.TReCaptcha');
    -
    -/**
    - * TReCaptchaValidator class
    - *
    - * TReCaptchaValidator validates user input against a reCAPTCHA represented by
    - * a {@link TReCaptcha} control. The input control fails validation if its value
    - * is not the same as the token displayed in reCAPTCHA. Note, if the user does
    - * not enter any thing, it is still considered as failing the validation.
    - *
    - * To use TReCaptchaValidator, specify the {@link setControlToValidate ControlToValidate}
    - * to be the ID path of the {@link TReCaptcha} control.
    - *
    - * @author Bérczi Gábor 
    - * @package System.Web.UI.WebControls
    - * @since 3.2
    - */
    -class TReCaptchaValidator extends TBaseValidator
    -{
    -	protected $_isvalid = null;
    -
    -	/**
    -	 * Gets the name of the javascript class responsible for performing validation for this control.
    -	 * This method overrides the parent implementation.
    -	 * @return string the javascript class name
    -	 */
    -	protected function getClientClassName()
    -	{
    -		return 'Prado.WebUI.TReCaptchaValidator';
    -	}
    -
    -	public function getEnableClientScript()
    -	{
    -		return true;
    -	}
    -
    -	protected function getCaptchaControl()
    -	{
    -		$control = $this->getValidationTarget();
    -		if (!$control)
    -			throw new Exception('No target control specified for TReCaptchaValidator');
    -		if (!($control instanceof TReCaptcha))
    -			throw new Exception('TReCaptchaValidator only works with TReCaptcha controls');
    -		return $control;
    -	}
    -
    -	public function getClientScriptOptions()
    -	{
    -		$options = parent::getClientScriptOptions();
    -		$options['ResponseFieldName'] = $this->getCaptchaControl()->getResponseFieldName();
    -		return $options;
    -	}
    -
    -	/**
    -	 * This method overrides the parent's implementation.
    -	 * The validation succeeds if the input control has the same value
    -	 * as the one displayed in the corresponding RECAPTCHA control.
    -	 *
    -	 * @return boolean whether the validation succeeds
    -	 */
    -	protected function evaluateIsValid()
    -	{
    -		// check validity only once (if trying to evaulate multiple times, all redundant checks would fail)
    -		if (is_null($this->_isvalid))
    -		{
    -			$control = $this->getCaptchaControl();
    -			$this->_isvalid = $control->validate();
    -		}
    -		return ($this->_isvalid==true);
    -	}
    -
    -	public function onPreRender($param)
    -	{
    -		parent::onPreRender($param);
    -
    -		$cs = $this->Page->getClientScript();
    -
    -		// communicate validation status to the client side
    -		$value = $this->_isvalid===false ? '0' : '1';
    -		$cs->registerHiddenField($this->getClientID().'_1',$value);
    -
    -		// check if we need to request a new captcha too
    -		if ($this->Page->IsCallback)
    -		{
    -		  // force update of validator display
    -		  if ($control = $this->getValidationTarget())
    -		  {
    -		    $cs->registerEndScript(
    -				$this->getClientID().'::validate',
    -				'$('.TJavaScript::quoteString($this->getClientID().'_1').').value = '.TJavaScript::quoteString($value).';'.
    -				'Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).');'
    -		    );
    -
    -		    if ($control->getVisible(true))
    -		      if ($this->_isvalid)
    -			{
    -				// if the challenge has been solved + we're in a callback and we still reach prerender phase,
    -				// that means that some other validator failed and the user will be sent back to the page/form with 
    -				// the captcha control. in this case we need to force re-rendering of the control, because once 
    -				// solved, the old challenge won't validate anymore anyway
    -
    -				$control->regenerateToken();
    -			}
    -		  }
    -		}
    -	}
    -
    -}
    -
    +
    + * @link http://www.devworx.hu/
    + * @copyright Copyright © 2011 DevWorx
    + * @license http://www.pradosoft.com/license/
    + * @package System.Web.UI.WebControls
    + */
    +
    +Prado::using('System.Web.UI.WebControls.TBaseValidator');
    +Prado::using('System.Web.UI.WebControls.TReCaptcha');
    +
    +/**
    + * TReCaptchaValidator class
    + *
    + * TReCaptchaValidator validates user input against a reCAPTCHA represented by
    + * a {@link TReCaptcha} control. The input control fails validation if its value
    + * is not the same as the token displayed in reCAPTCHA. Note, if the user does
    + * not enter any thing, it is still considered as failing the validation.
    + *
    + * To use TReCaptchaValidator, specify the {@link setControlToValidate ControlToValidate}
    + * to be the ID path of the {@link TReCaptcha} control.
    + *
    + * @author Bérczi Gábor 
    + * @package System.Web.UI.WebControls
    + * @since 3.2
    + */
    +class TReCaptchaValidator extends TBaseValidator
    +{
    +	protected $_isvalid = null;
    +
    +	/**
    +	 * Gets the name of the javascript class responsible for performing validation for this control.
    +	 * This method overrides the parent implementation.
    +	 * @return string the javascript class name
    +	 */
    +	protected function getClientClassName()
    +	{
    +		return 'Prado.WebUI.TReCaptchaValidator';
    +	}
    +
    +	public function getEnableClientScript()
    +	{
    +		return true;
    +	}
    +
    +	protected function getCaptchaControl()
    +	{
    +		$control = $this->getValidationTarget();
    +		if (!$control)
    +			throw new Exception('No target control specified for TReCaptchaValidator');
    +		if (!($control instanceof TReCaptcha))
    +			throw new Exception('TReCaptchaValidator only works with TReCaptcha controls');
    +		return $control;
    +	}
    +
    +	public function getClientScriptOptions()
    +	{
    +		$options = parent::getClientScriptOptions();
    +		$options['ResponseFieldName'] = $this->getCaptchaControl()->getResponseFieldName();
    +		return $options;
    +	}
    +
    +	/**
    +	 * This method overrides the parent's implementation.
    +	 * The validation succeeds if the input control has the same value
    +	 * as the one displayed in the corresponding RECAPTCHA control.
    +	 *
    +	 * @return boolean whether the validation succeeds
    +	 */
    +	protected function evaluateIsValid()
    +	{
    +		// check validity only once (if trying to evaulate multiple times, all redundant checks would fail)
    +		if (is_null($this->_isvalid))
    +		{
    +			$control = $this->getCaptchaControl();
    +			$this->_isvalid = $control->validate();
    +		}
    +		return ($this->_isvalid==true);
    +	}
    +
    +	public function onPreRender($param)
    +	{
    +		parent::onPreRender($param);
    +
    +		$cs = $this->Page->getClientScript();
    +
    +		// communicate validation status to the client side
    +		$value = $this->_isvalid===false ? '0' : '1';
    +		$cs->registerHiddenField($this->getClientID().'_1',$value);
    +
    +		// check if we need to request a new captcha too
    +		if ($this->Page->IsCallback)
    +		{
    +		  // force update of validator display
    +		  if ($control = $this->getValidationTarget())
    +		  {
    +		    $cs->registerEndScript(
    +				$this->getClientID().'::validate',
    +				'$('.TJavaScript::quoteString($this->getClientID().'_1').').value = '.TJavaScript::quoteString($value).';'.
    +				'Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).');'
    +		    );
    +
    +		    if ($control->getVisible(true))
    +		      if ($this->_isvalid)
    +			{
    +				// if the challenge has been solved + we're in a callback and we still reach prerender phase,
    +				// that means that some other validator failed and the user will be sent back to the page/form with 
    +				// the captcha control. in this case we need to force re-rendering of the control, because once 
    +				// solved, the old challenge won't validate anymore anyway
    +
    +				$control->regenerateToken();
    +			}
    +		  }
    +		}
    +	}
    +
    +}
    +
     ?>
    \ No newline at end of file
    diff --git a/framework/Web/UI/WebControls/TRegularExpressionValidator.php b/framework/Web/UI/WebControls/TRegularExpressionValidator.php
    index a2a5bc64..0e85907a 100644
    --- a/framework/Web/UI/WebControls/TRegularExpressionValidator.php
    +++ b/framework/Web/UI/WebControls/TRegularExpressionValidator.php
    @@ -1,144 +1,144 @@
    -
    - * @link http://www.pradosoft.com/
    - * @copyright Copyright © 2005-2012 PradoSoft
    - * @license http://www.pradosoft.com/license/
    - * @version $Id$
    - * @package System.Web.UI.WebControls
    - */
    -
    -/**
    - * Using TBaseValidator class
    - */
    -Prado::using('System.Web.UI.WebControls.TBaseValidator');
    -
    -/**
    - * TRegularExpressionValidator class
    - *
    - * TRegularExpressionValidator validates whether the value of an associated
    - * input component matches the pattern specified by a regular expression.
    - *
    - * You can specify the regular expression by setting the {@link setRegularExpression RegularExpression}
    - * property. Some commonly used regular expressions include:
    - * 
    - * French Phone Number: (0( \d|\d ))?\d\d \d\d(\d \d| \d\d )\d\d
    - * French Postal Code: \d{5}
    - * German Phone Number: ((\(0\d\d\) |(\(0\d{3}\) )?\d )?\d\d \d\d \d\d|\(0\d{4}\) \d \d\d-\d\d?)
    - * German Postal Code: (D-)?\d{5}
    - * Email Address: \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
    - * Japanese Phone Number: (0\d{1,4}-|\(0\d{1,4}\) ?)?\d{1,4}-\d{4}
    - * Japanese Postal Code: \d{3}(-(\d{4}|\d{2}))?
    - * P.R.C. Phone Number: (\(\d{3}\)|\d{3}-)?\d{8}
    - * P.R.C. Postal Code: \d{6}
    - * P.R.C. Social Security Number: \d{18}|\d{15}
    - * U.S. Phone Number: ((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}
    - * U.S. ZIP Code: \d{5}(-\d{4})?
    - * U.S. Social Security Number: \d{3}-\d{2}-\d{4}
    - * 
    - * - * Note, the validation succeeds if the associated input control contains empty input. - * Use a {@link TRequiredFieldValidator} to ensure the input is not empty. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRegularExpressionValidator extends TBaseValidator -{ - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TRegularExpressionValidator'; - } - - /** - * @return string the regular expression that determines the pattern used to validate a field. - */ - public function getRegularExpression() - { - return $this->getViewState('RegularExpression',''); - } - - /** - * @param string the regular expression that determines the pattern used to validate a field. - */ - public function setRegularExpression($value) - { - $this->setViewState('RegularExpression',$value,''); - } - - /** - * This method overrides the parent's implementation. - * The validation succeeds if the input data matches the regular expression. - * The validation always succeeds if ControlToValidate is not specified - * or the regular expression is empty, or the input data is empty. - * @return boolean whether the validation succeeds - */ - public function evaluateIsValid() - { - if(($value=$this->getValidationValue($this->getValidationTarget()))==='') - return true; - if(($expression=addcslashes($this->getRegularExpression(),"/"))!=='') - { - $mods = $this->getPatternModifiers(); - return preg_match("/^$expression\$/{$mods}",$value); - } - else - return true; - } - - /** - * @param string pattern modifiers for server side validation, - * see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php - */ - public function setPatternModifiers($value) - { - $this->setViewState('PatternModifiers', $value); - } - - /** - * @return string pattern modifiers, no modifiers by default. - */ - public function getPatternModifiers() - { - return $this->getViewState('PatternModifiers', ''); - } - - /** - * @param string pattern modifiers for clientside. - * (Only 'g','i' and 'm' are available.) - */ - public function setClientSidePatternModifiers($value) - { - $this->setViewState('ClientSidePatternModifiers', $value); - } - - /** - * @return string clientside pattern modifiers, no modifiers by default. - */ - public function getClientSidePatternModifiers() - { - return $this->getViewState('ClientSidePatternModifiers', ''); - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options = parent::getClientScriptOptions(); - $options['ValidationExpression']=$this->getRegularExpression(); - $options['PatternModifiers']=$this->getClientSidePatternModifiers(); - return $options; - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TRegularExpressionValidator class + * + * TRegularExpressionValidator validates whether the value of an associated + * input component matches the pattern specified by a regular expression. + * + * You can specify the regular expression by setting the {@link setRegularExpression RegularExpression} + * property. Some commonly used regular expressions include: + *
    + * French Phone Number: (0( \d|\d ))?\d\d \d\d(\d \d| \d\d )\d\d
    + * French Postal Code: \d{5}
    + * German Phone Number: ((\(0\d\d\) |(\(0\d{3}\) )?\d )?\d\d \d\d \d\d|\(0\d{4}\) \d \d\d-\d\d?)
    + * German Postal Code: (D-)?\d{5}
    + * Email Address: \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
    + * Japanese Phone Number: (0\d{1,4}-|\(0\d{1,4}\) ?)?\d{1,4}-\d{4}
    + * Japanese Postal Code: \d{3}(-(\d{4}|\d{2}))?
    + * P.R.C. Phone Number: (\(\d{3}\)|\d{3}-)?\d{8}
    + * P.R.C. Postal Code: \d{6}
    + * P.R.C. Social Security Number: \d{18}|\d{15}
    + * U.S. Phone Number: ((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}
    + * U.S. ZIP Code: \d{5}(-\d{4})?
    + * U.S. Social Security Number: \d{3}-\d{2}-\d{4}
    + * 
    + * + * Note, the validation succeeds if the associated input control contains empty input. + * Use a {@link TRequiredFieldValidator} to ensure the input is not empty. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRegularExpressionValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TRegularExpressionValidator'; + } + + /** + * @return string the regular expression that determines the pattern used to validate a field. + */ + public function getRegularExpression() + { + return $this->getViewState('RegularExpression',''); + } + + /** + * @param string the regular expression that determines the pattern used to validate a field. + */ + public function setRegularExpression($value) + { + $this->setViewState('RegularExpression',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input data matches the regular expression. + * The validation always succeeds if ControlToValidate is not specified + * or the regular expression is empty, or the input data is empty. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + if(($value=$this->getValidationValue($this->getValidationTarget()))==='') + return true; + if(($expression=addcslashes($this->getRegularExpression(),"/"))!=='') + { + $mods = $this->getPatternModifiers(); + return preg_match("/^$expression\$/{$mods}",$value); + } + else + return true; + } + + /** + * @param string pattern modifiers for server side validation, + * see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php + */ + public function setPatternModifiers($value) + { + $this->setViewState('PatternModifiers', $value); + } + + /** + * @return string pattern modifiers, no modifiers by default. + */ + public function getPatternModifiers() + { + return $this->getViewState('PatternModifiers', ''); + } + + /** + * @param string pattern modifiers for clientside. + * (Only 'g','i' and 'm' are available.) + */ + public function setClientSidePatternModifiers($value) + { + $this->setViewState('ClientSidePatternModifiers', $value); + } + + /** + * @return string clientside pattern modifiers, no modifiers by default. + */ + public function getClientSidePatternModifiers() + { + return $this->getViewState('ClientSidePatternModifiers', ''); + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['ValidationExpression']=$this->getRegularExpression(); + $options['PatternModifiers']=$this->getClientSidePatternModifiers(); + return $options; + } +} + diff --git a/framework/Web/UI/WebControls/TRepeatInfo.php b/framework/Web/UI/WebControls/TRepeatInfo.php index d7c6f0b6..2604e645 100644 --- a/framework/Web/UI/WebControls/TRepeatInfo.php +++ b/framework/Web/UI/WebControls/TRepeatInfo.php @@ -1,560 +1,560 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TTable'); - -/** - * IRepeatInfoUser interface. - * This interface must be implemented by classes who want to use {@link TRepeatInfo}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -interface IRepeatInfoUser -{ - /** - * @return boolean whether the repeat user contains footer - */ - public function getHasFooter(); - /** - * @return boolean whether the repeat user contains header - */ - public function getHasHeader(); - /** - * @return boolean whether the repeat user contains separators - */ - public function getHasSeparators(); - /** - * @return integer number of items to be rendered (excluding header, footer and separators) - */ - public function getItemCount(); - /** - * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) - * @param integer zero-based index of the current rendering item. - * @return TStyle CSS style used for rendering items (including header, footer and separators) - */ - public function generateItemStyle($itemType,$index); - /** - * Renders an item. - * @param THtmlWriter writer for the rendering purpose - * @param TRepeatInfo repeat information - * @param string item type - * @param integer zero-based index of the item being rendered - */ - public function renderItem($writer,$repeatInfo,$itemType,$index); -} - -/** - * TRepeatInfo class. - * TRepeatInfo represents repeat information for controls like {@link TCheckBoxList}. - * The layout of the repeated items is specified via {@link setRepeatLayout RepeatLayout}, - * which can be either Table (default), Flow or Raw. - * A table layout uses HTML table cells to organize the items while - * a flow layout uses line breaks to organize the items. - * The number of columns used to display the items is specified via - * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} - * governs the order of the items being rendered. - * - * Note, the Raw layout does not contain any formatting tags and thus ignores - * the column and repeat direction settings. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeatInfo extends TComponent -{ - /** - * @var string caption of the table used to organize the repeated items - */ - private $_caption=''; - /** - * @var TTableCaptionAlign alignment of the caption of the table used to organize the repeated items - */ - private $_captionAlign=TTableCaptionAlign::NotSet; - /** - * @var integer number of columns that the items should be arranged in - */ - private $_repeatColumns=0; - /** - * @var TRepeatDirection direction of the repetition - */ - private $_repeatDirection=TRepeatDirection::Vertical; - /** - * @var TRepeatLayout layout of the repeated items - */ - private $_repeatLayout=TRepeatLayout::Table; - - /** - * @return string caption of the table layout - */ - public function getCaption() - { - return $this->_caption; - } - - /** - * @param string caption of the table layout - */ - public function setCaption($value) - { - $this->_caption=$value; - } - - /** - * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. - */ - public function getCaptionAlign() - { - return $this->_captionAlign; - } - - /** - * @return TTableCaptionAlign alignment of the caption of the table layout. - */ - public function setCaptionAlign($value) - { - $this->_captionAlign=TPropertyValue::ensureEnum($value,'TTableCaptionAlign'); - } - - /** - * @return integer the number of columns that the repeated items should be displayed in. Defaults to 0, meaning not set. - */ - public function getRepeatColumns() - { - return $this->_repeatColumns; - } - - /** - * @param integer the number of columns that the repeated items should be displayed in. - */ - public function setRepeatColumns($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - throw new TInvalidDataValueException('repeatinfo_repeatcolumns_invalid'); - $this->_repeatColumns=$value; - } - - /** - * @return TRepeatDirection the direction of traversing the repeated items, defaults to TRepeatDirection::Vertical - */ - public function getRepeatDirection() - { - return $this->_repeatDirection; - } - - /** - * @param TRepeatDirection the direction of traversing the repeated items - */ - public function setRepeatDirection($value) - { - $this->_repeatDirection=TPropertyValue::ensureEnum($value,'TRepeatDirection'); - } - - /** - * @return TRepeatLayout how the repeated items should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. - */ - public function getRepeatLayout() - { - return $this->_repeatLayout; - } - - /** - * @param TRepeatLayout how the repeated items should be displayed, using table or using line breaks. - */ - public function setRepeatLayout($value) - { - $this->_repeatLayout=TPropertyValue::ensureEnum($value,'TRepeatLayout'); - } - - /** - * Renders the repeated items. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - */ - public function renderRepeater($writer, IRepeatInfoUser $user) - { - if($this->_repeatLayout===TRepeatLayout::Table) - { - $control=new TTable; - if($this->_caption!=='') - { - $control->setCaption($this->_caption); - $control->setCaptionAlign($this->_captionAlign); - } - } - else if($this->_repeatLayout===TRepeatLayout::Raw) - { - $this->renderRawContents($writer,$user); - return; - } - else - $control=new TWebControl; - $control->setID($user->getClientID()); - $control->copyBaseAttributes($user); - if($user->getHasStyle()) - $control->getStyle()->copyFrom($user->getStyle()); - $control->renderBeginTag($writer); - $writer->writeLine(); - - if($this->_repeatDirection===TRepeatDirection::Vertical) - $this->renderVerticalContents($writer,$user); - else - $this->renderHorizontalContents($writer,$user); - - $control->renderEndTag($writer); - } - - /** - * Renders contents in raw format. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - */ - protected function renderRawContents($writer,$user) - { - if($user->getHasHeader()) - $user->renderItem($writer,$this,'Header',-1); - - // render items - $hasSeparators=$user->getHasSeparators(); - $itemCount=$user->getItemCount(); - for($i=0;$i<$itemCount;++$i) - { - $user->renderItem($writer,$this,'Item',$i); - if($hasSeparators && $i!=$itemCount-1) - $user->renderItem($writer,$this,'Separator',$i); - } - if($user->getHasFooter()) - $user->renderItem($writer,$this,'Footer',-1); - } - - /** - * Renders contents in horizontal repeat direction. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - */ - protected function renderHorizontalContents($writer,$user) - { - $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); - $hasSeparators=$user->getHasSeparators(); - $itemCount=$user->getItemCount(); - $columns=$this->_repeatColumns===0?$itemCount:$this->_repeatColumns; - $totalColumns=$hasSeparators?$columns+$columns:$columns; - $needBreak=$columns<$itemCount; - - if($user->getHasHeader()) - $this->renderHeader($writer,$user,$tableLayout,$totalColumns,$needBreak); - - // render items - if($tableLayout) - { - $writer->renderBeginTag('tbody'); - $column=0; - for($i=0;$i<$itemCount;++$i) - { - if($column==0) - $writer->renderBeginTag('tr'); - if(($style=$user->generateItemStyle('Item',$i))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('td'); - $user->renderItem($writer,$this,'Item',$i); - $writer->renderEndTag(); - $writer->writeLine(); - if($hasSeparators && $i!=$itemCount-1) - { - if(($style=$user->generateItemStyle('Separator',$i))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('td'); - $user->renderItem($writer,$this,'Separator',$i); - $writer->renderEndTag(); - $writer->writeLine(); - } - $column++; - if($i==$itemCount-1) - { - $restColumns=$columns-$column; - if($hasSeparators) - $restColumns=$restColumns?$restColumns+$restColumns+1:1; - for($j=0;$j<$restColumns;++$j) - $writer->write("\n"); - } - if($column==$columns || $i==$itemCount-1) - { - $writer->renderEndTag(); - $writer->writeLine(); - $column=0; - } - } - $writer->renderEndTag(); - } - else - { - $column=0; - for($i=0;$i<$itemCount;++$i) - { - $user->renderItem($writer,$this,'Item',$i); - if($hasSeparators && $i!=$itemCount-1) - $user->renderItem($writer,$this,'Separator',$i); - $column++; - if($column==$columns || $i==$itemCount-1) - { - if($needBreak) - $writer->writeBreak(); - $column=0; - } - $writer->writeLine(); - } - } - - if($user->getHasFooter()) - $this->renderFooter($writer,$user,$tableLayout,$totalColumns,$needBreak); - } - - /** - * Renders contents in veritcal repeat direction. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - */ - protected function renderVerticalContents($writer,$user) - { - $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); - $hasSeparators=$user->getHasSeparators(); - $itemCount=$user->getItemCount(); - if($this->_repeatColumns<=1) - { - $rows=$itemCount; - $columns=1; - $lastColumns=1; - } - else - { - $columns=$this->_repeatColumns; - $rows=(int)(($itemCount+$columns-1)/$columns); - if($rows==0 && $itemCount>0) - $rows=1; - if(($lastColumns=$itemCount%$columns)==0) - $lastColumns=$columns; - } - $totalColumns=$hasSeparators?$columns+$columns:$columns; - - if($user->getHasHeader()) - $this->renderHeader($writer,$user,$tableLayout,$totalColumns,false); - - if($tableLayout) - { - $writer->renderBeginTag('tbody'); - $renderedItems=0; - for($row=0;$row<$rows;++$row) - { - $index=$row; - $writer->renderBeginTag('tr'); - for($col=0;$col<$columns;++$col) - { - if($renderedItems>=$itemCount) - break; - if($col>0) - { - $index+=$rows; - if($col-1>=$lastColumns) - $index--; - } - if($index>=$itemCount) - continue; - $renderedItems++; - if(($style=$user->generateItemStyle('Item',$index))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('td'); - $user->renderItem($writer,$this,'Item',$index); - $writer->renderEndTag(); - $writer->writeLine(); - if(!$hasSeparators) - continue; - if($renderedItems<$itemCount-1) - { - if($columns==1) - { - $writer->renderEndTag(); - $writer->renderBeginTag('tr'); - } - if(($style=$user->generateItemStyle('Separator',$index))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('td'); - $user->renderItem($writer,$this,'Separator',$index); - $writer->renderEndTag(); - $writer->writeLine(); - } - else if($columns>1) - $writer->write("\n"); - } - if($row==$rows-1) - { - $restColumns=$columns-$lastColumns; - if($hasSeparators) - $restColumns+=$restColumns; - for($col=0;$col<$restColumns;++$col) - $writer->write("\n"); - } - $writer->renderEndTag(); - $writer->writeLine(); - } - $writer->renderEndTag(); - } - else - { - $renderedItems=0; - for($row=0;$row<$rows;++$row) - { - $index=$row; - for($col=0;$col<$columns;++$col) - { - if($renderedItems>=$itemCount) - break; - if($col>0) - { - $index+=$rows; - if($col-1>=$lastColumns) - $index--; - } - if($index>=$itemCount) - continue; - $renderedItems++; - $user->renderItem($writer,$this,'Item',$index); - $writer->writeLine(); - if(!$hasSeparators) - continue; - if($renderedItems<$itemCount-1) - { - if($columns==1) - $writer->writeBreak(); - $user->renderItem($writer,$this,'Separator',$index); - } - $writer->writeLine(); - } - if($row<$rows-1 || $user->getHasFooter()) - $writer->writeBreak(); - } - } - - if($user->getHasFooter()) - $this->renderFooter($writer,$user,$tableLayout,$totalColumns,false); - - } - - /** - * Renders header. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - * @param boolean whether to render using table layout - * @param integer number of columns to be rendered - * @param boolean if a line break is needed at the end - */ - protected function renderHeader($writer,$user,$tableLayout,$columns,$needBreak) - { - if($tableLayout) - { - $writer->renderBeginTag('thead'); - $writer->renderBeginTag('tr'); - if($columns>1) - $writer->addAttribute('colspan',"$columns"); - $writer->addAttribute('scope','col'); - if(($style=$user->generateItemStyle('Header',-1))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('th'); - $user->renderItem($writer,$this,'Header',-1); - $writer->renderEndTag(); - $writer->renderEndTag(); - $writer->renderEndTag(); - } - else - { - $user->renderItem($writer,$this,'Header',-1); - if($needBreak) - $writer->writeBreak(); - } - $writer->writeLine(); - } - - /** - * Renders footer. - * @param THtmlWriter writer for the rendering purpose - * @param IRepeatInfoUser repeat information user - * @param boolean whether to render using table layout - * @param integer number of columns to be rendered - */ - protected function renderFooter($writer,$user,$tableLayout,$columns) - { - if($tableLayout) - { - $writer->renderBeginTag('tfoot'); - $writer->renderBeginTag('tr'); - if($columns>1) - $writer->addAttribute('colspan',"$columns"); - if(($style=$user->generateItemStyle('Footer',-1))!==null) - $style->addAttributesToRender($writer); - $writer->renderBeginTag('td'); - $user->renderItem($writer,$this,'Footer',-1); - $writer->renderEndTag(); - $writer->renderEndTag(); - $writer->renderEndTag(); - } - else - $user->renderItem($writer,$this,'Footer',-1); - $writer->writeLine(); - } -} - - -/** - * TRepeatDirection class. - * TRepeatDirection defines the enumerable type for the possible directions - * that repeated contents can repeat along - * - * The following enumerable values are defined: - * - Vertical - * - Horizontal - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TRepeatDirection extends TEnumerable -{ - const Vertical='Vertical'; - const Horizontal='Horizontal'; -} - -/** - * TRepeatLayout class. - * TRepeatLayout defines the enumerable type for the possible layouts - * that repeated contents can take. - * - * The following enumerable values are defined: - * - Table: the repeated contents are organized using an HTML table - * - Flow: the repeated contents are organized using HTML spans and breaks - * - Raw: the repeated contents are stacked together without any additional decorations - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TRepeatLayout extends TEnumerable -{ - const Table='Table'; - const Flow='Flow'; - const Raw='Raw'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TTable'); + +/** + * IRepeatInfoUser interface. + * This interface must be implemented by classes who want to use {@link TRepeatInfo}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +interface IRepeatInfoUser +{ + /** + * @return boolean whether the repeat user contains footer + */ + public function getHasFooter(); + /** + * @return boolean whether the repeat user contains header + */ + public function getHasHeader(); + /** + * @return boolean whether the repeat user contains separators + */ + public function getHasSeparators(); + /** + * @return integer number of items to be rendered (excluding header, footer and separators) + */ + public function getItemCount(); + /** + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer zero-based index of the current rendering item. + * @return TStyle CSS style used for rendering items (including header, footer and separators) + */ + public function generateItemStyle($itemType,$index); + /** + * Renders an item. + * @param THtmlWriter writer for the rendering purpose + * @param TRepeatInfo repeat information + * @param string item type + * @param integer zero-based index of the item being rendered + */ + public function renderItem($writer,$repeatInfo,$itemType,$index); +} + +/** + * TRepeatInfo class. + * TRepeatInfo represents repeat information for controls like {@link TCheckBoxList}. + * The layout of the repeated items is specified via {@link setRepeatLayout RepeatLayout}, + * which can be either Table (default), Flow or Raw. + * A table layout uses HTML table cells to organize the items while + * a flow layout uses line breaks to organize the items. + * The number of columns used to display the items is specified via + * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} + * governs the order of the items being rendered. + * + * Note, the Raw layout does not contain any formatting tags and thus ignores + * the column and repeat direction settings. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeatInfo extends TComponent +{ + /** + * @var string caption of the table used to organize the repeated items + */ + private $_caption=''; + /** + * @var TTableCaptionAlign alignment of the caption of the table used to organize the repeated items + */ + private $_captionAlign=TTableCaptionAlign::NotSet; + /** + * @var integer number of columns that the items should be arranged in + */ + private $_repeatColumns=0; + /** + * @var TRepeatDirection direction of the repetition + */ + private $_repeatDirection=TRepeatDirection::Vertical; + /** + * @var TRepeatLayout layout of the repeated items + */ + private $_repeatLayout=TRepeatLayout::Table; + + /** + * @return string caption of the table layout + */ + public function getCaption() + { + return $this->_caption; + } + + /** + * @param string caption of the table layout + */ + public function setCaption($value) + { + $this->_caption=$value; + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->_captionAlign; + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. + */ + public function setCaptionAlign($value) + { + $this->_captionAlign=TPropertyValue::ensureEnum($value,'TTableCaptionAlign'); + } + + /** + * @return integer the number of columns that the repeated items should be displayed in. Defaults to 0, meaning not set. + */ + public function getRepeatColumns() + { + return $this->_repeatColumns; + } + + /** + * @param integer the number of columns that the repeated items should be displayed in. + */ + public function setRepeatColumns($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('repeatinfo_repeatcolumns_invalid'); + $this->_repeatColumns=$value; + } + + /** + * @return TRepeatDirection the direction of traversing the repeated items, defaults to TRepeatDirection::Vertical + */ + public function getRepeatDirection() + { + return $this->_repeatDirection; + } + + /** + * @param TRepeatDirection the direction of traversing the repeated items + */ + public function setRepeatDirection($value) + { + $this->_repeatDirection=TPropertyValue::ensureEnum($value,'TRepeatDirection'); + } + + /** + * @return TRepeatLayout how the repeated items should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. + */ + public function getRepeatLayout() + { + return $this->_repeatLayout; + } + + /** + * @param TRepeatLayout how the repeated items should be displayed, using table or using line breaks. + */ + public function setRepeatLayout($value) + { + $this->_repeatLayout=TPropertyValue::ensureEnum($value,'TRepeatLayout'); + } + + /** + * Renders the repeated items. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + public function renderRepeater($writer, IRepeatInfoUser $user) + { + if($this->_repeatLayout===TRepeatLayout::Table) + { + $control=new TTable; + if($this->_caption!=='') + { + $control->setCaption($this->_caption); + $control->setCaptionAlign($this->_captionAlign); + } + } + else if($this->_repeatLayout===TRepeatLayout::Raw) + { + $this->renderRawContents($writer,$user); + return; + } + else + $control=new TWebControl; + $control->setID($user->getClientID()); + $control->copyBaseAttributes($user); + if($user->getHasStyle()) + $control->getStyle()->copyFrom($user->getStyle()); + $control->renderBeginTag($writer); + $writer->writeLine(); + + if($this->_repeatDirection===TRepeatDirection::Vertical) + $this->renderVerticalContents($writer,$user); + else + $this->renderHorizontalContents($writer,$user); + + $control->renderEndTag($writer); + } + + /** + * Renders contents in raw format. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderRawContents($writer,$user) + { + if($user->getHasHeader()) + $user->renderItem($writer,$this,'Header',-1); + + // render items + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + for($i=0;$i<$itemCount;++$i) + { + $user->renderItem($writer,$this,'Item',$i); + if($hasSeparators && $i!=$itemCount-1) + $user->renderItem($writer,$this,'Separator',$i); + } + if($user->getHasFooter()) + $user->renderItem($writer,$this,'Footer',-1); + } + + /** + * Renders contents in horizontal repeat direction. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderHorizontalContents($writer,$user) + { + $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + $columns=$this->_repeatColumns===0?$itemCount:$this->_repeatColumns; + $totalColumns=$hasSeparators?$columns+$columns:$columns; + $needBreak=$columns<$itemCount; + + if($user->getHasHeader()) + $this->renderHeader($writer,$user,$tableLayout,$totalColumns,$needBreak); + + // render items + if($tableLayout) + { + $writer->renderBeginTag('tbody'); + $column=0; + for($i=0;$i<$itemCount;++$i) + { + if($column==0) + $writer->renderBeginTag('tr'); + if(($style=$user->generateItemStyle('Item',$i))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Item',$i); + $writer->renderEndTag(); + $writer->writeLine(); + if($hasSeparators && $i!=$itemCount-1) + { + if(($style=$user->generateItemStyle('Separator',$i))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Separator',$i); + $writer->renderEndTag(); + $writer->writeLine(); + } + $column++; + if($i==$itemCount-1) + { + $restColumns=$columns-$column; + if($hasSeparators) + $restColumns=$restColumns?$restColumns+$restColumns+1:1; + for($j=0;$j<$restColumns;++$j) + $writer->write("\n"); + } + if($column==$columns || $i==$itemCount-1) + { + $writer->renderEndTag(); + $writer->writeLine(); + $column=0; + } + } + $writer->renderEndTag(); + } + else + { + $column=0; + for($i=0;$i<$itemCount;++$i) + { + $user->renderItem($writer,$this,'Item',$i); + if($hasSeparators && $i!=$itemCount-1) + $user->renderItem($writer,$this,'Separator',$i); + $column++; + if($column==$columns || $i==$itemCount-1) + { + if($needBreak) + $writer->writeBreak(); + $column=0; + } + $writer->writeLine(); + } + } + + if($user->getHasFooter()) + $this->renderFooter($writer,$user,$tableLayout,$totalColumns,$needBreak); + } + + /** + * Renders contents in veritcal repeat direction. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderVerticalContents($writer,$user) + { + $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + if($this->_repeatColumns<=1) + { + $rows=$itemCount; + $columns=1; + $lastColumns=1; + } + else + { + $columns=$this->_repeatColumns; + $rows=(int)(($itemCount+$columns-1)/$columns); + if($rows==0 && $itemCount>0) + $rows=1; + if(($lastColumns=$itemCount%$columns)==0) + $lastColumns=$columns; + } + $totalColumns=$hasSeparators?$columns+$columns:$columns; + + if($user->getHasHeader()) + $this->renderHeader($writer,$user,$tableLayout,$totalColumns,false); + + if($tableLayout) + { + $writer->renderBeginTag('tbody'); + $renderedItems=0; + for($row=0;$row<$rows;++$row) + { + $index=$row; + $writer->renderBeginTag('tr'); + for($col=0;$col<$columns;++$col) + { + if($renderedItems>=$itemCount) + break; + if($col>0) + { + $index+=$rows; + if($col-1>=$lastColumns) + $index--; + } + if($index>=$itemCount) + continue; + $renderedItems++; + if(($style=$user->generateItemStyle('Item',$index))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Item',$index); + $writer->renderEndTag(); + $writer->writeLine(); + if(!$hasSeparators) + continue; + if($renderedItems<$itemCount-1) + { + if($columns==1) + { + $writer->renderEndTag(); + $writer->renderBeginTag('tr'); + } + if(($style=$user->generateItemStyle('Separator',$index))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Separator',$index); + $writer->renderEndTag(); + $writer->writeLine(); + } + else if($columns>1) + $writer->write("\n"); + } + if($row==$rows-1) + { + $restColumns=$columns-$lastColumns; + if($hasSeparators) + $restColumns+=$restColumns; + for($col=0;$col<$restColumns;++$col) + $writer->write("\n"); + } + $writer->renderEndTag(); + $writer->writeLine(); + } + $writer->renderEndTag(); + } + else + { + $renderedItems=0; + for($row=0;$row<$rows;++$row) + { + $index=$row; + for($col=0;$col<$columns;++$col) + { + if($renderedItems>=$itemCount) + break; + if($col>0) + { + $index+=$rows; + if($col-1>=$lastColumns) + $index--; + } + if($index>=$itemCount) + continue; + $renderedItems++; + $user->renderItem($writer,$this,'Item',$index); + $writer->writeLine(); + if(!$hasSeparators) + continue; + if($renderedItems<$itemCount-1) + { + if($columns==1) + $writer->writeBreak(); + $user->renderItem($writer,$this,'Separator',$index); + } + $writer->writeLine(); + } + if($row<$rows-1 || $user->getHasFooter()) + $writer->writeBreak(); + } + } + + if($user->getHasFooter()) + $this->renderFooter($writer,$user,$tableLayout,$totalColumns,false); + + } + + /** + * Renders header. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + * @param boolean whether to render using table layout + * @param integer number of columns to be rendered + * @param boolean if a line break is needed at the end + */ + protected function renderHeader($writer,$user,$tableLayout,$columns,$needBreak) + { + if($tableLayout) + { + $writer->renderBeginTag('thead'); + $writer->renderBeginTag('tr'); + if($columns>1) + $writer->addAttribute('colspan',"$columns"); + $writer->addAttribute('scope','col'); + if(($style=$user->generateItemStyle('Header',-1))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('th'); + $user->renderItem($writer,$this,'Header',-1); + $writer->renderEndTag(); + $writer->renderEndTag(); + $writer->renderEndTag(); + } + else + { + $user->renderItem($writer,$this,'Header',-1); + if($needBreak) + $writer->writeBreak(); + } + $writer->writeLine(); + } + + /** + * Renders footer. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + * @param boolean whether to render using table layout + * @param integer number of columns to be rendered + */ + protected function renderFooter($writer,$user,$tableLayout,$columns) + { + if($tableLayout) + { + $writer->renderBeginTag('tfoot'); + $writer->renderBeginTag('tr'); + if($columns>1) + $writer->addAttribute('colspan',"$columns"); + if(($style=$user->generateItemStyle('Footer',-1))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Footer',-1); + $writer->renderEndTag(); + $writer->renderEndTag(); + $writer->renderEndTag(); + } + else + $user->renderItem($writer,$this,'Footer',-1); + $writer->writeLine(); + } +} + + +/** + * TRepeatDirection class. + * TRepeatDirection defines the enumerable type for the possible directions + * that repeated contents can repeat along + * + * The following enumerable values are defined: + * - Vertical + * - Horizontal + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TRepeatDirection extends TEnumerable +{ + const Vertical='Vertical'; + const Horizontal='Horizontal'; +} + +/** + * TRepeatLayout class. + * TRepeatLayout defines the enumerable type for the possible layouts + * that repeated contents can take. + * + * The following enumerable values are defined: + * - Table: the repeated contents are organized using an HTML table + * - Flow: the repeated contents are organized using HTML spans and breaks + * - Raw: the repeated contents are stacked together without any additional decorations + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TRepeatLayout extends TEnumerable +{ + const Table='Table'; + const Flow='Flow'; + const Raw='Raw'; +} + diff --git a/framework/Web/UI/WebControls/TRepeater.php b/framework/Web/UI/WebControls/TRepeater.php index 6a59cbab..944b3a17 100644 --- a/framework/Web/UI/WebControls/TRepeater.php +++ b/framework/Web/UI/WebControls/TRepeater.php @@ -1,1025 +1,1025 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TDataBoundControl and TDataFieldAccessor cass - */ -Prado::using('System.Web.UI.WebControls.TDataBoundControl'); -Prado::using('System.Util.TDataFieldAccessor'); - -/** - * TRepeater class. - * - * TRepeater displays its content repeatedly based on the data fetched from - * {@link setDataSource DataSource}. - * The repeated contents in TRepeater are called items, which are controls and - * can be accessed through {@link getItems Items}. When {@link dataBind()} is invoked, - * TRepeater creates an item for each row of data and binds the data row to the item. - * Optionally, a repeater can have a header, a footer and/or separators between items. - * - * The layout of the repeated contents are specified by inline templates. - * Repeater items, header, footer, etc. are being instantiated with the corresponding - * templates when data is being bound to the repeater. - * - * Since v3.1.0, the layout can also be specified by renderers. A renderer is a control class - * that can be instantiated as repeater items, header, etc. A renderer can thus be viewed - * as an external template (in fact, it can also be non-templated controls). - * - * A renderer can be any control class. - * - If the class implements {@link IDataRenderer}, the Data - * property will be set as the data row during databinding. Many PRADO controls - * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. - * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set - * as the zero-based index of the item in the repeater item collection, and - * the ItemType property as the item's type (such as TListItemType::Item). - * {@link TRepeaterItemRenderer} may be used as the convenient base class which - * already implements {@link IDataItemRenderer}. - * - * The following properties are used to specify different types of template and renderer - * for a repeater: - * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: - * for each repeated row of data - * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: - * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} - * will be used instead. - * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: - * for the repeater header. - * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: - * for the repeater footer. - * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: - * for content to be displayed between items. - * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: - * used when data bound to the repeater is empty. - * - * If a content type is defined with both a template and a renderer, the latter takes precedence. - * - * When {@link dataBind()} is being called, TRepeater undergoes the following lifecycles for each row of data: - * - create item based on templates or renderers - * - set the row of data to the item - * - raise {@link onItemCreated OnItemCreated}: - * - add the item as a child control - * - call dataBind() of the item - * - raise {@link onItemDataBound OnItemDataBound}: - * - * TRepeater raises an {@link onItemCommand OnItemCommand} whenever a button control - * within some repeater item raises a OnCommand event. Therefore, - * you can handle all sorts of OnCommand event in a central place by - * writing an event handler for {@link onItemCommand OnItemCommand}. - * - * When a page containing a repeater is post back, the repeater will restore automatically - * all its contents, including items, header, footer and separators. - * However, the data row associated with each item will not be recovered and become null. - * To access the data, use one of the following ways: - * - Use {@link getDataKeys DataKeys} to obtain the data key associated with - * the specified repeater item and use the key to fetch the corresponding data - * from some persistent storage such as DB. - * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. - * Be aware though, if the size of your dataset is big, your page size will become big. Some - * complex data may also have serializing problem if saved in viewstate. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeater extends TDataBoundControl implements INamingContainer -{ - /** - * Repeater item types - * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. - */ - const IT_HEADER='Header'; - const IT_FOOTER='Footer'; - const IT_ITEM='Item'; - const IT_SEPARATOR='Separator'; - const IT_ALTERNATINGITEM='AlternatingItem'; - - /** - * @var ITemplate template for repeater items - */ - private $_itemTemplate=null; - /** - * @var ITemplate template for each alternating item - */ - private $_alternatingItemTemplate=null; - /** - * @var ITemplate template for header - */ - private $_headerTemplate=null; - /** - * @var ITemplate template for footer - */ - private $_footerTemplate=null; - /** - * @var ITemplate template used for repeater when no data is bound - */ - private $_emptyTemplate=null; - /** - * @var ITemplate template for separator - */ - private $_separatorTemplate=null; - /** - * @var TRepeaterItemCollection list of repeater items - */ - private $_items=null; - /** - * @var TControl header item - */ - private $_header=null; - /** - * @var TControl footer item - */ - private $_footer=null; - - - /** - * @return string the class name for repeater items. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getItemRenderer() - { - return $this->getViewState('ItemRenderer',''); - } - - /** - * Sets the item renderer class. - * - * If not empty, the class will be used to instantiate as repeater items. - * This property takes precedence over {@link getItemTemplate ItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setItemTemplate - * @since 3.1.0 - */ - public function setItemRenderer($value) - { - $this->setViewState('ItemRenderer',$value,''); - } - - /** - * @return string the class name for alternative repeater items. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getAlternatingItemRenderer() - { - return $this->getViewState('AlternatingItemRenderer',''); - } - - /** - * Sets the alternative item renderer class. - * - * If not empty, the class will be used to instantiate as alternative repeater items. - * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setAlternatingItemTemplate - * @since 3.1.0 - */ - public function setAlternatingItemRenderer($value) - { - $this->setViewState('AlternatingItemRenderer',$value,''); - } - - /** - * @return string the class name for repeater item separators. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getSeparatorRenderer() - { - return $this->getViewState('SeparatorRenderer',''); - } - - /** - * Sets the repeater item separator renderer class. - * - * If not empty, the class will be used to instantiate as repeater item separators. - * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setSeparatorTemplate - * @since 3.1.0 - */ - public function setSeparatorRenderer($value) - { - $this->setViewState('SeparatorRenderer',$value,''); - } - - /** - * @return string the class name for repeater header item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getHeaderRenderer() - { - return $this->getViewState('HeaderRenderer',''); - } - - /** - * Sets the repeater header renderer class. - * - * If not empty, the class will be used to instantiate as repeater header item. - * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setHeaderTemplate - * @since 3.1.0 - */ - public function setHeaderRenderer($value) - { - $this->setViewState('HeaderRenderer',$value,''); - } - - /** - * @return string the class name for repeater footer item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getFooterRenderer() - { - return $this->getViewState('FooterRenderer',''); - } - - /** - * Sets the repeater footer renderer class. - * - * If not empty, the class will be used to instantiate as repeater footer item. - * This property takes precedence over {@link getFooterTemplate FooterTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setFooterTemplate - * @since 3.1.0 - */ - public function setFooterRenderer($value) - { - $this->setViewState('FooterRenderer',$value,''); - } - - /** - * @return string the class name for empty repeater item. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getEmptyRenderer() - { - return $this->getViewState('EmptyRenderer',''); - } - - /** - * Sets the repeater empty renderer class. - * - * The empty renderer is created as the child of the repeater - * if data bound to the repeater is empty. - * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. - * - * @param string the renderer class name in namespace format. - * @see setEmptyTemplate - * @since 3.1.0 - */ - public function setEmptyRenderer($value) - { - $this->setViewState('EmptyRenderer',$value,''); - } - - /** - * @return ITemplate the template for repeater items - */ - public function getItemTemplate() - { - return $this->_itemTemplate; - } - - /** - * @param ITemplate the template for repeater items - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_itemTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','ItemTemplate'); - } - - /** - * @return ITemplate the alternative template string for the item - */ - public function getAlternatingItemTemplate() - { - return $this->_alternatingItemTemplate; - } - - /** - * @param ITemplate the alternative item template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setAlternatingItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_alternatingItemTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','AlternatingItemTemplate'); - } - - /** - * @return ITemplate the header template - */ - public function getHeaderTemplate() - { - return $this->_headerTemplate; - } - - /** - * @param ITemplate the header template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setHeaderTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_headerTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','HeaderTemplate'); - } - - /** - * @return ITemplate the footer template - */ - public function getFooterTemplate() - { - return $this->_footerTemplate; - } - - /** - * @param ITemplate the footer template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setFooterTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_footerTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','FooterTemplate'); - } - - /** - * @return ITemplate the template applied when no data is bound to the repeater - */ - public function getEmptyTemplate() - { - return $this->_emptyTemplate; - } - - /** - * @param ITemplate the template applied when no data is bound to the repeater - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setEmptyTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_emptyTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','EmptyTemplate'); - } - - /** - * @return ITemplate the separator template - */ - public function getSeparatorTemplate() - { - return $this->_separatorTemplate; - } - - /** - * @param ITemplate the separator template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setSeparatorTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_separatorTemplate=$value; - else - throw new TInvalidDataTypeException('repeater_template_required','SeparatorTemplate'); - } - - /** - * @return TControl the header item - */ - public function getHeader() - { - return $this->_header; - } - - /** - * @return TControl the footer item - */ - public function getFooter() - { - return $this->_footer; - } - - /** - * @return TRepeaterItemCollection list of repeater item controls - */ - public function getItems() - { - if(!$this->_items) - $this->_items=new TRepeaterItemCollection; - return $this->_items; - } - - /** - * @return string the field of the data source that provides the keys of the list items. - */ - public function getDataKeyField() - { - return $this->getViewState('DataKeyField',''); - } - - /** - * @param string the field of the data source that provides the keys of the list items. - */ - public function setDataKeyField($value) - { - $this->setViewState('DataKeyField',$value,''); - } - - /** - * @return TList the keys used in the data listing control. - */ - public function getDataKeys() - { - if(($dataKeys=$this->getViewState('DataKeys',null))===null) - { - $dataKeys=new TList; - $this->setViewState('DataKeys',$dataKeys,null); - } - return $dataKeys; - } - - /** - * Creates a repeater item. - * This method invokes {@link createItem} to create a new repeater item. - * @param integer zero-based item index. - * @param TListItemType item type - * @return TControl the created item, null if item is not created - */ - private function createItemInternal($itemIndex,$itemType) - { - if(($item=$this->createItem($itemIndex,$itemType))!==null) - { - $param=new TRepeaterItemEventParameter($item); - $this->onItemCreated($param); - $this->getControls()->add($item); - return $item; - } - else - return null; - } - - /** - * Creates a repeater item and performs databinding. - * This method invokes {@link createItem} to create a new repeater item. - * @param integer zero-based item index. - * @param TListItemType item type - * @param mixed data to be associated with the item - * @return TControl the created item, null if item is not created - */ - private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) - { - if(($item=$this->createItem($itemIndex,$itemType))!==null) - { - $param=new TRepeaterItemEventParameter($item); - if($item instanceof IDataRenderer) - $item->setData($dataItem); - $this->onItemCreated($param); - $this->getControls()->add($item); - $item->dataBind(); - $this->onItemDataBound($param); - return $item; - } - else - return null; - } - - /** - * Creates a repeater item instance based on the item type and index. - * @param integer zero-based item index - * @param TListItemType item type - * @return TControl created repeater item - */ - protected function createItem($itemIndex,$itemType) - { - $template=null; - $classPath=null; - switch($itemType) - { - case TListItemType::Item : - $classPath=$this->getItemRenderer(); - $template=$this->_itemTemplate; - break; - case TListItemType::AlternatingItem : - if(($classPath=$this->getAlternatingItemRenderer())==='' && ($template=$this->_alternatingItemTemplate)===null) - { - $classPath=$this->getItemRenderer(); - $template=$this->_itemTemplate; - } - break; - case TListItemType::Header : - $classPath=$this->getHeaderRenderer(); - $template=$this->_headerTemplate; - break; - case TListItemType::Footer : - $classPath=$this->getFooterRenderer(); - $template=$this->_footerTemplate; - break; - case TListItemType::Separator : - $classPath=$this->getSeparatorRenderer(); - $template=$this->_separatorTemplate; - break; - default: - throw new TInvalidDataValueException('repeater_itemtype_unknown',$itemType); - } - if($classPath!=='') - { - $item=Prado::createComponent($classPath); - if($item instanceof IItemDataRenderer) - { - $item->setItemIndex($itemIndex); - $item->setItemType($itemType); - } - } - else if($template!==null) - { - $item=new TRepeaterItem; - $item->setItemIndex($itemIndex); - $item->setItemType($itemType); - $template->instantiateIn($item); - } - else - $item=null; - - return $item; - } - - /** - * Creates empty repeater content. - */ - protected function createEmptyContent() - { - if(($classPath=$this->getEmptyRenderer())!=='') - $this->getControls()->add(Prado::createComponent($classPath)); - else if($this->_emptyTemplate!==null) - $this->_emptyTemplate->instantiateIn($this); - } - - /** - * Renders the repeater. - * This method overrides the parent implementation by rendering the body - * content as the whole presentation of the repeater. Outer tag is not rendered. - * @param THtmlWriter writer - */ - public function render($writer) - { - if($this->_items && $this->_items->getCount() || $this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') - $this->renderContents($writer); - } - - /** - * Saves item count in viewstate. - * This method is invoked right before control state is to be saved. - */ - public function saveState() - { - parent::saveState(); - if($this->_items) - $this->setViewState('ItemCount',$this->_items->getCount(),0); - else - $this->clearViewState('ItemCount'); - } - - /** - * Loads item count information from viewstate. - * This method is invoked right after control state is loaded. - */ - public function loadState() - { - parent::loadState(); - if(!$this->getIsDataBound()) - $this->restoreItemsFromViewState(); - $this->clearViewState('ItemCount'); - } - - /** - * Clears up all items in the repeater. - */ - public function reset() - { - $this->getControls()->clear(); - $this->getItems()->clear(); - $this->_header=null; - $this->_footer=null; - } - - /** - * Creates repeater items based on viewstate information. - */ - protected function restoreItemsFromViewState() - { - $this->reset(); - if(($itemCount=$this->getViewState('ItemCount',0))>0) - { - $items=$this->getItems(); - $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; - $this->_header=$this->createItemInternal(-1,TListItemType::Header); - for($i=0;$i<$itemCount;++$i) - { - if($hasSeparator && $i>0) - $this->createItemInternal($i-1,TListItemType::Separator); - $itemType=$i%2==0?TListItemType::Item : TListItemType::AlternatingItem; - $items->add($this->createItemInternal($i,$itemType,false,null)); - } - $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); - } - else - $this->createEmptyContent(); - $this->clearChildState(); - } - - /** - * Performs databinding to populate repeater items from data source. - * This method is invoked by dataBind(). - * You may override this function to provide your own way of data population. - * @param Traversable the data - */ - protected function performDataBinding($data) - { - $this->reset(); - - $keys=$this->getDataKeys(); - $keys->clear(); - $keyField=$this->getDataKeyField(); - - $items=$this->getItems(); - $itemIndex=0; - $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; - foreach($data as $key=>$dataItem) - { - if($keyField!=='') - $keys->add($this->getDataFieldValue($dataItem,$keyField)); - else - $keys->add($key); - if($itemIndex===0) - $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); - if($hasSeparator && $itemIndex>0) - $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); - $itemType=$itemIndex%2==0?TListItemType::Item : TListItemType::AlternatingItem; - $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); - $itemIndex++; - } - if($itemIndex>0) - $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); - else - { - $this->createEmptyContent(); - $this->dataBindChildren(); - } - $this->setViewState('ItemCount',$itemIndex,0); - } - - /** - * This method overrides parent's implementation to handle - * {@link onItemCommand OnItemCommand} event which is bubbled from - * repeater items and their child controls. - * This method should only be used by control developers. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TRepeaterCommandEventParameter) - { - $this->onItemCommand($param); - return true; - } - else - return false; - } - - /** - * Raises OnItemCreated event. - * This method is invoked after a repeater item is created and instantiated with - * template, but before added to the page hierarchy. - * The repeater item control responsible for the event - * can be determined from the event parameter. - * If you override this method, be sure to call parent's implementation - * so that event handlers have chance to respond to the event. - * @param TRepeaterItemEventParameter event parameter - */ - public function onItemCreated($param) - { - $this->raiseEvent('OnItemCreated',$this,$param); - } - - /** - * Raises OnItemDataBound event. - * This method is invoked right after an item is data bound. - * The repeater item control responsible for the event - * can be determined from the event parameter. - * If you override this method, be sure to call parent's implementation - * so that event handlers have chance to respond to the event. - * @param TRepeaterItemEventParameter event parameter - */ - public function onItemDataBound($param) - { - $this->raiseEvent('OnItemDataBound',$this,$param); - } - - /** - * Raises OnItemCommand event. - * This method is invoked after a button control in - * a template raises OnCommand event. - * The repeater control responsible for the event - * can be determined from the event parameter. - * The event parameter also contains the information about - * the initial sender of the OnCommand event, command name - * and command parameter. - * You may override this method to provide customized event handling. - * Be sure to call parent's implementation so that - * event handlers have chance to respond to the event. - * @param TRepeaterCommandEventParameter event parameter - */ - public function onItemCommand($param) - { - $this->raiseEvent('OnItemCommand',$this,$param); - } - - /** - * Returns the value of the data at the specified field. - * If data is an array, TMap or TList, the value will be returned at the index - * of the specified field. If the data is a component with a property named - * as the field name, the property value will be returned. - * Otherwise, an exception will be raised. - * @param mixed data item - * @param mixed field name - * @return mixed data value at the specified field - * @throws TInvalidDataValueException if the data is invalid - */ - protected function getDataFieldValue($data,$field) - { - return TDataFieldAccessor::getDataFieldValue($data,$field); - } -} - -/** - * TRepeaterItemEventParameter class - * - * TRepeaterItemEventParameter encapsulates the parameter data for - * {@link TRepeater::onItemCreated ItemCreated} event of {@link TRepeater} controls. - * The {@link getItem Item} property indicates the repeater item related with the event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeaterItemEventParameter extends TEventParameter -{ - /** - * The repeater item control responsible for the event. - * @var TControl - */ - private $_item=null; - - /** - * Constructor. - * @param TControl repeater item related with the corresponding event - */ - public function __construct($item) - { - $this->_item=$item; - } - - /** - * @return TControl repeater item related with the corresponding event - */ - public function getItem() - { - return $this->_item; - } -} - -/** - * TRepeaterCommandEventParameter class - * - * TRepeaterCommandEventParameter encapsulates the parameter data for - * {@link TRepeater::onItemCommand ItemCommand} event of {@link TRepeater} controls. - * - * The {@link getItem Item} property indicates the repeater item related with the event. - * The {@link getCommandSource CommandSource} refers to the control that originally - * raises the Command event. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeaterCommandEventParameter extends TCommandEventParameter -{ - /** - * @var TControl the repeater item control responsible for the event. - */ - private $_item=null; - /** - * @var TControl the control originally raises the OnCommand event. - */ - private $_source=null; - - /** - * Constructor. - * @param TControl repeater item responsible for the event - * @param TControl original event sender - * @param TCommandEventParameter original event parameter - */ - public function __construct($item,$source,TCommandEventParameter $param) - { - $this->_item=$item; - $this->_source=$source; - parent::__construct($param->getCommandName(),$param->getCommandParameter()); - } - - /** - * @return TControl the repeater item control responsible for the event. - */ - public function getItem() - { - return $this->_item; - } - - /** - * @return TControl the control originally raises the OnCommand event. - */ - public function getCommandSource() - { - return $this->_source; - } -} - -/** - * TRepeaterItem class - * - * A TRepeaterItem control represents an item in the {@link TRepeater} control, - * such as heading section, footer section, or a data item. - * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> - * and {@link getDataItem DataItem} properties, respectively. The type of the item - * is given by {@link getItemType ItemType} property. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeaterItem extends TControl implements INamingContainer, IItemDataRenderer -{ - /** - * index of the data item in the Items collection of repeater - */ - private $_itemIndex; - /** - * type of the TRepeaterItem - * @var TListItemType - */ - private $_itemType; - /** - * data associated with this item - * @var mixed - */ - private $_data; - - /** - * @return TListItemType item type - */ - public function getItemType() - { - return $this->_itemType; - } - - /** - * @param TListItemType item type. - */ - public function setItemType($value) - { - $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); - } - - /** - * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. - * If the item is not in the collection (e.g. it is a header item), it returns -1. - * @return integer zero-based index of the item. - */ - public function getItemIndex() - { - return $this->_itemIndex; - } - - /** - * Sets the zero-based index for the item. - * If the item is not in the item collection (e.g. it is a header item), -1 should be used. - * @param integer zero-based index of the item. - */ - public function setItemIndex($value) - { - $this->_itemIndex=TPropertyValue::ensureInteger($value); - } - - /** - * @return mixed data associated with the item - * @since 3.1.0 - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed data to be associated with the item - * @since 3.1.0 - */ - public function setData($value) - { - $this->_data=$value; - } - - /** - * This property is deprecated since v3.1.0. - * @return mixed data associated with the item - * @deprecated deprecated since v3.1.0. Use {@link getData} instead. - */ - public function getDataItem() - { - return $this->getData(); - } - - /** - * This property is deprecated since v3.1.0. - * @param mixed data to be associated with the item - * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. - */ - public function setDataItem($value) - { - return $this->setData($value); - } - - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } -} - - -/** - * TRepeaterItemCollection class. - * - * TRepeaterItemCollection represents a collection of repeater items. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRepeaterItemCollection extends TList -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by inserting only objects that are descendant of {@link TControl}. - * @param integer the speicified position. - * @param TControl new item - * @throws TInvalidDataTypeException if the item to be inserted is not a control. - */ - public function insertAt($index,$item) - { - if($item instanceof TControl) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('repeateritemcollection_item_invalid'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TDataBoundControl and TDataFieldAccessor cass + */ +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Util.TDataFieldAccessor'); + +/** + * TRepeater class. + * + * TRepeater displays its content repeatedly based on the data fetched from + * {@link setDataSource DataSource}. + * The repeated contents in TRepeater are called items, which are controls and + * can be accessed through {@link getItems Items}. When {@link dataBind()} is invoked, + * TRepeater creates an item for each row of data and binds the data row to the item. + * Optionally, a repeater can have a header, a footer and/or separators between items. + * + * The layout of the repeated contents are specified by inline templates. + * Repeater items, header, footer, etc. are being instantiated with the corresponding + * templates when data is being bound to the repeater. + * + * Since v3.1.0, the layout can also be specified by renderers. A renderer is a control class + * that can be instantiated as repeater items, header, etc. A renderer can thus be viewed + * as an external template (in fact, it can also be non-templated controls). + * + * A renderer can be any control class. + * - If the class implements {@link IDataRenderer}, the Data + * property will be set as the data row during databinding. Many PRADO controls + * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. + * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set + * as the zero-based index of the item in the repeater item collection, and + * the ItemType property as the item's type (such as TListItemType::Item). + * {@link TRepeaterItemRenderer} may be used as the convenient base class which + * already implements {@link IDataItemRenderer}. + * + * The following properties are used to specify different types of template and renderer + * for a repeater: + * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: + * for each repeated row of data + * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: + * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} + * will be used instead. + * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: + * for the repeater header. + * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: + * for the repeater footer. + * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: + * for content to be displayed between items. + * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: + * used when data bound to the repeater is empty. + * + * If a content type is defined with both a template and a renderer, the latter takes precedence. + * + * When {@link dataBind()} is being called, TRepeater undergoes the following lifecycles for each row of data: + * - create item based on templates or renderers + * - set the row of data to the item + * - raise {@link onItemCreated OnItemCreated}: + * - add the item as a child control + * - call dataBind() of the item + * - raise {@link onItemDataBound OnItemDataBound}: + * + * TRepeater raises an {@link onItemCommand OnItemCommand} whenever a button control + * within some repeater item raises a OnCommand event. Therefore, + * you can handle all sorts of OnCommand event in a central place by + * writing an event handler for {@link onItemCommand OnItemCommand}. + * + * When a page containing a repeater is post back, the repeater will restore automatically + * all its contents, including items, header, footer and separators. + * However, the data row associated with each item will not be recovered and become null. + * To access the data, use one of the following ways: + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified repeater item and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. + * Be aware though, if the size of your dataset is big, your page size will become big. Some + * complex data may also have serializing problem if saved in viewstate. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeater extends TDataBoundControl implements INamingContainer +{ + /** + * Repeater item types + * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. + */ + const IT_HEADER='Header'; + const IT_FOOTER='Footer'; + const IT_ITEM='Item'; + const IT_SEPARATOR='Separator'; + const IT_ALTERNATINGITEM='AlternatingItem'; + + /** + * @var ITemplate template for repeater items + */ + private $_itemTemplate=null; + /** + * @var ITemplate template for each alternating item + */ + private $_alternatingItemTemplate=null; + /** + * @var ITemplate template for header + */ + private $_headerTemplate=null; + /** + * @var ITemplate template for footer + */ + private $_footerTemplate=null; + /** + * @var ITemplate template used for repeater when no data is bound + */ + private $_emptyTemplate=null; + /** + * @var ITemplate template for separator + */ + private $_separatorTemplate=null; + /** + * @var TRepeaterItemCollection list of repeater items + */ + private $_items=null; + /** + * @var TControl header item + */ + private $_header=null; + /** + * @var TControl footer item + */ + private $_footer=null; + + + /** + * @return string the class name for repeater items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item renderer class. + * + * If not empty, the class will be used to instantiate as repeater items. + * This property takes precedence over {@link getItemTemplate ItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setItemTemplate + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for alternative repeater items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getAlternatingItemRenderer() + { + return $this->getViewState('AlternatingItemRenderer',''); + } + + /** + * Sets the alternative item renderer class. + * + * If not empty, the class will be used to instantiate as alternative repeater items. + * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setAlternatingItemTemplate + * @since 3.1.0 + */ + public function setAlternatingItemRenderer($value) + { + $this->setViewState('AlternatingItemRenderer',$value,''); + } + + /** + * @return string the class name for repeater item separators. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSeparatorRenderer() + { + return $this->getViewState('SeparatorRenderer',''); + } + + /** + * Sets the repeater item separator renderer class. + * + * If not empty, the class will be used to instantiate as repeater item separators. + * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSeparatorTemplate + * @since 3.1.0 + */ + public function setSeparatorRenderer($value) + { + $this->setViewState('SeparatorRenderer',$value,''); + } + + /** + * @return string the class name for repeater header item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the repeater header renderer class. + * + * If not empty, the class will be used to instantiate as repeater header item. + * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setHeaderTemplate + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @return string the class name for repeater footer item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the repeater footer renderer class. + * + * If not empty, the class will be used to instantiate as repeater footer item. + * This property takes precedence over {@link getFooterTemplate FooterTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setFooterTemplate + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @return string the class name for empty repeater item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEmptyRenderer() + { + return $this->getViewState('EmptyRenderer',''); + } + + /** + * Sets the repeater empty renderer class. + * + * The empty renderer is created as the child of the repeater + * if data bound to the repeater is empty. + * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEmptyTemplate + * @since 3.1.0 + */ + public function setEmptyRenderer($value) + { + $this->setViewState('EmptyRenderer',$value,''); + } + + /** + * @return ITemplate the template for repeater items + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the template for repeater items + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','ItemTemplate'); + } + + /** + * @return ITemplate the alternative template string for the item + */ + public function getAlternatingItemTemplate() + { + return $this->_alternatingItemTemplate; + } + + /** + * @param ITemplate the alternative item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setAlternatingItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_alternatingItemTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','AlternatingItemTemplate'); + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','HeaderTemplate'); + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','FooterTemplate'); + } + + /** + * @return ITemplate the template applied when no data is bound to the repeater + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the repeater + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','EmptyTemplate'); + } + + /** + * @return ITemplate the separator template + */ + public function getSeparatorTemplate() + { + return $this->_separatorTemplate; + } + + /** + * @param ITemplate the separator template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSeparatorTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_separatorTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','SeparatorTemplate'); + } + + /** + * @return TControl the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TControl the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return TRepeaterItemCollection list of repeater item controls + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TRepeaterItemCollection; + return $this->_items; + } + + /** + * @return string the field of the data source that provides the keys of the list items. + */ + public function getDataKeyField() + { + return $this->getViewState('DataKeyField',''); + } + + /** + * @param string the field of the data source that provides the keys of the list items. + */ + public function setDataKeyField($value) + { + $this->setViewState('DataKeyField',$value,''); + } + + /** + * @return TList the keys used in the data listing control. + */ + public function getDataKeys() + { + if(($dataKeys=$this->getViewState('DataKeys',null))===null) + { + $dataKeys=new TList; + $this->setViewState('DataKeys',$dataKeys,null); + } + return $dataKeys; + } + + /** + * Creates a repeater item. + * This method invokes {@link createItem} to create a new repeater item. + * @param integer zero-based item index. + * @param TListItemType item type + * @return TControl the created item, null if item is not created + */ + private function createItemInternal($itemIndex,$itemType) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TRepeaterItemEventParameter($item); + $this->onItemCreated($param); + $this->getControls()->add($item); + return $item; + } + else + return null; + } + + /** + * Creates a repeater item and performs databinding. + * This method invokes {@link createItem} to create a new repeater item. + * @param integer zero-based item index. + * @param TListItemType item type + * @param mixed data to be associated with the item + * @return TControl the created item, null if item is not created + */ + private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TRepeaterItemEventParameter($item); + if($item instanceof IDataRenderer) + $item->setData($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + return $item; + } + else + return null; + } + + /** + * Creates a repeater item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TControl created repeater item + */ + protected function createItem($itemIndex,$itemType) + { + $template=null; + $classPath=null; + switch($itemType) + { + case TListItemType::Item : + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + break; + case TListItemType::AlternatingItem : + if(($classPath=$this->getAlternatingItemRenderer())==='' && ($template=$this->_alternatingItemTemplate)===null) + { + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + } + break; + case TListItemType::Header : + $classPath=$this->getHeaderRenderer(); + $template=$this->_headerTemplate; + break; + case TListItemType::Footer : + $classPath=$this->getFooterRenderer(); + $template=$this->_footerTemplate; + break; + case TListItemType::Separator : + $classPath=$this->getSeparatorRenderer(); + $template=$this->_separatorTemplate; + break; + default: + throw new TInvalidDataValueException('repeater_itemtype_unknown',$itemType); + } + if($classPath!=='') + { + $item=Prado::createComponent($classPath); + if($item instanceof IItemDataRenderer) + { + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + } + } + else if($template!==null) + { + $item=new TRepeaterItem; + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + $template->instantiateIn($item); + } + else + $item=null; + + return $item; + } + + /** + * Creates empty repeater content. + */ + protected function createEmptyContent() + { + if(($classPath=$this->getEmptyRenderer())!=='') + $this->getControls()->add(Prado::createComponent($classPath)); + else if($this->_emptyTemplate!==null) + $this->_emptyTemplate->instantiateIn($this); + } + + /** + * Renders the repeater. + * This method overrides the parent implementation by rendering the body + * content as the whole presentation of the repeater. Outer tag is not rendered. + * @param THtmlWriter writer + */ + public function render($writer) + { + if($this->_items && $this->_items->getCount() || $this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') + $this->renderContents($writer); + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getIsDataBound()) + $this->restoreItemsFromViewState(); + $this->clearViewState('ItemCount'); + } + + /** + * Clears up all items in the repeater. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + } + + /** + * Creates repeater items based on viewstate information. + */ + protected function restoreItemsFromViewState() + { + $this->reset(); + if(($itemCount=$this->getViewState('ItemCount',0))>0) + { + $items=$this->getItems(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $this->_header=$this->createItemInternal(-1,TListItemType::Header); + for($i=0;$i<$itemCount;++$i) + { + if($hasSeparator && $i>0) + $this->createItemInternal($i-1,TListItemType::Separator); + $itemType=$i%2==0?TListItemType::Item : TListItemType::AlternatingItem; + $items->add($this->createItemInternal($i,$itemType,false,null)); + } + $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); + } + else + $this->createEmptyContent(); + $this->clearChildState(); + } + + /** + * Performs databinding to populate repeater items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $this->reset(); + + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + + $items=$this->getItems(); + $itemIndex=0; + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + foreach($data as $key=>$dataItem) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($dataItem,$keyField)); + else + $keys->add($key); + if($itemIndex===0) + $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); + if($hasSeparator && $itemIndex>0) + $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); + $itemType=$itemIndex%2==0?TListItemType::Item : TListItemType::AlternatingItem; + $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); + $itemIndex++; + } + if($itemIndex>0) + $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); + else + { + $this->createEmptyContent(); + $this->dataBindChildren(); + } + $this->setViewState('ItemCount',$itemIndex,0); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * repeater items and their child controls. + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $this->onItemCommand($param); + return true; + } + else + return false; + } + + /** + * Raises OnItemCreated event. + * This method is invoked after a repeater item is created and instantiated with + * template, but before added to the page hierarchy. + * The repeater item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TRepeaterItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked right after an item is data bound. + * The repeater item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TRepeaterItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked after a button control in + * a template raises OnCommand event. + * The repeater control responsible for the event + * can be determined from the event parameter. + * The event parameter also contains the information about + * the initial sender of the OnCommand event, command name + * and command parameter. + * You may override this method to provide customized event handling. + * Be sure to call parent's implementation so that + * event handlers have chance to respond to the event. + * @param TRepeaterCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Returns the value of the data at the specified field. + * If data is an array, TMap or TList, the value will be returned at the index + * of the specified field. If the data is a component with a property named + * as the field name, the property value will be returned. + * Otherwise, an exception will be raised. + * @param mixed data item + * @param mixed field name + * @return mixed data value at the specified field + * @throws TInvalidDataValueException if the data is invalid + */ + protected function getDataFieldValue($data,$field) + { + return TDataFieldAccessor::getDataFieldValue($data,$field); + } +} + +/** + * TRepeaterItemEventParameter class + * + * TRepeaterItemEventParameter encapsulates the parameter data for + * {@link TRepeater::onItemCreated ItemCreated} event of {@link TRepeater} controls. + * The {@link getItem Item} property indicates the repeater item related with the event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItemEventParameter extends TEventParameter +{ + /** + * The repeater item control responsible for the event. + * @var TControl + */ + private $_item=null; + + /** + * Constructor. + * @param TControl repeater item related with the corresponding event + */ + public function __construct($item) + { + $this->_item=$item; + } + + /** + * @return TControl repeater item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TRepeaterCommandEventParameter class + * + * TRepeaterCommandEventParameter encapsulates the parameter data for + * {@link TRepeater::onItemCommand ItemCommand} event of {@link TRepeater} controls. + * + * The {@link getItem Item} property indicates the repeater item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TControl the repeater item control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the OnCommand event. + */ + private $_source=null; + + /** + * Constructor. + * @param TControl repeater item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TControl the repeater item control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TRepeaterItem class + * + * A TRepeaterItem control represents an item in the {@link TRepeater} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItem extends TControl implements INamingContainer, IItemDataRenderer +{ + /** + * index of the data item in the Items collection of repeater + */ + private $_itemIndex; + /** + * type of the TRepeaterItem + * @var TListItemType + */ + private $_itemType; + /** + * data associated with this item + * @var mixed + */ + private $_data; + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + + +/** + * TRepeaterItemCollection class. + * + * TRepeaterItemCollection represents a collection of repeater items. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only objects that are descendant of {@link TControl}. + * @param integer the speicified position. + * @param TControl new item + * @throws TInvalidDataTypeException if the item to be inserted is not a control. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('repeateritemcollection_item_invalid'); + } +} + diff --git a/framework/Web/UI/WebControls/TRepeaterItemRenderer.php b/framework/Web/UI/WebControls/TRepeaterItemRenderer.php index 85991b38..fe5171ad 100644 --- a/framework/Web/UI/WebControls/TRepeaterItemRenderer.php +++ b/framework/Web/UI/WebControls/TRepeaterItemRenderer.php @@ -1,50 +1,50 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TRepeater'); -Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); - -/** - * TRepeaterItemRenderer class - * - * TRepeaterItemRenderer can be used as a convenient base class to - * define an item renderer class specific for {@link TRepeater}. - * - * TRepeaterItemRenderer extends {@link TItemDataRenderer} and implements - * the bubbling scheme for the OnCommand event of repeater items. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.0 - */ -class TRepeaterItemRenderer extends TItemDataRenderer -{ - /** - * This method overrides parent's implementation by wrapping event parameter - * for OnCommand event with item information. - * @param TControl the sender of the event - * @param TEventParameter event parameter - * @return boolean whether the event bubbling should stop here. - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); - return true; - } - else - return false; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TRepeater'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TRepeaterItemRenderer class + * + * TRepeaterItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TRepeater}. + * + * TRepeaterItemRenderer extends {@link TItemDataRenderer} and implements + * the bubbling scheme for the OnCommand event of repeater items. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TRepeaterItemRenderer extends TItemDataRenderer +{ + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + diff --git a/framework/Web/UI/WebControls/TRequiredFieldValidator.php b/framework/Web/UI/WebControls/TRequiredFieldValidator.php index 97555b82..89b142bf 100644 --- a/framework/Web/UI/WebControls/TRequiredFieldValidator.php +++ b/framework/Web/UI/WebControls/TRequiredFieldValidator.php @@ -1,138 +1,138 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TBaseValidator class - */ -Prado::using('System.Web.UI.WebControls.TBaseValidator'); - -/** - * TRequiredFieldValidator class - * - * TRequiredFieldValidator makes the associated input control a required field. - * The input control fails validation if its value does not change from - * the {@link setInitialValue InitialValue} property upon losing focus. - * - * Validation will also succeed if input is of TListControl type and the number - * of selected values different from the initial value is greater than zero. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TRequiredFieldValidator extends TBaseValidator -{ - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TRequiredFieldValidator'; - } - - /** - * @return string the initial value of the associated input control. Defaults to empty string. - * If the associated input control does not change from this initial value - * upon postback, the validation fails. - */ - public function getInitialValue() - { - return $this->getViewState('InitialValue',''); - } - - /** - * @param string the initial value of the associated input control. - * If the associated input control does not change from this initial value - * upon postback, the validation fails. - */ - public function setInitialValue($value) - { - $this->setViewState('InitialValue',TPropertyValue::ensureString($value),''); - } - - /** - * This method overrides the parent's implementation. - * The validation succeeds if the input component changes its data - * from the {@link getInitialValue InitialValue} or the input control is not given. - * - * Validation will also succeed if input is of TListControl type and the - * number of selected values different from the initial value is greater - * than zero. - * - * @return boolean whether the validation succeeds - */ - protected function evaluateIsValid() - { - $control = $this->getValidationTarget(); - if($control instanceof TListControl) - return $this->validateListControl($control); - else if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) - return $this->validateRadioButtonGroup($control); - else - return $this->validateStandardControl($control); - } - - private function validateListControl($control) - { - $initial = trim($this->getInitialValue()); - $count = 0; - foreach($control->getItems() as $item) - { - if($item->getSelected() && $item->getValue() != $initial) - $count++; - } - return $count > 0; - } - - private function validateRadioButtonGroup($control) - { - $initial = trim($this->getInitialValue()); - foreach($control->getRadioButtonsInGroup() as $radio) - { - if($radio->getChecked()) - { - if(strlen($value = $radio->getValue()) > 0) - return $value !== $initial; - else - return true; - } - } - return false; - } - - private function validateStandardControl($control) - { - $initial = trim($this->getInitialValue()); - $value=$this->getValidationValue($control); - return (is_bool($value) && $value) || trim($value)!==$initial; - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $options = parent::getClientScriptOptions(); - $options['InitialValue']=$this->getInitialValue(); - $control = $this->getValidationTarget(); - if($control instanceof TListControl) - $options['TotalItems'] = $control->getItemCount(); - if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) - $options['GroupName'] = $control->getGroupName(); - return $options; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TRequiredFieldValidator class + * + * TRequiredFieldValidator makes the associated input control a required field. + * The input control fails validation if its value does not change from + * the {@link setInitialValue InitialValue} property upon losing focus. + * + * Validation will also succeed if input is of TListControl type and the number + * of selected values different from the initial value is greater than zero. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRequiredFieldValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TRequiredFieldValidator'; + } + + /** + * @return string the initial value of the associated input control. Defaults to empty string. + * If the associated input control does not change from this initial value + * upon postback, the validation fails. + */ + public function getInitialValue() + { + return $this->getViewState('InitialValue',''); + } + + /** + * @param string the initial value of the associated input control. + * If the associated input control does not change from this initial value + * upon postback, the validation fails. + */ + public function setInitialValue($value) + { + $this->setViewState('InitialValue',TPropertyValue::ensureString($value),''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input component changes its data + * from the {@link getInitialValue InitialValue} or the input control is not given. + * + * Validation will also succeed if input is of TListControl type and the + * number of selected values different from the initial value is greater + * than zero. + * + * @return boolean whether the validation succeeds + */ + protected function evaluateIsValid() + { + $control = $this->getValidationTarget(); + if($control instanceof TListControl) + return $this->validateListControl($control); + else if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) + return $this->validateRadioButtonGroup($control); + else + return $this->validateStandardControl($control); + } + + private function validateListControl($control) + { + $initial = trim($this->getInitialValue()); + $count = 0; + foreach($control->getItems() as $item) + { + if($item->getSelected() && $item->getValue() != $initial) + $count++; + } + return $count > 0; + } + + private function validateRadioButtonGroup($control) + { + $initial = trim($this->getInitialValue()); + foreach($control->getRadioButtonsInGroup() as $radio) + { + if($radio->getChecked()) + { + if(strlen($value = $radio->getValue()) > 0) + return $value !== $initial; + else + return true; + } + } + return false; + } + + private function validateStandardControl($control) + { + $initial = trim($this->getInitialValue()); + $value=$this->getValidationValue($control); + return (is_bool($value) && $value) || trim($value)!==$initial; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['InitialValue']=$this->getInitialValue(); + $control = $this->getValidationTarget(); + if($control instanceof TListControl) + $options['TotalItems'] = $control->getItemCount(); + if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) + $options['GroupName'] = $control->getGroupName(); + return $options; + } +} + diff --git a/framework/Web/UI/WebControls/TSafeHtml.php b/framework/Web/UI/WebControls/TSafeHtml.php index e88277d4..1cd0a597 100644 --- a/framework/Web/UI/WebControls/TSafeHtml.php +++ b/framework/Web/UI/WebControls/TSafeHtml.php @@ -1,85 +1,85 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TSafeHtml class - * - * TSafeHtml is a control that strips down all potentially dangerous - * HTML content. It is mainly a wrapper of {@link http://pear.php.net/package/SafeHTML SafeHTML} - * project. According to the SafeHTML project, it tries to safeguard - * the following situations when the string is to be displayed to end-users, - * - Opening tag without its closing tag - * - closing tag without its opening tag - * - any of these tags: base, basefont, head, html, body, applet, object, - * iframe, frame, frameset, script, layer, ilayer, embed, bgsound, link, - * meta, style, title, blink, xml, etc. - * - any of these attributes: on*, data*, dynsrc - * - javascript:/vbscript:/about: etc. protocols - * - expression/behavior etc. in styles - * - any other active content. - * - * To use TSafeHtml, simply enclose the content to be secured within - * the body of TSafeHtml in a template. - * - * If the content is encoded in UTF-7, you'll need to enable the {@link setRepackUTF7 RepackUTF7} property - * to ensure the contents gets parsed correctly. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TSafeHtml extends TControl -{ - /** - * Sets whether to parse the contents as UTF-7. This property enables a routine - * that repacks the content as UTF-7 before parsing it. Defaults to false. - * @param boolean whether to parse the contents as UTF-7 - */ - public function setRepackUTF7($value) - { - $this->setViewState('RepackUTF7',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether to parse the contents as UTF-7. Defaults to false. - */ - public function getRepackUTF7() - { - return $this->getViewState('RepackUTF7',false); - } - - /** - * Renders body content. - * This method overrides parent implementation by removing - * malicious javascript code from the body content - * @param THtmlWriter writer - */ - public function render($writer) - { - $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); - parent::render($htmlWriter); - $writer->write($this->parseSafeHtml($htmlWriter->flush())); - } - - /** - * Use SafeHTML to remove malicous javascript from the HTML content. - * @param string HTML content - * @return string safer HTML content - */ - protected function parseSafeHtml($text) - { - $renderer = Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); - return $renderer->parse($text, $this->getRepackUTF7()); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TSafeHtml class + * + * TSafeHtml is a control that strips down all potentially dangerous + * HTML content. It is mainly a wrapper of {@link http://pear.php.net/package/SafeHTML SafeHTML} + * project. According to the SafeHTML project, it tries to safeguard + * the following situations when the string is to be displayed to end-users, + * - Opening tag without its closing tag + * - closing tag without its opening tag + * - any of these tags: base, basefont, head, html, body, applet, object, + * iframe, frame, frameset, script, layer, ilayer, embed, bgsound, link, + * meta, style, title, blink, xml, etc. + * - any of these attributes: on*, data*, dynsrc + * - javascript:/vbscript:/about: etc. protocols + * - expression/behavior etc. in styles + * - any other active content. + * + * To use TSafeHtml, simply enclose the content to be secured within + * the body of TSafeHtml in a template. + * + * If the content is encoded in UTF-7, you'll need to enable the {@link setRepackUTF7 RepackUTF7} property + * to ensure the contents gets parsed correctly. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TSafeHtml extends TControl +{ + /** + * Sets whether to parse the contents as UTF-7. This property enables a routine + * that repacks the content as UTF-7 before parsing it. Defaults to false. + * @param boolean whether to parse the contents as UTF-7 + */ + public function setRepackUTF7($value) + { + $this->setViewState('RepackUTF7',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether to parse the contents as UTF-7. Defaults to false. + */ + public function getRepackUTF7() + { + return $this->getViewState('RepackUTF7',false); + } + + /** + * Renders body content. + * This method overrides parent implementation by removing + * malicious javascript code from the body content + * @param THtmlWriter writer + */ + public function render($writer) + { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + parent::render($htmlWriter); + $writer->write($this->parseSafeHtml($htmlWriter->flush())); + } + + /** + * Use SafeHTML to remove malicous javascript from the HTML content. + * @param string HTML content + * @return string safer HTML content + */ + protected function parseSafeHtml($text) + { + $renderer = Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); + return $renderer->parse($text, $this->getRepackUTF7()); + } +} + diff --git a/framework/Web/UI/WebControls/TSlider.php b/framework/Web/UI/WebControls/TSlider.php index ce080f5f..06df52b6 100644 --- a/framework/Web/UI/WebControls/TSlider.php +++ b/framework/Web/UI/WebControls/TSlider.php @@ -1,574 +1,574 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ - -/** - * TSlider class - * - * TSlider displays a slider for numeric input purpose. A slider consists of a 'track', - * which define the range of possible value, and a 'handle' which can slide on the track, to select - * a value in the range. The track can be either Horizontal or Vertical, depending of the {@link SetDirection Direction} - * property. By default, it's horizontal. - * - * The range boundaries are defined by {@link SetMinValue MinValue} and {@link SetMaxValue MaxValue} properties. - * The default range is from 0 to 100. - * The {@link SetStepSize StepSize} property can be used to define the step between 2 values inside the range. - * Notice that this step will be recomputed if there is more than 200 values between the range boundaries. - * You can also provide the allowed values by setting the {@link SetValues Values} array. - * - * A 'Progress Indicator' can be displayed within the track with the {@link SetProgressIndicator ProgressIndicator} property. - * - * The TSlider control can be easily customized using CssClasses. You can provide your own css file, using the - * {@link SetCssUrl CssUrl} property. - * The css class for TSlider can be set by the {@link setCssClass CssClass} property. Default value is "Slider HorizontalSlider" - * for an horizontal slider, and "Slider VerticalSlider" for a vertical one. - * - * If {@link SetAutoPostBack AutoPostBack} property is true, postback is sent as soon as the value changed. - * - * TSlider raises the {@link onValueChanged} event when the value of the slider has changed during postback. - * - * You can also attach ClientSide javascript events handler to the slider : - * - ClientSide.onSlide is called when the handle is slided on the track. You can get the current value in the value - * javascript variable. You can use this event to update on client side a label with the current value - * - ClientSide.onChange is called when the slider value has changed (at the end of a move). - * - * @author Christophe Boulain - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ -class TSlider extends TWebControl implements IPostBackDataHandler, IDataRenderer -{ - const MAX_STEPS=200; - /** - * @var TSliderHandle handle component - */ - private $_handle; - /* - * @var boolean Wether the data has changed during postback - */ - private $_dataChanged=false; - /** - * @var TSliderClientScript Clients side javascripts - */ - private $_clientScript=null; - - /** - * @return TSliderDirection Direction of slider (Horizontal or Vertical). Defaults to Horizontal. - */ - public function getDirection() - { - return $this->getViewState('Direction', TSliderDirection::Horizontal); - } - - /** - * @param TSliderDirection Direction of slider (Horizontal or Vertical) - */ - public function setDirection($value) - { - $this->setViewState('Direction', TPropertyValue::ensureEnum($value,'TSliderDirection'),TSliderDirection::Horizontal); - } - - /** - * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to '' (a default CSS file will be applied in this case.) - */ - public function getCssUrl() - { - return $this->getViewState('CssUrl',''); - } - - /** - * @param string URL for the CSS file including all relevant CSS class definitions. - */ - public function setCssUrl($value) - { - $this->setViewState('CssUrl',TPropertyValue::ensureString($value),''); - } - - /** - * @return float Maximum value for the slider. Defaults to 100.0. - */ - public function getMaxValue() - { - return $this->getViewState('MaxValue',100.0); - } - - /** - * @param float Maximum value for slider - */ - public function setMaxValue($value) - { - $this->setViewState('MaxValue', TPropertyValue::ensureFloat($value),100.0); - } - - /** - * @return float Minimum value for slider. Defaults to 0.0. - */ - public function getMinValue() - { - return $this->getViewState('MinValue',0.0); - } - - /** - * @param float Minimum value for slider - */ - public function setMinValue($value) - { - $this->setViewState('MinValue', TPropertyValue::ensureFloat($value),0.0); - } - - /** - * @return float Step size. Defaults to 1.0. - */ - public function getStepSize() - { - return $this->getViewState('StepSize', 1.0); - } - - /** - * Sets the step size used to determine the places where the slider handle can stop at. - * An evenly distributed stop marks will be generated according to - * {@link getMinValue MinValue}, {@link getMaxValue MaxValue} and StepSize. - * To use uneven stop marks, set {@link setValues Values}. - * @param float Step size. - */ - public function setStepSize($value) - { - $this->setViewState('StepSize', $value, 1.0); - } - - /** - * @return boolean wether to display a progress indicator or not. Defaults to true. - */ - public function getProgressIndicator () - { - return $this->getViewState('ProgressIndicator', true); - } - - /** - * @param boolean wether to display a progress indicator or not. Defaults to true. - */ - public function setProgressIndicator ($value) - { - $this->setViewState('ProgressIndicator', TPropertyValue::ensureBoolean($value), true); - } - /** - * @return float current value of slider - */ - public function getValue() - { - return $this->getViewState('Value',0.0); - } - - /** - * @param float current value of slider - */ - public function setValue($value) - { - $this->setViewState('Value', TPropertyValue::ensureFloat($value),0.0); - } - - /** - * Returns the value of the TSlider control. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getValue()}. - * @return string the value of the TSlider control. - * @see getValue - */ - public function getData() - { - return $this->getValue(); - } - - /** - * Sets the value of the TSlider control. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setValue()}. - * @param string the value of the TSlider control. - * @see setValue - */ - public function setData($value) - { - $this->setValue($value); - } - - /** - * @return array list of allowed values the slider can take. Defaults to an empty array. - */ - public function getValues() - { - return $this->getViewState('Values', array()); - } - - /** - * Sets the possible values that the slider can take. - * If this is set, {@link setStepSize StepSize} will be ignored. The latter - * generates a set of evenly distributed candidate values. - * @param array list of allowed values the slider can take - */ - public function setValues($value) - { - $this->setViewState('Values', TPropertyValue::ensureArray($value), array()); - } - - /** - * @return boolean a value indicating whether an automatic postback to the server - * will occur whenever the user modifies the slider value. Defaults to false. - */ - public function getAutoPostBack() - { - return $this->getViewState('AutoPostBack',false); - } - - /** - * Sets the value indicating if postback automatically. - * An automatic postback to the server will occur whenever the user - * modifies the slider value. - * @param boolean the value indicating if postback automatically - */ - public function setAutoPostBack($value) - { - $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); - } - - /** - * 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.TSlider'; - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * Raises postdata changed event. - * This method is required by {@link IPostBackDataHandler} interface. - * It is invoked by the framework when {@link getValue Value} property - * is changed on postback. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - $this->onValueChanged(null); - } - - /** - * Raises OnValueChanged event. - * This method is invoked when the {@link getValue Value} - * property changes on postback. - * If you override this method, be sure to call the parent implementation to ensure - * the invocation of the attached event handlers. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onValueChanged($param) - { - $this->raiseEvent('OnValueChanged',$this,$param); - } - - /** - * Loads user input data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - $value=(float)$values[$this->getClientID().'_1']; - if($this->getValue()!==$value) - { - $this->setValue($value); - return $this->_dataChanged=true; - } - else - return false; - } - - /** - * Gets the TSliderClientScript to set the TSlider event handlers. - * - * The slider on the client-side supports the following events. - * # OnSliderMove -- raised when the slider is moved. - * # OnSliderChanged -- raised when the slider value is changed - * - * You can attach custom javascript code to each of these events - * - * @return TSliderClientScript javascript validator event options. - */ - public function getClientSide() - { - if($this->_clientScript===null) - $this->_clientScript = $this->createClientScript(); - return $this->_clientScript; - } - - /** - * @return TSliderClientScript javascript event options. - */ - protected function createClientScript() - { - return new TSliderClientScript; - } - - /** - * @return string the HTML tag name for slider. Defaults to div. - */ - public function getTagName () - { - return "div"; - } - - /** - * Add the specified css classes to the track - * @param THtmlWriter writer - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - $writer->addAttribute('id',$this->getClientID()); - if ($this->getCssClass()==='') - { - $class=($this->getDirection()==TSliderDirection::Horizontal)?'HorizontalSlider':'VerticalSlider'; - $writer->addAttribute('class', 'Slider '.$class); - } - - } - - /** - * Render the body content - */ - public function renderContents($writer) - { - // Render the 'Track' - $writer->addAttribute('class', 'Track'); - $writer->addAttribute('id', $this->getClientID().'_track'); - $writer->renderBeginTag('div'); - // Render the 'Progress Indicator' - if ($this->getProgressIndicator()) - { - $writer->addAttribute('class', 'Progress'); - $writer->addAttribute('id', $this->getClientID().'_progress'); - $writer->renderBeginTag('div'); - $writer->renderEndTag(); - } - // Render the 'Ruler' - /* - * Removing for now - $writer->addAttribute('class', 'RuleContainer'); - $writer->addAttribute('id', $this->getClientID()."_rule"); - $writer->renderBeginTag('div'); - for ($i=0;$i<=100;$i+=10) - { - $writer->addAttribute('class', 'RuleMark'); - $attr=($this->getDirection()===TSliderDirection::Horizontal)?"left":"top"; - $writer->addStyleAttribute($attr, $i.'%'); - $writer->renderBeginTag('div'); - $writer->renderEndTag(); - } - $writer->renderEndTag(); - */ - - $writer->renderEndTag(); - - // Render the 'Handle' - $writer->addAttribute('class', 'Handle'); - $writer->addAttribute('id', $this->getClientID().'_handle'); - $writer->renderBeginTag('div'); - $writer->renderEndTag(); - } - /** - * Registers CSS and JS. - * This method is invoked right before the control rendering, if the control is visible. - * @param mixed event parameter - */ - public function onPreRender ($param) - { - parent::onPreRender($param); - $this->registerStyleSheet(); - $this->registerSliderClientScript(); - - } - - /** - * Registers the CSS relevant to the TSlider. - * It will register the CSS file specified by {@link getCssUrl CssUrl}. - * If that is not set, it will use the default CSS. - */ - protected function registerStyleSheet() - { - if(($url=$this->getCssUrl())==='') - { - $manager=$this->getApplication()->getAssetManager(); - // publish the assets - $url=$manager->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'TSlider'); - $url.='/TSlider.css'; - } - $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); - } - - /** - * Registers the javascript code to initialize the slider. - */ - protected function registerSliderClientScript() - { - $page=$this->getPage(); - $cs = $page->getClientScript(); - $cs->registerPradoScript("slider"); - $id=$this->getClientID(); - $cs->registerHiddenField($id.'_1',$this->getValue()); - $page->registerRequiresPostData($this); - $cs->registerPostBackControl($this->getClientClassName(),$this->getSliderOptions()); - } - - /** - * Get javascript sliderr options. - * @return array slider client-side options - */ - protected function getSliderOptions() - { - // PostBack Options : - $options['ID'] = $this->getClientID(); - $options['EventTarget'] = $this->getUniqueID(); - $options['AutoPostBack'] = $this->getAutoPostBack(); - - // Slider Control options - $minValue=$this->getMinValue(); - $maxValue=$this->getMaxValue(); - $options['axis'] = strtolower($this->getDirection()); - $options['maximum'] = $maxValue; - $options['minimum'] = $minValue; - $options['range'] = TJavascript::quoteJsLiteral('$R('.$minValue.",".$maxValue.")"); - $options['sliderValue'] = $this->getValue(); - $options['disabled'] = !$this->getEnabled(); - $values=$this->getValues(); - if (!empty($values)) - { - // Values are provided. Check if min/max are present in them - if (!in_array($minValue, $values)) $values[]=$minValue; - if (!in_array($maxValue, $values)) $values[]=$maxValue; - // Remove all values outsize the range [min..max] - foreach ($values as $idx=>$value) - { - if ($value < $minValue) unset ($values[$idx]); - if ($value > $maxValue) unset ($values[$idx]); - } - } - else - { - // Values are not provided, generate automatically using stepsize - $step=$this->getStepSize(); - // We want at most self::MAX_STEPS values, so, change the step if necessary - if (($maxValue-$minValue)/$step > self::MAX_STEPS) - { - $step=($maxValue-$minValue)/self::MAX_STEPS; - } - $values=array(); - for ($i=$minValue;$i<=$maxValue;$i+=$step) - $values[]=$i; - // Add max if it's not in the array because of step - if (!in_array($maxValue, $values)) $values[]=$maxValue; - } - $options['values'] = $values; - if($this->_clientScript!==null) - $options = array_merge($options,$this->_clientScript->getOptions()->toArray()); - return $options; - } -} - -/** - * TSliderClientScript class. - * - * Client-side slider events {@link setOnChange OnChange} and {@line setOnMove OnMove} - * can be modified through the {@link TSlider:: getClientSide ClientSide} - * property of a slider. - * - * The current value of the slider can be get in the 'value' js variable - * - * The OnMove event is raised when the slider moves - * The OnChange event is raised when the slider value is changed (or at the end of a move) - * - * @author Christophe Boulain - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ -class TSliderClientScript extends TClientSideOptions -{ - /** - * Javascript code to execute when the slider value is changed. - * @param string javascript code - */ - public function setOnChange($javascript) - { - $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); - $this->setFunction('onChange', $code); - } - - /** - * @return string javascript code to execute when the slider value is changed. - */ - public function getOnChange() - { - return $this->getOption('onChange'); - } - - /* Javascript code to execute when the slider moves. - * @param string javascript code - */ - public function setOnSlide($javascript) - { - $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); - $this->setFunction('onSlide', $code); - } - - /** - * @return string javascript code to execute when the slider moves. - */ - public function getOnSlide() - { - return $this->getOption('onSlide'); - } -} - - -/** - * TSliderDirection class. - * - * TSliderDirection defines the enumerable type for the possible direction that can be used in a {@link TSlider} - * - * The following enumerable values are defined : - * - Horizontal : Horizontal slider - * - Vertical : Vertical slider - * - * @author Christophe Boulain - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1.1 - */ -class TSliderDirection extends TEnumerable -{ - const Horizontal='Horizontal'; - const Vertical='Vertical'; -} - - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ + +/** + * TSlider class + * + * TSlider displays a slider for numeric input purpose. A slider consists of a 'track', + * which define the range of possible value, and a 'handle' which can slide on the track, to select + * a value in the range. The track can be either Horizontal or Vertical, depending of the {@link SetDirection Direction} + * property. By default, it's horizontal. + * + * The range boundaries are defined by {@link SetMinValue MinValue} and {@link SetMaxValue MaxValue} properties. + * The default range is from 0 to 100. + * The {@link SetStepSize StepSize} property can be used to define the step between 2 values inside the range. + * Notice that this step will be recomputed if there is more than 200 values between the range boundaries. + * You can also provide the allowed values by setting the {@link SetValues Values} array. + * + * A 'Progress Indicator' can be displayed within the track with the {@link SetProgressIndicator ProgressIndicator} property. + * + * The TSlider control can be easily customized using CssClasses. You can provide your own css file, using the + * {@link SetCssUrl CssUrl} property. + * The css class for TSlider can be set by the {@link setCssClass CssClass} property. Default value is "Slider HorizontalSlider" + * for an horizontal slider, and "Slider VerticalSlider" for a vertical one. + * + * If {@link SetAutoPostBack AutoPostBack} property is true, postback is sent as soon as the value changed. + * + * TSlider raises the {@link onValueChanged} event when the value of the slider has changed during postback. + * + * You can also attach ClientSide javascript events handler to the slider : + * - ClientSide.onSlide is called when the handle is slided on the track. You can get the current value in the value + * javascript variable. You can use this event to update on client side a label with the current value + * - ClientSide.onChange is called when the slider value has changed (at the end of a move). + * + * @author Christophe Boulain + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSlider extends TWebControl implements IPostBackDataHandler, IDataRenderer +{ + const MAX_STEPS=200; + /** + * @var TSliderHandle handle component + */ + private $_handle; + /* + * @var boolean Wether the data has changed during postback + */ + private $_dataChanged=false; + /** + * @var TSliderClientScript Clients side javascripts + */ + private $_clientScript=null; + + /** + * @return TSliderDirection Direction of slider (Horizontal or Vertical). Defaults to Horizontal. + */ + public function getDirection() + { + return $this->getViewState('Direction', TSliderDirection::Horizontal); + } + + /** + * @param TSliderDirection Direction of slider (Horizontal or Vertical) + */ + public function setDirection($value) + { + $this->setViewState('Direction', TPropertyValue::ensureEnum($value,'TSliderDirection'),TSliderDirection::Horizontal); + } + + /** + * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to '' (a default CSS file will be applied in this case.) + */ + public function getCssUrl() + { + return $this->getViewState('CssUrl',''); + } + + /** + * @param string URL for the CSS file including all relevant CSS class definitions. + */ + public function setCssUrl($value) + { + $this->setViewState('CssUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return float Maximum value for the slider. Defaults to 100.0. + */ + public function getMaxValue() + { + return $this->getViewState('MaxValue',100.0); + } + + /** + * @param float Maximum value for slider + */ + public function setMaxValue($value) + { + $this->setViewState('MaxValue', TPropertyValue::ensureFloat($value),100.0); + } + + /** + * @return float Minimum value for slider. Defaults to 0.0. + */ + public function getMinValue() + { + return $this->getViewState('MinValue',0.0); + } + + /** + * @param float Minimum value for slider + */ + public function setMinValue($value) + { + $this->setViewState('MinValue', TPropertyValue::ensureFloat($value),0.0); + } + + /** + * @return float Step size. Defaults to 1.0. + */ + public function getStepSize() + { + return $this->getViewState('StepSize', 1.0); + } + + /** + * Sets the step size used to determine the places where the slider handle can stop at. + * An evenly distributed stop marks will be generated according to + * {@link getMinValue MinValue}, {@link getMaxValue MaxValue} and StepSize. + * To use uneven stop marks, set {@link setValues Values}. + * @param float Step size. + */ + public function setStepSize($value) + { + $this->setViewState('StepSize', $value, 1.0); + } + + /** + * @return boolean wether to display a progress indicator or not. Defaults to true. + */ + public function getProgressIndicator () + { + return $this->getViewState('ProgressIndicator', true); + } + + /** + * @param boolean wether to display a progress indicator or not. Defaults to true. + */ + public function setProgressIndicator ($value) + { + $this->setViewState('ProgressIndicator', TPropertyValue::ensureBoolean($value), true); + } + /** + * @return float current value of slider + */ + public function getValue() + { + return $this->getViewState('Value',0.0); + } + + /** + * @param float current value of slider + */ + public function setValue($value) + { + $this->setViewState('Value', TPropertyValue::ensureFloat($value),0.0); + } + + /** + * Returns the value of the TSlider control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string the value of the TSlider control. + * @see getValue + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Sets the value of the TSlider control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string the value of the TSlider control. + * @see setValue + */ + public function setData($value) + { + $this->setValue($value); + } + + /** + * @return array list of allowed values the slider can take. Defaults to an empty array. + */ + public function getValues() + { + return $this->getViewState('Values', array()); + } + + /** + * Sets the possible values that the slider can take. + * If this is set, {@link setStepSize StepSize} will be ignored. The latter + * generates a set of evenly distributed candidate values. + * @param array list of allowed values the slider can take + */ + public function setValues($value) + { + $this->setViewState('Values', TPropertyValue::ensureArray($value), array()); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user modifies the slider value. Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * modifies the slider value. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * 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.TSlider'; + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getValue Value} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onValueChanged(null); + } + + /** + * Raises OnValueChanged event. + * This method is invoked when the {@link getValue Value} + * property changes on postback. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onValueChanged($param) + { + $this->raiseEvent('OnValueChanged',$this,$param); + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=(float)$values[$this->getClientID().'_1']; + if($this->getValue()!==$value) + { + $this->setValue($value); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Gets the TSliderClientScript to set the TSlider event handlers. + * + * The slider on the client-side supports the following events. + * # OnSliderMove -- raised when the slider is moved. + * # OnSliderChanged -- raised when the slider value is changed + * + * You can attach custom javascript code to each of these events + * + * @return TSliderClientScript javascript validator event options. + */ + public function getClientSide() + { + if($this->_clientScript===null) + $this->_clientScript = $this->createClientScript(); + return $this->_clientScript; + } + + /** + * @return TSliderClientScript javascript event options. + */ + protected function createClientScript() + { + return new TSliderClientScript; + } + + /** + * @return string the HTML tag name for slider. Defaults to div. + */ + public function getTagName () + { + return "div"; + } + + /** + * Add the specified css classes to the track + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + if ($this->getCssClass()==='') + { + $class=($this->getDirection()==TSliderDirection::Horizontal)?'HorizontalSlider':'VerticalSlider'; + $writer->addAttribute('class', 'Slider '.$class); + } + + } + + /** + * Render the body content + */ + public function renderContents($writer) + { + // Render the 'Track' + $writer->addAttribute('class', 'Track'); + $writer->addAttribute('id', $this->getClientID().'_track'); + $writer->renderBeginTag('div'); + // Render the 'Progress Indicator' + if ($this->getProgressIndicator()) + { + $writer->addAttribute('class', 'Progress'); + $writer->addAttribute('id', $this->getClientID().'_progress'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + // Render the 'Ruler' + /* + * Removing for now + $writer->addAttribute('class', 'RuleContainer'); + $writer->addAttribute('id', $this->getClientID()."_rule"); + $writer->renderBeginTag('div'); + for ($i=0;$i<=100;$i+=10) + { + $writer->addAttribute('class', 'RuleMark'); + $attr=($this->getDirection()===TSliderDirection::Horizontal)?"left":"top"; + $writer->addStyleAttribute($attr, $i.'%'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + $writer->renderEndTag(); + */ + + $writer->renderEndTag(); + + // Render the 'Handle' + $writer->addAttribute('class', 'Handle'); + $writer->addAttribute('id', $this->getClientID().'_handle'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender ($param) + { + parent::onPreRender($param); + $this->registerStyleSheet(); + $this->registerSliderClientScript(); + + } + + /** + * Registers the CSS relevant to the TSlider. + * It will register the CSS file specified by {@link getCssUrl CssUrl}. + * If that is not set, it will use the default CSS. + */ + protected function registerStyleSheet() + { + if(($url=$this->getCssUrl())==='') + { + $manager=$this->getApplication()->getAssetManager(); + // publish the assets + $url=$manager->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'TSlider'); + $url.='/TSlider.css'; + } + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + + /** + * Registers the javascript code to initialize the slider. + */ + protected function registerSliderClientScript() + { + $page=$this->getPage(); + $cs = $page->getClientScript(); + $cs->registerPradoScript("slider"); + $id=$this->getClientID(); + $cs->registerHiddenField($id.'_1',$this->getValue()); + $page->registerRequiresPostData($this); + $cs->registerPostBackControl($this->getClientClassName(),$this->getSliderOptions()); + } + + /** + * Get javascript sliderr options. + * @return array slider client-side options + */ + protected function getSliderOptions() + { + // PostBack Options : + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['AutoPostBack'] = $this->getAutoPostBack(); + + // Slider Control options + $minValue=$this->getMinValue(); + $maxValue=$this->getMaxValue(); + $options['axis'] = strtolower($this->getDirection()); + $options['maximum'] = $maxValue; + $options['minimum'] = $minValue; + $options['range'] = TJavascript::quoteJsLiteral('$R('.$minValue.",".$maxValue.")"); + $options['sliderValue'] = $this->getValue(); + $options['disabled'] = !$this->getEnabled(); + $values=$this->getValues(); + if (!empty($values)) + { + // Values are provided. Check if min/max are present in them + if (!in_array($minValue, $values)) $values[]=$minValue; + if (!in_array($maxValue, $values)) $values[]=$maxValue; + // Remove all values outsize the range [min..max] + foreach ($values as $idx=>$value) + { + if ($value < $minValue) unset ($values[$idx]); + if ($value > $maxValue) unset ($values[$idx]); + } + } + else + { + // Values are not provided, generate automatically using stepsize + $step=$this->getStepSize(); + // We want at most self::MAX_STEPS values, so, change the step if necessary + if (($maxValue-$minValue)/$step > self::MAX_STEPS) + { + $step=($maxValue-$minValue)/self::MAX_STEPS; + } + $values=array(); + for ($i=$minValue;$i<=$maxValue;$i+=$step) + $values[]=$i; + // Add max if it's not in the array because of step + if (!in_array($maxValue, $values)) $values[]=$maxValue; + } + $options['values'] = $values; + if($this->_clientScript!==null) + $options = array_merge($options,$this->_clientScript->getOptions()->toArray()); + return $options; + } +} + +/** + * TSliderClientScript class. + * + * Client-side slider events {@link setOnChange OnChange} and {@line setOnMove OnMove} + * can be modified through the {@link TSlider:: getClientSide ClientSide} + * property of a slider. + * + * The current value of the slider can be get in the 'value' js variable + * + * The OnMove event is raised when the slider moves + * The OnChange event is raised when the slider value is changed (or at the end of a move) + * + * @author Christophe Boulain + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSliderClientScript extends TClientSideOptions +{ + /** + * Javascript code to execute when the slider value is changed. + * @param string javascript code + */ + public function setOnChange($javascript) + { + $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); + $this->setFunction('onChange', $code); + } + + /** + * @return string javascript code to execute when the slider value is changed. + */ + public function getOnChange() + { + return $this->getOption('onChange'); + } + + /* Javascript code to execute when the slider moves. + * @param string javascript code + */ + public function setOnSlide($javascript) + { + $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); + $this->setFunction('onSlide', $code); + } + + /** + * @return string javascript code to execute when the slider moves. + */ + public function getOnSlide() + { + return $this->getOption('onSlide'); + } +} + + +/** + * TSliderDirection class. + * + * TSliderDirection defines the enumerable type for the possible direction that can be used in a {@link TSlider} + * + * The following enumerable values are defined : + * - Horizontal : Horizontal slider + * - Vertical : Vertical slider + * + * @author Christophe Boulain + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSliderDirection extends TEnumerable +{ + const Horizontal='Horizontal'; + const Vertical='Vertical'; +} + + diff --git a/framework/Web/UI/WebControls/TStatements.php b/framework/Web/UI/WebControls/TStatements.php index c3f43278..dd7631e2 100644 --- a/framework/Web/UI/WebControls/TStatements.php +++ b/framework/Web/UI/WebControls/TStatements.php @@ -1,63 +1,63 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TStatements class - * - * TStatements executes one or several PHP statements and renders the display - * generated during the execution. The execution happens during the rendering stage. - * The PHP statements being executed can be set via the property - * {@link setStatements Statements}. The context of the statemenets executed - * is the TStatements object itself. - * - * Note, since TStatements allows execution of arbitrary PHP statements, - * make sure {@link setStatements Statements} does not come directly from user input. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TStatements extends TControl -{ - /** - * @var string PHP statements - */ - private $_s=''; - - /** - * @return string the statements to be executed - */ - public function getStatements() - { - return $this->_s; - } - - /** - * @param string the PHP statements to be executed - */ - public function setStatements($value) - { - $this->_s=$value; - } - - /** - * Renders the evaluation result of the statements. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function render($writer) - { - if($this->_s!=='') - $writer->write($this->evaluateStatements($this->_s)); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TStatements class + * + * TStatements executes one or several PHP statements and renders the display + * generated during the execution. The execution happens during the rendering stage. + * The PHP statements being executed can be set via the property + * {@link setStatements Statements}. The context of the statemenets executed + * is the TStatements object itself. + * + * Note, since TStatements allows execution of arbitrary PHP statements, + * make sure {@link setStatements Statements} does not come directly from user input. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TStatements extends TControl +{ + /** + * @var string PHP statements + */ + private $_s=''; + + /** + * @return string the statements to be executed + */ + public function getStatements() + { + return $this->_s; + } + + /** + * @param string the PHP statements to be executed + */ + public function setStatements($value) + { + $this->_s=$value; + } + + /** + * Renders the evaluation result of the statements. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->_s!=='') + $writer->write($this->evaluateStatements($this->_s)); + } +} + diff --git a/framework/Web/UI/WebControls/TStyle.php b/framework/Web/UI/WebControls/TStyle.php index 58be223a..93f7d45d 100644 --- a/framework/Web/UI/WebControls/TStyle.php +++ b/framework/Web/UI/WebControls/TStyle.php @@ -1,893 +1,893 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TFont definition - */ -Prado::using('System.Web.UI.WebControls.TFont'); - -/** - * TStyle class - * - * TStyle encapsulates the CSS style applied to a control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TStyle extends TComponent -{ - /** - * @var array storage of CSS fields - */ - private $_fields=array(); - /** - * @var TFont font object - */ - private $_font=null; - /** - * @var string CSS class name - */ - private $_class=null; - /** - * @var string CSS style string (those not represented by specific fields of TStyle) - */ - private $_customStyle=null; - /** - * @var string display style - */ - private $_displayStyle='Fixed'; - - /** - * Constructor. - * @param TStyle style to copy from - */ - public function __construct($style=null) - { - if($style!==null) - $this->copyFrom($style); - } - - /** - * Need to clone the font object. - */ - public function __clone() - { - if($this->_font!==null) - $this->_font = clone($this->_font); - } - - /** - * @return string the background color of the control - */ - public function getBackColor() - { - return isset($this->_fields['background-color'])?$this->_fields['background-color']:''; - } - - /** - * @param string the background color of the control - */ - public function setBackColor($value) - { - if(trim($value)==='') - unset($this->_fields['background-color']); - else - $this->_fields['background-color']=$value; - } - - /** - * @return string the border color of the control - */ - public function getBorderColor() - { - return isset($this->_fields['border-color'])?$this->_fields['border-color']:''; - } - - /** - * @param string the border color of the control - */ - public function setBorderColor($value) - { - if(trim($value)==='') - unset($this->_fields['border-color']); - else - $this->_fields['border-color']=$value; - } - - /** - * @return string the border style of the control - */ - public function getBorderStyle() - { - return isset($this->_fields['border-style'])?$this->_fields['border-style']:''; - } - - /** - * Sets the border style of the control. - * @param string the border style of the control - */ - public function setBorderStyle($value) - { - if(trim($value)==='') - unset($this->_fields['border-style']); - else - $this->_fields['border-style']=$value; - } - - /** - * @return string the border width of the control - */ - public function getBorderWidth() - { - return isset($this->_fields['border-width'])?$this->_fields['border-width']:''; - } - - /** - * @param string the border width of the control - */ - public function setBorderWidth($value) - { - if(trim($value)==='') - unset($this->_fields['border-width']); - else - $this->_fields['border-width']=$value; - } - - /** - * @return string the CSS class of the control - */ - public function getCssClass() - { - return $this->_class===null?'':$this->_class; - } - - /** - * @return boolean true if CSS is set or empty. - */ - public function hasCssClass() - { - return ($this->_class!==null); - } - - /** - * @param string the name of the CSS class of the control - */ - public function setCssClass($value) - { - $this->_class=$value; - } - - /** - * @return TFont the font of the control - */ - public function getFont() - { - if($this->_font===null) - $this->_font=new TFont; - return $this->_font; - } - - /** - * @return boolean true if font is set. - */ - public function hasFont() - { - return $this->_font !== null; - } - - /** - * @param TDisplayStyle control display style, default is TDisplayStyle::Fixed - */ - public function setDisplayStyle($value) - { - $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle'); - switch($this->_displayStyle) - { - case TDisplayStyle::None: - $this->_fields['display'] = 'none'; - break; - case TDisplayStyle::Dynamic: - $this->_fields['display'] = ''; //remove the display property - break; - case TDisplayStyle::Fixed: - $this->_fields['visibility'] = 'visible'; - break; - case TDisplayStyle::Hidden: - $this->_fields['visibility'] = 'hidden'; - break; - } - } - - /** - * @return TDisplayStyle display style - */ - public function getDisplayStyle() - { - return $this->_displayStyle; - } - - /** - * @return string the foreground color of the control - */ - public function getForeColor() - { - return isset($this->_fields['color'])?$this->_fields['color']:''; - } - - /** - * @param string the foreground color of the control - */ - public function setForeColor($value) - { - if(trim($value)==='') - unset($this->_fields['color']); - else - $this->_fields['color']=$value; - } - - /** - * @return string the height of the control - */ - public function getHeight() - { - return isset($this->_fields['height'])?$this->_fields['height']:''; - } - - /** - * @param string the height of the control - */ - public function setHeight($value) - { - if(trim($value)==='') - unset($this->_fields['height']); - else - $this->_fields['height']=$value; - } - - /** - * @return string the custom style of the control - */ - public function getCustomStyle() - { - return $this->_customStyle===null?'':$this->_customStyle; - } - - /** - * Sets custom style fields from a string. - * Custom style fields will be overwritten by style fields explicitly defined. - * @param string the custom style of the control - */ - public function setCustomStyle($value) - { - $this->_customStyle=$value; - } - - /** - * @return string a single style field value set via {@link setStyleField}. Defaults to empty string. - */ - public function getStyleField($name) - { - return isset($this->_fields[$name])?$this->_fields[$name]:''; - } - - /** - * Sets a single style field value. - * Style fields set by this method will overwrite those set by {@link setCustomStyle}. - * @param string style field name - * @param string style field value - */ - public function setStyleField($name,$value) - { - $this->_fields[$name]=$value; - } - - /** - * Clears a single style field value; - * @param string style field name - */ - public function clearStyleField($name) - { - unset($this->_fields[$name]); - } - - /** - * @return boolean whether a style field has been defined by {@link setStyleField} - */ - public function hasStyleField($name) - { - return isset($this->_fields[$name]); - } - - /** - * @return string the width of the control - */ - public function getWidth() - { - return isset($this->_fields['width'])?$this->_fields['width']:''; - } - - /** - * @param string the width of the control - */ - public function setWidth($value) - { - $this->_fields['width']=$value; - } - - /** - * Resets the style to the original empty state. - */ - public function reset() - { - $this->_fields=array(); - $this->_font=null; - $this->_class=null; - $this->_customStyle=null; - } - - /** - * Copies the fields in a new style to this style. - * If a style field is set in the new style, the corresponding field - * in this style will be overwritten. - * @param TStyle the new style - */ - public function copyFrom($style) - { - if($style instanceof TStyle) - { - $this->_fields=array_merge($this->_fields,$style->_fields); - if($style->_class!==null) - $this->_class=$style->_class; - if($style->_customStyle!==null) - $this->_customStyle=$style->_customStyle; - if($style->_font!==null) - $this->getFont()->copyFrom($style->_font); - } - } - - /** - * Merges the style with a new one. - * If a style field is not set in this style, it will be overwritten by - * the new one. - * @param TStyle the new style - */ - public function mergeWith($style) - { - if($style instanceof TStyle) - { - $this->_fields=array_merge($style->_fields,$this->_fields); - if($this->_class===null) - $this->_class=$style->_class; - if($this->_customStyle===null) - $this->_customStyle=$style->_customStyle; - if($style->_font!==null) - $this->getFont()->mergeWith($style->_font); - } - } - - /** - * Adds attributes related to CSS styles to renderer. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function addAttributesToRender($writer) - { - if($this->_customStyle!==null) - { - foreach(explode(';',$this->_customStyle) as $style) - { - $arr=explode(':',$style,2); - if(isset($arr[1]) && trim($arr[0])!=='') - $writer->addStyleAttribute(trim($arr[0]),trim($arr[1])); - } - } - $writer->addStyleAttributes($this->_fields); - if($this->_font!==null) - $this->_font->addAttributesToRender($writer); - if($this->_class!==null) - $writer->addAttribute('class',$this->_class); - } - - /** - * @return array list of style fields. - */ - public function getStyleFields() - { - return $this->_fields; - } -} - -/** - * TDisplayStyle defines the enumerable type for the possible styles - * that a web control can display. - * - * The following enumerable values are defined: - * - None: the control is not displayed and not included in the layout. - * - Dynamic: the control is displayed and included in the layout, the layout flow is dependent on the control (equivalent to display:'' in css). - * - Fixed: Similar to Dynamic with CSS "visibility" set "shown". - * - Hidden: the control is not displayed and is included in the layout. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.1 - */ -class TDisplayStyle extends TEnumerable -{ - const None='None'; - const Dynamic='Dynamic'; - const Fixed='Fixed'; - const Hidden='Hidden'; -} - -/** - * TTableStyle class. - * TTableStyle represents the CSS style specific for HTML table. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableStyle extends TStyle -{ - /** - * @var TVerticalAlign the URL of the background image for the table - */ - private $_backImageUrl=null; - /** - * @var THorizontalAlign horizontal alignment of the contents within the table - */ - private $_horizontalAlign=null; - /** - * @var integer cellpadding of the table - */ - private $_cellPadding=null; - /** - * @var integer cellspacing of the table - */ - private $_cellSpacing=null; - /** - * @var TTableGridLines grid line setting of the table - */ - private $_gridLines=null; - /** - * @var boolean whether the table border should be collapsed - */ - private $_borderCollapse=null; - - /** - * Sets the style attributes to default values. - * This method overrides the parent implementation by - * resetting additional TTableStyle specific attributes. - */ - public function reset() - { - $this->_backImageUrl=null; - $this->_horizontalAlign=null; - $this->_cellPadding=null; - $this->_cellSpacing=null; - $this->_gridLines=null; - $this->_borderCollapse=null; - } - - /** - * Copies the fields in a new style to this style. - * If a style field is set in the new style, the corresponding field - * in this style will be overwritten. - * @param TStyle the new style - */ - public function copyFrom($style) - { - parent::copyFrom($style); - if($style instanceof TTableStyle) - { - if($style->_backImageUrl!==null) - $this->_backImageUrl=$style->_backImageUrl; - if($style->_horizontalAlign!==null) - $this->_horizontalAlign=$style->_horizontalAlign; - if($style->_cellPadding!==null) - $this->_cellPadding=$style->_cellPadding; - if($style->_cellSpacing!==null) - $this->_cellSpacing=$style->_cellSpacing; - if($style->_gridLines!==null) - $this->_gridLines=$style->_gridLines; - if($style->_borderCollapse!==null) - $this->_borderCollapse=$style->_borderCollapse; - } - } - - /** - * Merges the style with a new one. - * If a style field is not set in this style, it will be overwritten by - * the new one. - * @param TStyle the new style - */ - public function mergeWith($style) - { - parent::mergeWith($style); - if($style instanceof TTableStyle) - { - if($this->_backImageUrl===null && $style->_backImageUrl!==null) - $this->_backImageUrl=$style->_backImageUrl; - if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) - $this->_horizontalAlign=$style->_horizontalAlign; - if($this->_cellPadding===null && $style->_cellPadding!==null) - $this->_cellPadding=$style->_cellPadding; - if($this->_cellSpacing===null && $style->_cellSpacing!==null) - $this->_cellSpacing=$style->_cellSpacing; - if($this->_gridLines===null && $style->_gridLines!==null) - $this->_gridLines=$style->_gridLines; - if($this->_borderCollapse===null && $style->_borderCollapse!==null) - $this->_borderCollapse=$style->_borderCollapse; - } - } - - - /** - * Adds attributes related to CSS styles to renderer. - * This method overrides the parent implementation. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function addAttributesToRender($writer) - { - if(($url=trim($this->getBackImageUrl()))!=='') - $writer->addStyleAttribute('background-image','url('.$url.')'); - - if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) - $writer->addStyleAttribute('text-align',strtolower($horizontalAlign)); - - if(($cellPadding=$this->getCellPadding())>=0) - $writer->addAttribute('cellpadding',"$cellPadding"); - - if(($cellSpacing=$this->getCellSpacing())>=0) - $writer->addAttribute('cellspacing',"$cellSpacing"); - - if($this->getBorderCollapse()) - $writer->addStyleAttribute('border-collapse','collapse'); - - switch($this->getGridLines()) - { - case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break; - case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break; - case TTableGridLines::Both : $writer->addAttribute('rules','all'); break; - } - - parent::addAttributesToRender($writer); - } - - /** - * @return string the URL of the background image for the table - */ - public function getBackImageUrl() - { - return $this->_backImageUrl===null?'':$this->_backImageUrl; - } - - /** - * Sets the URL of the background image for the table - * @param string the URL - */ - public function setBackImageUrl($value) - { - $this->_backImageUrl=$value; - } - - /** - * @return THorizontalAlign the horizontal alignment of the contents within the table, defaults to THorizontalAlign::NotSet. - */ - public function getHorizontalAlign() - { - return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; - } - - /** - * Sets the horizontal alignment of the contents within the table. - * @param THorizontalAlign the horizontal alignment - */ - public function setHorizontalAlign($value) - { - $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); - } - - /** - * @return integer cellpadding of the table. Defaults to -1, meaning not set. - */ - public function getCellPadding() - { - return $this->_cellPadding===null?-1:$this->_cellPadding; - } - - /** - * @param integer cellpadding of the table. A value equal to -1 clears up the setting. - * @throws TInvalidDataValueException if the value is less than -1. - */ - public function setCellPadding($value) - { - if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1) - throw new TInvalidDataValueException('tablestyle_cellpadding_invalid'); - } - - /** - * @return integer cellspacing of the table. Defaults to -1, meaning not set. - */ - public function getCellSpacing() - { - return $this->_cellSpacing===null?-1:$this->_cellSpacing; - } - - /** - * @param integer cellspacing of the table. A value equal to -1 clears up the setting. - * @throws TInvalidDataValueException if the value is less than -1. - */ - public function setCellSpacing($value) - { - if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1) - throw new TInvalidDataValueException('tablestyle_cellspacing_invalid'); - } - - /** - * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. - */ - public function getGridLines() - { - return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines; - } - - /** - * Sets the grid line style of the table. - * @param TTableGridLines the grid line setting of the table - */ - public function setGridLines($value) - { - $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines'); - } - - - /** - * @return boolean whether the table borders should be collapsed. Defaults to false. - */ - public function getBorderCollapse() - { - return $this->_borderCollapse===null?false:$this->_borderCollapse; - } - - /** - * @param boolean whether the table borders should be collapsed. - */ - public function setBorderCollapse($value) - { - $this->_borderCollapse=TPropertyValue::ensureBoolean($value); - } -} - -/** - * TTableItemStyle class. - * TTableItemStyle represents the CSS style specific for HTML table item. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableItemStyle extends TStyle -{ - /** - * @var THorizontalAlign horizontal alignment of the contents within the table item - */ - private $_horizontalAlign=null; - /** - * @var TVerticalAlign vertical alignment of the contents within the table item - */ - private $_verticalAlign=null; - /** - * @var boolean whether the content wraps within the table item - */ - private $_wrap=null; - - /** - * Sets the style attributes to default values. - * This method overrides the parent implementation by - * resetting additional TTableItemStyle specific attributes. - */ - public function reset() - { - parent::reset(); - $this->_verticalAlign=null; - $this->_horizontalAlign=null; - $this->_wrap=null; - } - - /** - * Copies the fields in a new style to this style. - * If a style field is set in the new style, the corresponding field - * in this style will be overwritten. - * @param TStyle the new style - */ - public function copyFrom($style) - { - parent::copyFrom($style); - if($style instanceof TTableItemStyle) - { - if($this->_verticalAlign===null && $style->_verticalAlign!==null) - $this->_verticalAlign=$style->_verticalAlign; - if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) - $this->_horizontalAlign=$style->_horizontalAlign; - if($this->_wrap===null && $style->_wrap!==null) - $this->_wrap=$style->_wrap; - } - } - - /** - * Merges the style with a new one. - * If a style field is not set in this style, it will be overwritten by - * the new one. - * @param TStyle the new style - */ - public function mergeWith($style) - { - parent::mergeWith($style); - if($style instanceof TTableItemStyle) - { - if($style->_verticalAlign!==null) - $this->_verticalAlign=$style->_verticalAlign; - if($style->_horizontalAlign!==null) - $this->_horizontalAlign=$style->_horizontalAlign; - if($style->_wrap!==null) - $this->_wrap=$style->_wrap; - } - } - - /** - * Adds attributes related to CSS styles to renderer. - * This method overrides the parent implementation. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function addAttributesToRender($writer) - { - if(!$this->getWrap()) - $writer->addStyleAttribute('white-space','nowrap'); - - if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) - $writer->addAttribute('align',strtolower($horizontalAlign)); - - if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet) - $writer->addAttribute('valign',strtolower($verticalAlign)); - - parent::addAttributesToRender($writer); - } - - /** - * @return THorizontalAlign the horizontal alignment of the contents within the table item, defaults to THorizontalAlign::NotSet. - */ - public function getHorizontalAlign() - { - return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; - } - - /** - * Sets the horizontal alignment of the contents within the table item. - * @param THorizontalAlign the horizontal alignment - */ - public function setHorizontalAlign($value) - { - $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); - } - - /** - * @return TVerticalAlign the vertical alignment of the contents within the table item, defaults to TVerticalAlign::NotSet. - */ - public function getVerticalAlign() - { - return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign; - } - - /** - * Sets the vertical alignment of the contents within the table item. - * @param TVerticalAlign the horizontal alignment - */ - public function setVerticalAlign($value) - { - $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign'); - } - - /** - * @return boolean whether the content wraps within the table item. Defaults to true. - */ - public function getWrap() - { - return $this->_wrap===null?true:$this->_wrap; - } - - /** - * Sets the value indicating whether the content wraps within the table item. - * @param boolean whether the content wraps within the panel. - */ - public function setWrap($value) - { - $this->_wrap=TPropertyValue::ensureBoolean($value); - } -} - -/** - * THorizontalAlign class. - * THorizontalAlign defines the enumerable type for the possible horizontal alignments in a CSS style. - * - * The following enumerable values are defined: - * - NotSet: the alignment is not specified. - * - Left: left aligned - * - Right: right aligned - * - Center: center aligned - * - Justify: the begin and end are justified - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class THorizontalAlign extends TEnumerable -{ - const NotSet='NotSet'; - const Left='Left'; - const Right='Right'; - const Center='Center'; - const Justify='Justify'; -} - -/** - * TVerticalAlign class. - * TVerticalAlign defines the enumerable type for the possible vertical alignments in a CSS style. - * - * The following enumerable values are defined: - * - NotSet: the alignment is not specified. - * - Top: top aligned - * - Bottom: bottom aligned - * - Middle: middle aligned - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TVerticalAlign extends TEnumerable -{ - const NotSet='NotSet'; - const Top='Top'; - const Bottom='Bottom'; - const Middle='Middle'; -} - - -/** - * TTableGridLines class. - * TTableGridLines defines the enumerable type for the possible grid line types of an HTML table. - * - * The following enumerable values are defined: - * - None: no grid lines - * - Horizontal: horizontal grid lines only - * - Vertical: vertical grid lines only - * - Both: both horizontal and vertical grid lines are shown - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTableGridLines extends TEnumerable -{ - const None='None'; - const Horizontal='Horizontal'; - const Vertical='Vertical'; - const Both='Both'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TFont definition + */ +Prado::using('System.Web.UI.WebControls.TFont'); + +/** + * TStyle class + * + * TStyle encapsulates the CSS style applied to a control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TStyle extends TComponent +{ + /** + * @var array storage of CSS fields + */ + private $_fields=array(); + /** + * @var TFont font object + */ + private $_font=null; + /** + * @var string CSS class name + */ + private $_class=null; + /** + * @var string CSS style string (those not represented by specific fields of TStyle) + */ + private $_customStyle=null; + /** + * @var string display style + */ + private $_displayStyle='Fixed'; + + /** + * Constructor. + * @param TStyle style to copy from + */ + public function __construct($style=null) + { + if($style!==null) + $this->copyFrom($style); + } + + /** + * Need to clone the font object. + */ + public function __clone() + { + if($this->_font!==null) + $this->_font = clone($this->_font); + } + + /** + * @return string the background color of the control + */ + public function getBackColor() + { + return isset($this->_fields['background-color'])?$this->_fields['background-color']:''; + } + + /** + * @param string the background color of the control + */ + public function setBackColor($value) + { + if(trim($value)==='') + unset($this->_fields['background-color']); + else + $this->_fields['background-color']=$value; + } + + /** + * @return string the border color of the control + */ + public function getBorderColor() + { + return isset($this->_fields['border-color'])?$this->_fields['border-color']:''; + } + + /** + * @param string the border color of the control + */ + public function setBorderColor($value) + { + if(trim($value)==='') + unset($this->_fields['border-color']); + else + $this->_fields['border-color']=$value; + } + + /** + * @return string the border style of the control + */ + public function getBorderStyle() + { + return isset($this->_fields['border-style'])?$this->_fields['border-style']:''; + } + + /** + * Sets the border style of the control. + * @param string the border style of the control + */ + public function setBorderStyle($value) + { + if(trim($value)==='') + unset($this->_fields['border-style']); + else + $this->_fields['border-style']=$value; + } + + /** + * @return string the border width of the control + */ + public function getBorderWidth() + { + return isset($this->_fields['border-width'])?$this->_fields['border-width']:''; + } + + /** + * @param string the border width of the control + */ + public function setBorderWidth($value) + { + if(trim($value)==='') + unset($this->_fields['border-width']); + else + $this->_fields['border-width']=$value; + } + + /** + * @return string the CSS class of the control + */ + public function getCssClass() + { + return $this->_class===null?'':$this->_class; + } + + /** + * @return boolean true if CSS is set or empty. + */ + public function hasCssClass() + { + return ($this->_class!==null); + } + + /** + * @param string the name of the CSS class of the control + */ + public function setCssClass($value) + { + $this->_class=$value; + } + + /** + * @return TFont the font of the control + */ + public function getFont() + { + if($this->_font===null) + $this->_font=new TFont; + return $this->_font; + } + + /** + * @return boolean true if font is set. + */ + public function hasFont() + { + return $this->_font !== null; + } + + /** + * @param TDisplayStyle control display style, default is TDisplayStyle::Fixed + */ + public function setDisplayStyle($value) + { + $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle'); + switch($this->_displayStyle) + { + case TDisplayStyle::None: + $this->_fields['display'] = 'none'; + break; + case TDisplayStyle::Dynamic: + $this->_fields['display'] = ''; //remove the display property + break; + case TDisplayStyle::Fixed: + $this->_fields['visibility'] = 'visible'; + break; + case TDisplayStyle::Hidden: + $this->_fields['visibility'] = 'hidden'; + break; + } + } + + /** + * @return TDisplayStyle display style + */ + public function getDisplayStyle() + { + return $this->_displayStyle; + } + + /** + * @return string the foreground color of the control + */ + public function getForeColor() + { + return isset($this->_fields['color'])?$this->_fields['color']:''; + } + + /** + * @param string the foreground color of the control + */ + public function setForeColor($value) + { + if(trim($value)==='') + unset($this->_fields['color']); + else + $this->_fields['color']=$value; + } + + /** + * @return string the height of the control + */ + public function getHeight() + { + return isset($this->_fields['height'])?$this->_fields['height']:''; + } + + /** + * @param string the height of the control + */ + public function setHeight($value) + { + if(trim($value)==='') + unset($this->_fields['height']); + else + $this->_fields['height']=$value; + } + + /** + * @return string the custom style of the control + */ + public function getCustomStyle() + { + return $this->_customStyle===null?'':$this->_customStyle; + } + + /** + * Sets custom style fields from a string. + * Custom style fields will be overwritten by style fields explicitly defined. + * @param string the custom style of the control + */ + public function setCustomStyle($value) + { + $this->_customStyle=$value; + } + + /** + * @return string a single style field value set via {@link setStyleField}. Defaults to empty string. + */ + public function getStyleField($name) + { + return isset($this->_fields[$name])?$this->_fields[$name]:''; + } + + /** + * Sets a single style field value. + * Style fields set by this method will overwrite those set by {@link setCustomStyle}. + * @param string style field name + * @param string style field value + */ + public function setStyleField($name,$value) + { + $this->_fields[$name]=$value; + } + + /** + * Clears a single style field value; + * @param string style field name + */ + public function clearStyleField($name) + { + unset($this->_fields[$name]); + } + + /** + * @return boolean whether a style field has been defined by {@link setStyleField} + */ + public function hasStyleField($name) + { + return isset($this->_fields[$name]); + } + + /** + * @return string the width of the control + */ + public function getWidth() + { + return isset($this->_fields['width'])?$this->_fields['width']:''; + } + + /** + * @param string the width of the control + */ + public function setWidth($value) + { + $this->_fields['width']=$value; + } + + /** + * Resets the style to the original empty state. + */ + public function reset() + { + $this->_fields=array(); + $this->_font=null; + $this->_class=null; + $this->_customStyle=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($this->_fields,$style->_fields); + if($style->_class!==null) + $this->_class=$style->_class; + if($style->_customStyle!==null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->copyFrom($style->_font); + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($style->_fields,$this->_fields); + if($this->_class===null) + $this->_class=$style->_class; + if($this->_customStyle===null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->mergeWith($style->_font); + } + } + + /** + * Adds attributes related to CSS styles to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if($this->_customStyle!==null) + { + foreach(explode(';',$this->_customStyle) as $style) + { + $arr=explode(':',$style,2); + if(isset($arr[1]) && trim($arr[0])!=='') + $writer->addStyleAttribute(trim($arr[0]),trim($arr[1])); + } + } + $writer->addStyleAttributes($this->_fields); + if($this->_font!==null) + $this->_font->addAttributesToRender($writer); + if($this->_class!==null) + $writer->addAttribute('class',$this->_class); + } + + /** + * @return array list of style fields. + */ + public function getStyleFields() + { + return $this->_fields; + } +} + +/** + * TDisplayStyle defines the enumerable type for the possible styles + * that a web control can display. + * + * The following enumerable values are defined: + * - None: the control is not displayed and not included in the layout. + * - Dynamic: the control is displayed and included in the layout, the layout flow is dependent on the control (equivalent to display:'' in css). + * - Fixed: Similar to Dynamic with CSS "visibility" set "shown". + * - Hidden: the control is not displayed and is included in the layout. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.1 + */ +class TDisplayStyle extends TEnumerable +{ + const None='None'; + const Dynamic='Dynamic'; + const Fixed='Fixed'; + const Hidden='Hidden'; +} + +/** + * TTableStyle class. + * TTableStyle represents the CSS style specific for HTML table. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableStyle extends TStyle +{ + /** + * @var TVerticalAlign the URL of the background image for the table + */ + private $_backImageUrl=null; + /** + * @var THorizontalAlign horizontal alignment of the contents within the table + */ + private $_horizontalAlign=null; + /** + * @var integer cellpadding of the table + */ + private $_cellPadding=null; + /** + * @var integer cellspacing of the table + */ + private $_cellSpacing=null; + /** + * @var TTableGridLines grid line setting of the table + */ + private $_gridLines=null; + /** + * @var boolean whether the table border should be collapsed + */ + private $_borderCollapse=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TTableStyle specific attributes. + */ + public function reset() + { + $this->_backImageUrl=null; + $this->_horizontalAlign=null; + $this->_cellPadding=null; + $this->_cellSpacing=null; + $this->_gridLines=null; + $this->_borderCollapse=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableStyle) + { + if($style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableStyle) + { + if($this->_backImageUrl===null && $style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_cellPadding===null && $style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($this->_cellSpacing===null && $style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($this->_gridLines===null && $style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($this->_borderCollapse===null && $style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + + + /** + * Adds attributes related to CSS styles to renderer. + * This method overrides the parent implementation. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if(($url=trim($this->getBackImageUrl()))!=='') + $writer->addStyleAttribute('background-image','url('.$url.')'); + + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addStyleAttribute('text-align',strtolower($horizontalAlign)); + + if(($cellPadding=$this->getCellPadding())>=0) + $writer->addAttribute('cellpadding',"$cellPadding"); + + if(($cellSpacing=$this->getCellSpacing())>=0) + $writer->addAttribute('cellspacing',"$cellSpacing"); + + if($this->getBorderCollapse()) + $writer->addStyleAttribute('border-collapse','collapse'); + + switch($this->getGridLines()) + { + case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break; + case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break; + case TTableGridLines::Both : $writer->addAttribute('rules','all'); break; + } + + parent::addAttributesToRender($writer); + } + + /** + * @return string the URL of the background image for the table + */ + public function getBackImageUrl() + { + return $this->_backImageUrl===null?'':$this->_backImageUrl; + } + + /** + * Sets the URL of the background image for the table + * @param string the URL + */ + public function setBackImageUrl($value) + { + $this->_backImageUrl=$value; + } + + /** + * @return THorizontalAlign the horizontal alignment of the contents within the table, defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + + /** + * Sets the horizontal alignment of the contents within the table. + * @param THorizontalAlign the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + + /** + * @return integer cellpadding of the table. Defaults to -1, meaning not set. + */ + public function getCellPadding() + { + return $this->_cellPadding===null?-1:$this->_cellPadding; + } + + /** + * @param integer cellpadding of the table. A value equal to -1 clears up the setting. + * @throws TInvalidDataValueException if the value is less than -1. + */ + public function setCellPadding($value) + { + if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellpadding_invalid'); + } + + /** + * @return integer cellspacing of the table. Defaults to -1, meaning not set. + */ + public function getCellSpacing() + { + return $this->_cellSpacing===null?-1:$this->_cellSpacing; + } + + /** + * @param integer cellspacing of the table. A value equal to -1 clears up the setting. + * @throws TInvalidDataValueException if the value is less than -1. + */ + public function setCellSpacing($value) + { + if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellspacing_invalid'); + } + + /** + * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. + */ + public function getGridLines() + { + return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines; + } + + /** + * Sets the grid line style of the table. + * @param TTableGridLines the grid line setting of the table + */ + public function setGridLines($value) + { + $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines'); + } + + + /** + * @return boolean whether the table borders should be collapsed. Defaults to false. + */ + public function getBorderCollapse() + { + return $this->_borderCollapse===null?false:$this->_borderCollapse; + } + + /** + * @param boolean whether the table borders should be collapsed. + */ + public function setBorderCollapse($value) + { + $this->_borderCollapse=TPropertyValue::ensureBoolean($value); + } +} + +/** + * TTableItemStyle class. + * TTableItemStyle represents the CSS style specific for HTML table item. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableItemStyle extends TStyle +{ + /** + * @var THorizontalAlign horizontal alignment of the contents within the table item + */ + private $_horizontalAlign=null; + /** + * @var TVerticalAlign vertical alignment of the contents within the table item + */ + private $_verticalAlign=null; + /** + * @var boolean whether the content wraps within the table item + */ + private $_wrap=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TTableItemStyle specific attributes. + */ + public function reset() + { + parent::reset(); + $this->_verticalAlign=null; + $this->_horizontalAlign=null; + $this->_wrap=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableItemStyle) + { + if($this->_verticalAlign===null && $style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_wrap===null && $style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableItemStyle) + { + if($style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + + /** + * Adds attributes related to CSS styles to renderer. + * This method overrides the parent implementation. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if(!$this->getWrap()) + $writer->addStyleAttribute('white-space','nowrap'); + + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addAttribute('align',strtolower($horizontalAlign)); + + if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet) + $writer->addAttribute('valign',strtolower($verticalAlign)); + + parent::addAttributesToRender($writer); + } + + /** + * @return THorizontalAlign the horizontal alignment of the contents within the table item, defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * @param THorizontalAlign the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + + /** + * @return TVerticalAlign the vertical alignment of the contents within the table item, defaults to TVerticalAlign::NotSet. + */ + public function getVerticalAlign() + { + return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * @param TVerticalAlign the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign'); + } + + /** + * @return boolean whether the content wraps within the table item. Defaults to true. + */ + public function getWrap() + { + return $this->_wrap===null?true:$this->_wrap; + } + + /** + * Sets the value indicating whether the content wraps within the table item. + * @param boolean whether the content wraps within the panel. + */ + public function setWrap($value) + { + $this->_wrap=TPropertyValue::ensureBoolean($value); + } +} + +/** + * THorizontalAlign class. + * THorizontalAlign defines the enumerable type for the possible horizontal alignments in a CSS style. + * + * The following enumerable values are defined: + * - NotSet: the alignment is not specified. + * - Left: left aligned + * - Right: right aligned + * - Center: center aligned + * - Justify: the begin and end are justified + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class THorizontalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Left='Left'; + const Right='Right'; + const Center='Center'; + const Justify='Justify'; +} + +/** + * TVerticalAlign class. + * TVerticalAlign defines the enumerable type for the possible vertical alignments in a CSS style. + * + * The following enumerable values are defined: + * - NotSet: the alignment is not specified. + * - Top: top aligned + * - Bottom: bottom aligned + * - Middle: middle aligned + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TVerticalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Top='Top'; + const Bottom='Bottom'; + const Middle='Middle'; +} + + +/** + * TTableGridLines class. + * TTableGridLines defines the enumerable type for the possible grid line types of an HTML table. + * + * The following enumerable values are defined: + * - None: no grid lines + * - Horizontal: horizontal grid lines only + * - Vertical: vertical grid lines only + * - Both: both horizontal and vertical grid lines are shown + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableGridLines extends TEnumerable +{ + const None='None'; + const Horizontal='Horizontal'; + const Vertical='Vertical'; + const Both='Both'; +} + diff --git a/framework/Web/UI/WebControls/TTable.php b/framework/Web/UI/WebControls/TTable.php index a333bf09..b7a774ae 100644 --- a/framework/Web/UI/WebControls/TTable.php +++ b/framework/Web/UI/WebControls/TTable.php @@ -1,410 +1,410 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTableRow class - */ -Prado::using('System.Web.UI.WebControls.TTableRow'); - -/** - * TTable class - * - * TTable displays an HTML table on a Web page. - * - * A table may have {@link setCaption Caption}, whose alignment is specified - * via {@link setCaptionAlign CaptionAlign}. The table cellpadding and cellspacing - * are specified via {@link setCellPadding CellPadding} and {@link setCellSpacing CellSpacing} - * properties, respectively. The {@link setGridLines GridLines} specifies how - * the table should display its borders. The horizontal alignment of the table - * content can be specified via {@link setHorizontalAlign HorizontalAlign}, - * and {@link setBackImageUrl BackImageUrl} can assign a background image to the table. - * - * A TTable maintains a list of {@link TTableRow} controls in its - * {@link getRows Rows} property. Each {@link TTableRow} represents - * an HTML table row. - * - * To populate the table {@link getRows Rows}, you may either use control template - * or dynamically create {@link TTableRow} in code. - * In template, do as follows to create the table rows and cells, - * - * - * - * - * - * - * - * - * - * - * - * - * The above can also be accomplished in code as follows, - * - * $table=new TTable; - * $row=new TTableRow; - * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); - * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); - * $table->Rows->add($row); - * $row=new TTableRow; - * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); - * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); - * $table->Rows->add($row); - * - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTable extends TWebControl -{ - /** - * @return string tag name for the table - */ - protected function getTagName() - { - return 'table'; - } - - /** - * Adds object parsed from template to the control. - * This method adds only {@link TTableRow} objects into the {@link getRows Rows} collection. - * All other objects are ignored. - * @param mixed object parsed from template - */ - public function addParsedObject($object) - { - if($object instanceof TTableRow) - $this->getRows()->add($object); - } - - /** - * Creates a style object for the control. - * This method creates a {@link TTableStyle} to be used by the table. - * @return TTableStyle control style to be used - */ - protected function createStyle() - { - return new TTableStyle; - } - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - $border=0; - if($this->getHasStyle()) - { - if($this->getGridLines()!==TTableGridLines::None) - { - if(($border=$this->getBorderWidth())==='') - $border=1; - else - $border=(int)$border; - } - } - $writer->addAttribute('border',"$border"); - } - - /** - * Creates a control collection object that is to be used to hold child controls - * @return TTableRowCollection control collection - * @see getControls - */ - protected function createControlCollection() - { - return new TTableRowCollection($this); - } - - /** - * @return TTableRowCollection list of {@link TTableRow} controls - */ - public function getRows() - { - return $this->getControls(); - } - - /** - * @return string table caption - */ - public function getCaption() - { - return $this->getViewState('Caption',''); - } - - /** - * @param string table caption - */ - public function setCaption($value) - { - $this->setViewState('Caption',$value,''); - } - - /** - * @return TTableCaptionAlign table caption alignment. Defaults to TTableCaptionAlign::NotSet. - */ - public function getCaptionAlign() - { - return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); - } - - /** - * @param TTableCaptionAlign table caption alignment. - */ - public function setCaptionAlign($value) - { - $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); - } - - /** - * @return integer the cellspacing for the table. Defaults to -1, meaning not set. - */ - public function getCellSpacing() - { - if($this->getHasStyle()) - return $this->getStyle()->getCellSpacing(); - else - return -1; - } - - /** - * @param integer the cellspacing for the table. Defaults to -1, meaning not set. - */ - public function setCellSpacing($value) - { - $this->getStyle()->setCellSpacing($value); - } - - /** - * @return integer the cellpadding for the table. Defaults to -1, meaning not set. - */ - public function getCellPadding() - { - if($this->getHasStyle()) - return $this->getStyle()->getCellPadding(); - else - return -1; - } - - /** - * @param integer the cellpadding for the table. Defaults to -1, meaning not set. - */ - public function setCellPadding($value) - { - $this->getStyle()->setCellPadding($value); - } - - /** - * @return THorizontalAlign the horizontal alignment of the table content. Defaults to THorizontalAlign::NotSet. - */ - public function getHorizontalAlign() - { - if($this->getHasStyle()) - return $this->getStyle()->getHorizontalAlign(); - else - return THorizontalAlign::NotSet; - } - - /** - * @param THorizontalAlign the horizontal alignment of the table content. - */ - public function setHorizontalAlign($value) - { - $this->getStyle()->setHorizontalAlign($value); - } - - /** - * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. - */ - public function getGridLines() - { - if($this->getHasStyle()) - return $this->getStyle()->getGridLines(); - else - return TTableGridLines::None; - } - - /** - * @param TTableGridLines the grid line setting of the table - */ - public function setGridLines($value) - { - $this->getStyle()->setGridLines($value); - } - - /** - * @return string the URL of the background image for the table - */ - public function getBackImageUrl() - { - if($this->getHasStyle()) - return $this->getStyle()->getBackImageUrl(); - else - return ''; - } - - /** - * Sets the URL of the background image for the table - * @param string the URL - */ - public function setBackImageUrl($value) - { - $this->getStyle()->setBackImageUrl($value); - } - - /** - * Renders the openning tag for the table control which will render table caption if present. - * @param THtmlWriter the writer used for the rendering purpose - */ - public function renderBeginTag($writer) - { - parent::renderBeginTag($writer); - if(($caption=$this->getCaption())!=='') - { - if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) - $writer->addAttribute('align',strtolower($align)); - $writer->renderBeginTag('caption'); - $writer->write($caption); - $writer->renderEndTag(); - } - } - - /** - * Renders body contents of the table. - * @param THtmlWriter the writer used for the rendering purpose. - */ - public function renderContents($writer) - { - if($this->getHasControls()) - { - $renderTableSection=false; - foreach($this->getControls() as $row) - { - if($row->getTableSection()!==TTableRowSection::Body) - { - $renderTableSection=true; - break; - } - } - if($renderTableSection) - { - $currentSection=TTableRowSection::Header; - $writer->writeLine(); - foreach($this->getControls() as $index=>$row) - { - if(($section=$row->getTableSection())===$currentSection) - { - if($index===0 && $currentSection===TTableRowSection::Header) - $writer->renderBeginTag('thead'); - } - else - { - if($currentSection===TTableRowSection::Header) - { - if($index>0) - $writer->renderEndTag(); - if($section===TTableRowSection::Body) - $writer->renderBeginTag('tbody'); - else - $writer->renderBeginTag('tfoot'); - $currentSection=$section; - } - else if($currentSection===TTableRowSection::Body) - { - $writer->renderEndTag(); - if($section===TTableRowSection::Footer) - $writer->renderBeginTag('tfoot'); - else - throw new TConfigurationException('table_tablesection_outoforder'); - $currentSection=$section; - } - else // Footer - throw new TConfigurationException('table_tablesection_outoforder'); - } - $row->renderControl($writer); - $writer->writeLine(); - } - $writer->renderEndTag(); - } - else - { - $writer->writeLine(); - foreach($this->getControls() as $row) - { - $row->renderControl($writer); - $writer->writeLine(); - } - } - } - } -} - - -/** - * TTableRowCollection class. - * - * TTableRowCollection is used to maintain a list of rows belong to a table. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableRowCollection extends TControlCollection -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional - * operations for each newly added table row. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TTableRow object. - */ - public function insertAt($index,$item) - { - if($item instanceof TTableRow) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('tablerowcollection_tablerow_required'); - } -} - - -/** - * TTableCaptionAlign class. - * TTableCaptionAlign defines the enumerable type for the possible alignments - * that a table caption can take. - * - * The following enumerable values are defined: - * - NotSet: alignment not specified - * - Top: top aligned - * - Bottom: bottom aligned - * - Left: left aligned - * - Right: right aligned - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTableCaptionAlign extends TEnumerable -{ - const NotSet='NotSet'; - const Top='Top'; - const Bottom='Bottom'; - const Left='Left'; - const Right='Right'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTable class + * + * TTable displays an HTML table on a Web page. + * + * A table may have {@link setCaption Caption}, whose alignment is specified + * via {@link setCaptionAlign CaptionAlign}. The table cellpadding and cellspacing + * are specified via {@link setCellPadding CellPadding} and {@link setCellSpacing CellSpacing} + * properties, respectively. The {@link setGridLines GridLines} specifies how + * the table should display its borders. The horizontal alignment of the table + * content can be specified via {@link setHorizontalAlign HorizontalAlign}, + * and {@link setBackImageUrl BackImageUrl} can assign a background image to the table. + * + * A TTable maintains a list of {@link TTableRow} controls in its + * {@link getRows Rows} property. Each {@link TTableRow} represents + * an HTML table row. + * + * To populate the table {@link getRows Rows}, you may either use control template + * or dynamically create {@link TTableRow} in code. + * In template, do as follows to create the table rows and cells, + * + * + * + * + * + * + * + * + * + * + * + * + * The above can also be accomplished in code as follows, + * + * $table=new TTable; + * $row=new TTableRow; + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $table->Rows->add($row); + * $row=new TTableRow; + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $table->Rows->add($row); + * + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTable extends TWebControl +{ + /** + * @return string tag name for the table + */ + protected function getTagName() + { + return 'table'; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TTableRow} objects into the {@link getRows Rows} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + if($object instanceof TTableRow) + $this->getRows()->add($object); + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableStyle} to be used by the table. + * @return TTableStyle control style to be used + */ + protected function createStyle() + { + return new TTableStyle; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $border=0; + if($this->getHasStyle()) + { + if($this->getGridLines()!==TTableGridLines::None) + { + if(($border=$this->getBorderWidth())==='') + $border=1; + else + $border=(int)$border; + } + } + $writer->addAttribute('border',"$border"); + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TTableRowCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return new TTableRowCollection($this); + } + + /** + * @return TTableRowCollection list of {@link TTableRow} controls + */ + public function getRows() + { + return $this->getControls(); + } + + /** + * @return string table caption + */ + public function getCaption() + { + return $this->getViewState('Caption',''); + } + + /** + * @param string table caption + */ + public function setCaption($value) + { + $this->setViewState('Caption',$value,''); + } + + /** + * @return TTableCaptionAlign table caption alignment. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); + } + + /** + * @param TTableCaptionAlign table caption alignment. + */ + public function setCaptionAlign($value) + { + $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); + } + + /** + * @return integer the cellspacing for the table. Defaults to -1, meaning not set. + */ + public function getCellSpacing() + { + if($this->getHasStyle()) + return $this->getStyle()->getCellSpacing(); + else + return -1; + } + + /** + * @param integer the cellspacing for the table. Defaults to -1, meaning not set. + */ + public function setCellSpacing($value) + { + $this->getStyle()->setCellSpacing($value); + } + + /** + * @return integer the cellpadding for the table. Defaults to -1, meaning not set. + */ + public function getCellPadding() + { + if($this->getHasStyle()) + return $this->getStyle()->getCellPadding(); + else + return -1; + } + + /** + * @param integer the cellpadding for the table. Defaults to -1, meaning not set. + */ + public function setCellPadding($value) + { + $this->getStyle()->setCellPadding($value); + } + + /** + * @return THorizontalAlign the horizontal alignment of the table content. Defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return THorizontalAlign::NotSet; + } + + /** + * @param THorizontalAlign the horizontal alignment of the table content. + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. + */ + public function getGridLines() + { + if($this->getHasStyle()) + return $this->getStyle()->getGridLines(); + else + return TTableGridLines::None; + } + + /** + * @param TTableGridLines the grid line setting of the table + */ + public function setGridLines($value) + { + $this->getStyle()->setGridLines($value); + } + + /** + * @return string the URL of the background image for the table + */ + public function getBackImageUrl() + { + if($this->getHasStyle()) + return $this->getStyle()->getBackImageUrl(); + else + return ''; + } + + /** + * Sets the URL of the background image for the table + * @param string the URL + */ + public function setBackImageUrl($value) + { + $this->getStyle()->setBackImageUrl($value); + } + + /** + * Renders the openning tag for the table control which will render table caption if present. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if(($caption=$this->getCaption())!=='') + { + if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) + $writer->addAttribute('align',strtolower($align)); + $writer->renderBeginTag('caption'); + $writer->write($caption); + $writer->renderEndTag(); + } + } + + /** + * Renders body contents of the table. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + if($this->getHasControls()) + { + $renderTableSection=false; + foreach($this->getControls() as $row) + { + if($row->getTableSection()!==TTableRowSection::Body) + { + $renderTableSection=true; + break; + } + } + if($renderTableSection) + { + $currentSection=TTableRowSection::Header; + $writer->writeLine(); + foreach($this->getControls() as $index=>$row) + { + if(($section=$row->getTableSection())===$currentSection) + { + if($index===0 && $currentSection===TTableRowSection::Header) + $writer->renderBeginTag('thead'); + } + else + { + if($currentSection===TTableRowSection::Header) + { + if($index>0) + $writer->renderEndTag(); + if($section===TTableRowSection::Body) + $writer->renderBeginTag('tbody'); + else + $writer->renderBeginTag('tfoot'); + $currentSection=$section; + } + else if($currentSection===TTableRowSection::Body) + { + $writer->renderEndTag(); + if($section===TTableRowSection::Footer) + $writer->renderBeginTag('tfoot'); + else + throw new TConfigurationException('table_tablesection_outoforder'); + $currentSection=$section; + } + else // Footer + throw new TConfigurationException('table_tablesection_outoforder'); + } + $row->renderControl($writer); + $writer->writeLine(); + } + $writer->renderEndTag(); + } + else + { + $writer->writeLine(); + foreach($this->getControls() as $row) + { + $row->renderControl($writer); + $writer->writeLine(); + } + } + } + } +} + + +/** + * TTableRowCollection class. + * + * TTableRowCollection is used to maintain a list of rows belong to a table. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableRowCollection extends TControlCollection +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added table row. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TTableRow object. + */ + public function insertAt($index,$item) + { + if($item instanceof TTableRow) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('tablerowcollection_tablerow_required'); + } +} + + +/** + * TTableCaptionAlign class. + * TTableCaptionAlign defines the enumerable type for the possible alignments + * that a table caption can take. + * + * The following enumerable values are defined: + * - NotSet: alignment not specified + * - Top: top aligned + * - Bottom: bottom aligned + * - Left: left aligned + * - Right: right aligned + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableCaptionAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Top='Top'; + const Bottom='Bottom'; + const Left='Left'; + const Right='Right'; +} + diff --git a/framework/Web/UI/WebControls/TTableCell.php b/framework/Web/UI/WebControls/TTableCell.php index 6b43990b..70ff573f 100644 --- a/framework/Web/UI/WebControls/TTableCell.php +++ b/framework/Web/UI/WebControls/TTableCell.php @@ -1,222 +1,222 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TTableCell class. - * - * TTableCell displays a table cell on a Web page. Content of the table cell - * is specified by the {@link setText Text} property. If {@link setText Text} - * is empty, the body contents enclosed by the table cell component tag are rendered. - * Note, {@link setText Text} is not HTML-encoded when displayed. So make sure - * it does not contain dangerous characters. - * - * The horizontal and vertical alignments of the contents in the cell - * are specified via {@link setHorizontalAlign HorizontalAlign} and - * {@link setVerticalAlign VerticalAlign} properties, respectively. - * - * The colspan and rowspan of the cell are specified via {@link setColumnSpan ColumnSpan} - * and {@link setRowSpan RowSpan} properties. And the {@link setWrap Wrap} property - * indicates whether the contents in the cell should be wrapped. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableCell extends TWebControl implements IDataRenderer -{ - /** - * @return string tag name for the table cell - */ - protected function getTagName() - { - return 'td'; - } - - /** - * Creates a style object for the control. - * This method creates a {@link TTableItemStyle} to be used by the table cell. - * @return TStyle control style to be used - */ - protected function createStyle() - { - return new TTableItemStyle; - } - - /** - * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. - */ - public function getHorizontalAlign() - { - if($this->getHasStyle()) - return $this->getStyle()->getHorizontalAlign(); - else - return 'NotSet'; - } - - /** - * Sets the horizontal alignment of the contents within the table item. - * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' - * @param string the horizontal alignment - */ - public function setHorizontalAlign($value) - { - $this->getStyle()->setHorizontalAlign($value); - } - - /** - * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. - */ - public function getVerticalAlign() - { - if($this->getHasStyle()) - return $this->getStyle()->getVerticalAlign(); - else - return 'NotSet'; - } - - /** - * Sets the vertical alignment of the contents within the table item. - * Valid values include 'NotSet','Top','Bottom','Middle' - * @param string the horizontal alignment - */ - public function setVerticalAlign($value) - { - $this->getStyle()->setVerticalAlign($value); - } - - /** - * @return integer the columnspan for the table cell, 0 if not set. - */ - public function getColumnSpan() - { - return $this->getViewState('ColumnSpan', 0); - } - - /** - * Sets the columnspan for the table cell. - * @param integer the columnspan for the table cell, 0 if not set. - */ - public function setColumnSpan($value) - { - $this->setViewState('ColumnSpan', TPropertyValue::ensureInteger($value), 0); - } - - /** - * @return integer the rowspan for the table cell, 0 if not set. - */ - public function getRowSpan() - { - return $this->getViewState('RowSpan', 0); - } - - /** - * Sets the rowspan for the table cell. - * @param integer the rowspan for the table cell, 0 if not set. - */ - public function setRowSpan($value) - { - $this->setViewState('RowSpan', TPropertyValue::ensureInteger($value), 0); - } - - /** - * @return boolean whether the text content wraps within a table cell. Defaults to true. - */ - public function getWrap() - { - if($this->getHasStyle()) - return $this->getStyle()->getWrap(); - else - return true; - } - - /** - * Sets the value indicating whether the text content wraps within a table cell. - * @param boolean whether the text content wraps within a table cell. - */ - public function setWrap($value) - { - $this->getStyle()->setWrap($value); - } - - /** - * @return string the text content of the table cell. - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * Sets the text content of the table cell. - * If the text content is empty, body content (child controls) of the cell will be rendered. - * @param string the text content - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - } - - /** - * Returns the text content of the table cell. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string the text content of the table cell. - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the text content of the table cell. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string the text content of the table cell. - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - if(($colspan=$this->getColumnSpan())>0) - $writer->addAttribute('colspan',"$colspan"); - if(($rowspan=$this->getRowSpan())>0) - $writer->addAttribute('rowspan',"$rowspan"); - } - - /** - * Renders body contents of the table cell. - * @param THtmlWriter the writer used for the rendering purpose. - */ - public function renderContents($writer) - { - if(($text=$this->getText())!=='') - $writer->write($text); - else if($this->getHasControls()) - parent::renderContents($writer); - else - $writer->write(' '); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TTableCell class. + * + * TTableCell displays a table cell on a Web page. Content of the table cell + * is specified by the {@link setText Text} property. If {@link setText Text} + * is empty, the body contents enclosed by the table cell component tag are rendered. + * Note, {@link setText Text} is not HTML-encoded when displayed. So make sure + * it does not contain dangerous characters. + * + * The horizontal and vertical alignments of the contents in the cell + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * The colspan and rowspan of the cell are specified via {@link setColumnSpan ColumnSpan} + * and {@link setRowSpan RowSpan} properties. And the {@link setWrap Wrap} property + * indicates whether the contents in the cell should be wrapped. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableCell extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name for the table cell + */ + protected function getTagName() + { + return 'td'; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by the table cell. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' + * @param string the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getVerticalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getVerticalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * Valid values include 'NotSet','Top','Bottom','Middle' + * @param string the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->getStyle()->setVerticalAlign($value); + } + + /** + * @return integer the columnspan for the table cell, 0 if not set. + */ + public function getColumnSpan() + { + return $this->getViewState('ColumnSpan', 0); + } + + /** + * Sets the columnspan for the table cell. + * @param integer the columnspan for the table cell, 0 if not set. + */ + public function setColumnSpan($value) + { + $this->setViewState('ColumnSpan', TPropertyValue::ensureInteger($value), 0); + } + + /** + * @return integer the rowspan for the table cell, 0 if not set. + */ + public function getRowSpan() + { + return $this->getViewState('RowSpan', 0); + } + + /** + * Sets the rowspan for the table cell. + * @param integer the rowspan for the table cell, 0 if not set. + */ + public function setRowSpan($value) + { + $this->setViewState('RowSpan', TPropertyValue::ensureInteger($value), 0); + } + + /** + * @return boolean whether the text content wraps within a table cell. Defaults to true. + */ + public function getWrap() + { + if($this->getHasStyle()) + return $this->getStyle()->getWrap(); + else + return true; + } + + /** + * Sets the value indicating whether the text content wraps within a table cell. + * @param boolean whether the text content wraps within a table cell. + */ + public function setWrap($value) + { + $this->getStyle()->setWrap($value); + } + + /** + * @return string the text content of the table cell. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text content of the table cell. + * If the text content is empty, body content (child controls) of the cell will be rendered. + * @param string the text content + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the text content of the table cell. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text content of the table cell. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text content of the table cell. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text content of the table cell. + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if(($colspan=$this->getColumnSpan())>0) + $writer->addAttribute('colspan',"$colspan"); + if(($rowspan=$this->getRowSpan())>0) + $writer->addAttribute('rowspan',"$rowspan"); + } + + /** + * Renders body contents of the table cell. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + if(($text=$this->getText())!=='') + $writer->write($text); + else if($this->getHasControls()) + parent::renderContents($writer); + else + $writer->write(' '); + } +} + diff --git a/framework/Web/UI/WebControls/TTableFooterRow.php b/framework/Web/UI/WebControls/TTableFooterRow.php index 84bc29f1..1387c385 100644 --- a/framework/Web/UI/WebControls/TTableFooterRow.php +++ b/framework/Web/UI/WebControls/TTableFooterRow.php @@ -1,47 +1,47 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTableRow class. - */ -Prado::using('System.Web.UI.WebControls.TTableRow'); - -/** - * TTableFooterRow class. - * - * TTableFooterRow displays a table footer row. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.1 - */ -class TTableFooterRow extends TTableRow -{ - /** - * @return string location of a row in a table. Always returns 'Footer'. - */ - public function getTableSection() - { - return 'Footer'; - } - - /** - * @param string location of a row in a table. - * @throws TInvalidOperationException if this method is invoked - */ - public function setTableSection($value) - { - throw new TInvalidOperationException('tablefooterrow_tablesection_readonly'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class. + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTableFooterRow class. + * + * TTableFooterRow displays a table footer row. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.1 + */ +class TTableFooterRow extends TTableRow +{ + /** + * @return string location of a row in a table. Always returns 'Footer'. + */ + public function getTableSection() + { + return 'Footer'; + } + + /** + * @param string location of a row in a table. + * @throws TInvalidOperationException if this method is invoked + */ + public function setTableSection($value) + { + throw new TInvalidOperationException('tablefooterrow_tablesection_readonly'); + } +} + diff --git a/framework/Web/UI/WebControls/TTableHeaderCell.php b/framework/Web/UI/WebControls/TTableHeaderCell.php index bbf3e58a..72eae44e 100644 --- a/framework/Web/UI/WebControls/TTableHeaderCell.php +++ b/framework/Web/UI/WebControls/TTableHeaderCell.php @@ -1,124 +1,124 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTableCell class - */ -Prado::using('System.Web.UI.WebControls.TTableCell'); - - -/** - * TTableHeaderCell class. - * - * TTableHeaderCell displays a table header cell on a Web page. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableHeaderCell extends TTableCell -{ - /** - * @return string tag name for the table header cell - */ - protected function getTagName() - { - return 'th'; - } - - /** - * Adds attributes to renderer. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - parent::addAttributesToRender($writer); - if(($scope=$this->getScope())!==TTableHeaderScope::NotSet) - $writer->addAttribute('scope',$scope===TTableHeaderScope::Row?'row':'col'); - if(($text=$this->getAbbreviatedText())!=='') - $writer->addAttribute('abbr',$text); - if(($text=$this->getCategoryText())!=='') - $writer->addAttribute('axis',$text); - } - - /** - * @return TTableHeaderScope the scope of the cells that the header cell applies to. Defaults to TTableHeaderScope::NotSet. - */ - public function getScope() - { - return $this->getViewState('Scope',TTableHeaderScope::NotSet); - } - - /** - * @param TTableHeaderScope the scope of the cells that the header cell applies to. - */ - public function setScope($value) - { - $this->setViewState('Scope',TPropertyValue::ensureEnum($value,'TTableHeaderScope'),TTableHeaderScope::NotSet); - } - - /** - * @return string the abbr attribute of the HTML th element - */ - public function getAbbreviatedText() - { - return $this->getViewState('AbbreviatedText',''); - } - - /** - * @param string the abbr attribute of the HTML th element - */ - public function setAbbreviatedText($value) - { - $this->setViewState('AbbreviatedText',$value,''); - } - - /** - * @return string the axis attribute of the HTML th element - */ - public function getCategoryText() - { - return $this->getViewState('CategoryText',''); - } - - /** - * @param string the axis attribute of the HTML th element - */ - public function setCategoryText($value) - { - $this->setViewState('CategoryText',$value,''); - } -} - - -/** - * TTableHeaderScope class. - * TTableHeaderScope defines the enumerable type for the possible table scopes that a table header is associated with. - * - * The following enumerable values are defined: - * - NotSet: the scope is not specified - * - Row: the scope is row-wise - * - Column: the scope is column-wise - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTableHeaderScope extends TEnumerable -{ - const NotSet='NotSet'; - const Row='Row'; - const Column='Column'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableCell class + */ +Prado::using('System.Web.UI.WebControls.TTableCell'); + + +/** + * TTableHeaderCell class. + * + * TTableHeaderCell displays a table header cell on a Web page. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableHeaderCell extends TTableCell +{ + /** + * @return string tag name for the table header cell + */ + protected function getTagName() + { + return 'th'; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if(($scope=$this->getScope())!==TTableHeaderScope::NotSet) + $writer->addAttribute('scope',$scope===TTableHeaderScope::Row?'row':'col'); + if(($text=$this->getAbbreviatedText())!=='') + $writer->addAttribute('abbr',$text); + if(($text=$this->getCategoryText())!=='') + $writer->addAttribute('axis',$text); + } + + /** + * @return TTableHeaderScope the scope of the cells that the header cell applies to. Defaults to TTableHeaderScope::NotSet. + */ + public function getScope() + { + return $this->getViewState('Scope',TTableHeaderScope::NotSet); + } + + /** + * @param TTableHeaderScope the scope of the cells that the header cell applies to. + */ + public function setScope($value) + { + $this->setViewState('Scope',TPropertyValue::ensureEnum($value,'TTableHeaderScope'),TTableHeaderScope::NotSet); + } + + /** + * @return string the abbr attribute of the HTML th element + */ + public function getAbbreviatedText() + { + return $this->getViewState('AbbreviatedText',''); + } + + /** + * @param string the abbr attribute of the HTML th element + */ + public function setAbbreviatedText($value) + { + $this->setViewState('AbbreviatedText',$value,''); + } + + /** + * @return string the axis attribute of the HTML th element + */ + public function getCategoryText() + { + return $this->getViewState('CategoryText',''); + } + + /** + * @param string the axis attribute of the HTML th element + */ + public function setCategoryText($value) + { + $this->setViewState('CategoryText',$value,''); + } +} + + +/** + * TTableHeaderScope class. + * TTableHeaderScope defines the enumerable type for the possible table scopes that a table header is associated with. + * + * The following enumerable values are defined: + * - NotSet: the scope is not specified + * - Row: the scope is row-wise + * - Column: the scope is column-wise + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableHeaderScope extends TEnumerable +{ + const NotSet='NotSet'; + const Row='Row'; + const Column='Column'; +} + diff --git a/framework/Web/UI/WebControls/TTableHeaderRow.php b/framework/Web/UI/WebControls/TTableHeaderRow.php index 448902dc..2c05b94d 100644 --- a/framework/Web/UI/WebControls/TTableHeaderRow.php +++ b/framework/Web/UI/WebControls/TTableHeaderRow.php @@ -1,47 +1,47 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTableRow class. - */ -Prado::using('System.Web.UI.WebControls.TTableRow'); - -/** - * TTableHeaderRow class. - * - * TTableHeaderRow displays a table header row. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.1 - */ -class TTableHeaderRow extends TTableRow -{ - /** - * @return string location of a row in a table. Always returns 'Header'. - */ - public function getTableSection() - { - return 'Header'; - } - - /** - * @param string location of a row in a table. - * @throws TInvalidOperationException if this method is invoked - */ - public function setTableSection($value) - { - throw new TInvalidOperationException('tableheaderrow_tablesection_readonly'); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class. + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTableHeaderRow class. + * + * TTableHeaderRow displays a table header row. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.1 + */ +class TTableHeaderRow extends TTableRow +{ + /** + * @return string location of a row in a table. Always returns 'Header'. + */ + public function getTableSection() + { + return 'Header'; + } + + /** + * @param string location of a row in a table. + * @throws TInvalidOperationException if this method is invoked + */ + public function setTableSection($value) + { + throw new TInvalidOperationException('tableheaderrow_tablesection_readonly'); + } +} + diff --git a/framework/Web/UI/WebControls/TTableRow.php b/framework/Web/UI/WebControls/TTableRow.php index e50099bf..3cfc82d0 100644 --- a/framework/Web/UI/WebControls/TTableRow.php +++ b/framework/Web/UI/WebControls/TTableRow.php @@ -1,208 +1,208 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TTableCell class - */ -Prado::using('System.Web.UI.WebControls.TTableCell'); - -/** - * TTableRow class. - * - * TTableRow displays a table row. The table cells in the row can be accessed - * via {@link getCells Cells}. The horizontal and vertical alignments of the row - * are specified via {@link setHorizontalAlign HorizontalAlign} and - * {@link setVerticalAlign VerticalAlign} properties, respectively. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableRow extends TWebControl -{ - /** - * @return string tag name for the table - */ - protected function getTagName() - { - return 'tr'; - } - - /** - * Adds object parsed from template to the control. - * This method adds only {@link TTableCell} objects into the {@link getCells Cells} collection. - * All other objects are ignored. - * @param mixed object parsed from template - */ - public function addParsedObject($object) - { - if($object instanceof TTableCell) - $this->getCells()->add($object); - } - - /** - * Creates a style object for the control. - * This method creates a {@link TTableItemStyle} to be used by the table row. - * @return TStyle control style to be used - */ - protected function createStyle() - { - return new TTableItemStyle; - } - - /** - * Creates a control collection object that is to be used to hold child controls - * @return TTableCellCollection control collection - * @see getControls - */ - protected function createControlCollection() - { - return new TTableCellCollection($this); - } - - /** - * @return TTableCellCollection list of {@link TTableCell} controls - */ - public function getCells() - { - return $this->getControls(); - } - - /** - * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. - */ - public function getHorizontalAlign() - { - if($this->getHasStyle()) - return $this->getStyle()->getHorizontalAlign(); - else - return 'NotSet'; - } - - /** - * Sets the horizontal alignment of the contents within the table item. - * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' - * @param string the horizontal alignment - */ - public function setHorizontalAlign($value) - { - $this->getStyle()->setHorizontalAlign($value); - } - - /** - * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. - */ - public function getVerticalAlign() - { - if($this->getHasStyle()) - return $this->getStyle()->getVerticalAlign(); - else - return 'NotSet'; - } - - /** - * Sets the vertical alignment of the contents within the table item. - * Valid values include 'NotSet','Top','Bottom','Middle' - * @param string the horizontal alignment - */ - public function setVerticalAlign($value) - { - $this->getStyle()->setVerticalAlign($value); - } - - /** - * @return TTableRowSection location of a row in a table. Defaults to TTableRowSection::Body. - */ - public function getTableSection() - { - return $this->getViewState('TableSection',TTableRowSection::Body); - } - - /** - * @param TTableRowSection location of a row in a table. - */ - public function setTableSection($value) - { - $this->setViewState('TableSection',TPropertyValue::ensureEnum($value,'TTableRowSection'),TTableRowSection::Body); - } - - /** - * Renders body contents of the table row - * @param THtmlWriter writer for the rendering purpose - */ - public function renderContents($writer) - { - if($this->getHasControls()) - { - $writer->writeLine(); - foreach($this->getControls() as $cell) - { - $cell->renderControl($writer); - $writer->writeLine(); - } - } - } -} - -/** - * TTableCellCollection class. - * - * TTableCellCollection is used to maintain a list of cells belong to a table row. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTableCellCollection extends TControlCollection -{ - /** - * Inserts an item at the specified position. - * This overrides the parent implementation by performing additional - * operations for each newly added table cell. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item to be inserted is not a TTableCell object. - */ - public function insertAt($index,$item) - { - if($item instanceof TTableCell) - parent::insertAt($index,$item); - else - throw new TInvalidDataTypeException('tablecellcollection_tablecell_required'); - } -} - - -/** - * TTableRowSection class. - * TTableRowSection defines the enumerable type for the possible table sections - * that a {@link TTableRow} can be within. - * - * The following enumerable values are defined: - * - Header: in table header - * - Body: in table body - * - Footer: in table footer - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTableRowSection extends TEnumerable -{ - const Header='Header'; - const Body='Body'; - const Footer='Footer'; -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableCell class + */ +Prado::using('System.Web.UI.WebControls.TTableCell'); + +/** + * TTableRow class. + * + * TTableRow displays a table row. The table cells in the row can be accessed + * via {@link getCells Cells}. The horizontal and vertical alignments of the row + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableRow extends TWebControl +{ + /** + * @return string tag name for the table + */ + protected function getTagName() + { + return 'tr'; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TTableCell} objects into the {@link getCells Cells} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + if($object instanceof TTableCell) + $this->getCells()->add($object); + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by the table row. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TTableCellCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return new TTableCellCollection($this); + } + + /** + * @return TTableCellCollection list of {@link TTableCell} controls + */ + public function getCells() + { + return $this->getControls(); + } + + /** + * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' + * @param string the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getVerticalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getVerticalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * Valid values include 'NotSet','Top','Bottom','Middle' + * @param string the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->getStyle()->setVerticalAlign($value); + } + + /** + * @return TTableRowSection location of a row in a table. Defaults to TTableRowSection::Body. + */ + public function getTableSection() + { + return $this->getViewState('TableSection',TTableRowSection::Body); + } + + /** + * @param TTableRowSection location of a row in a table. + */ + public function setTableSection($value) + { + $this->setViewState('TableSection',TPropertyValue::ensureEnum($value,'TTableRowSection'),TTableRowSection::Body); + } + + /** + * Renders body contents of the table row + * @param THtmlWriter writer for the rendering purpose + */ + public function renderContents($writer) + { + if($this->getHasControls()) + { + $writer->writeLine(); + foreach($this->getControls() as $cell) + { + $cell->renderControl($writer); + $writer->writeLine(); + } + } + } +} + +/** + * TTableCellCollection class. + * + * TTableCellCollection is used to maintain a list of cells belong to a table row. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableCellCollection extends TControlCollection +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added table cell. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TTableCell object. + */ + public function insertAt($index,$item) + { + if($item instanceof TTableCell) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('tablecellcollection_tablecell_required'); + } +} + + +/** + * TTableRowSection class. + * TTableRowSection defines the enumerable type for the possible table sections + * that a {@link TTableRow} can be within. + * + * The following enumerable values are defined: + * - Header: in table header + * - Body: in table body + * - Footer: in table footer + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableRowSection extends TEnumerable +{ + const Header='Header'; + const Body='Body'; + const Footer='Footer'; +} + diff --git a/framework/Web/UI/WebControls/TTemplateColumn.php b/framework/Web/UI/WebControls/TTemplateColumn.php index f2bf02d7..34c9bbaf 100644 --- a/framework/Web/UI/WebControls/TTemplateColumn.php +++ b/framework/Web/UI/WebControls/TTemplateColumn.php @@ -1,256 +1,256 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TDataGridColumn class file - */ -Prado::using('System.Web.UI.WebControls.TDataGridColumn'); - -/** - * TTemplateColumn class - * - * TTemplateColumn customizes the layout of controls in the column with templates. - * In particular, you can specify {@link setItemTemplate ItemTemplate}, - * {@link setEditItemTemplate EditItemTemplate}, {@link setHeaderTemplate HeaderTemplate} - * and {@link setFooterTemplate FooterTemplate} to customize specific - * type of cells in the column. - * - * Since v3.1.0, TTemplateColumn has introduced two new properties {@link setItemRenderer ItemRenderer} - * and {@link setEditItemRenderer EditItemRenderer} which can be used to specify - * the layout of the datagrid cells in browsing and editing mode. - * A renderer refers to a control class that is to be instantiated as a control. - * For more details, see {@link TRepeater} and {@link TDataList}. - * - * When a renderer and a template are both defined for a type of item, the former - * takes precedence. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTemplateColumn extends TDataGridColumn -{ - /** - * Various item templates - * @var string - */ - private $_itemTemplate=null; - private $_editItemTemplate=null; - private $_headerTemplate=null; - private $_footerTemplate=null; - - /** - * @return string the class name for the item cell renderer. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getItemRenderer() - { - return $this->getViewState('ItemRenderer',''); - } - - /** - * Sets the item cell renderer class. - * - * If not empty, the class will be used to instantiate as a child control in the item cells of the column. - * - * If the class implements {@link IDataRenderer}, the Data property - * will be set as the row of the data associated with the datagrid item that this cell resides in. - * - * @param string the renderer class name in namespace format. - * @since 3.1.0 - */ - public function setItemRenderer($value) - { - $this->setViewState('ItemRenderer',$value,''); - } - - /** - * @return string the class name for the edit item cell renderer. Defaults to empty, meaning not set. - * @since 3.1.0 - */ - public function getEditItemRenderer() - { - return $this->getViewState('EditItemRenderer',''); - } - - /** - * Sets the edit item cell renderer class. - * - * If not empty, the class will be used to instantiate as a child control in the item cell that is in edit mode. - * - * If the class implements {@link IDataRenderer}, the Data property - * will be set as the row of the data associated with the datagrid item that this cell resides in. - * - * @param string the renderer class name in namespace format. - * @since 3.1.0 - */ - public function setEditItemRenderer($value) - { - $this->setViewState('EditItemRenderer',$value,''); - } - - /** - * @return ITemplate the edit item template - */ - public function getEditItemTemplate() - { - return $this->_editItemTemplate; - } - - /** - * @param ITemplate the edit item template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setEditItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_editItemTemplate=$value; - else - throw new TInvalidDataTypeException('templatecolumn_template_required','EditItemTemplate'); - } - - /** - * @return ITemplate the item template - */ - public function getItemTemplate() - { - return $this->_itemTemplate; - } - - /** - * @param ITemplate the item template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setItemTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_itemTemplate=$value; - else - throw new TInvalidDataTypeException('templatecolumn_template_required','ItemTemplate'); - } - - /** - * @return ITemplate the header template - */ - public function getHeaderTemplate() - { - return $this->_headerTemplate; - } - - /** - * @param ITemplate the header template. - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setHeaderTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_headerTemplate=$value; - else - throw new TInvalidDataTypeException('templatecolumn_template_required','HeaderTemplate'); - } - - /** - * @return ITemplate the footer template - */ - public function getFooterTemplate() - { - return $this->_footerTemplate; - } - - /** - * @param ITemplate the footer template - * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. - */ - public function setFooterTemplate($value) - { - if($value instanceof ITemplate || $value===null) - $this->_footerTemplate=$value; - else - throw new TInvalidDataTypeException('templatecolumn_template_required','FooterTemplate'); - } - - /** - * Initializes the specified cell to its initial values. - * This method overrides the parent implementation. - * It initializes the cell based on different templates - * (ItemTemplate, EditItemTemplate, HeaderTemplate, FooterTemplate). - * @param TTableCell the cell to be initialized. - * @param integer the index to the Columns property that the cell resides in. - * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) - */ - public function initializeCell($cell,$columnIndex,$itemType) - { - if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) - { - if($itemType===TListItemType::EditItem) - { - if(($classPath=$this->getEditItemRenderer())==='' && ($template=$this->_editItemTemplate)===null) - { - $classPath=$this->getItemRenderer(); - $template=$this->_itemTemplate; - } - } - else - { - $template=$this->_itemTemplate; - $classPath=$this->getItemRenderer(); - } - if($classPath!=='') - { - $control=Prado::createComponent($classPath); - $cell->getControls()->add($control); - if($control instanceof IItemDataRenderer) - { - $control->setItemIndex($cell->getParent()->getItemIndex()); - $control->setItemType($itemType); - } - if($control instanceof IDataRenderer) - $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); - } - else if($template!==null) - $template->instantiateIn($cell); - else if($itemType!==TListItemType::EditItem) - $cell->setText(' '); - } - else if($itemType===TListItemType::Header) - { - if(($classPath=$this->getHeaderRenderer())!=='') - $this->initializeHeaderCell($cell,$columnIndex); - else if($this->_headerTemplate!==null) - $this->_headerTemplate->instantiateIn($cell); - else - $this->initializeHeaderCell($cell,$columnIndex); - } - else if($itemType===TListItemType::Footer) - { - if(($classPath=$this->getFooterRenderer())!=='') - $this->initializeFooterCell($cell,$columnIndex); - else if($this->_footerTemplate!==null) - $this->_footerTemplate->instantiateIn($cell); - else - $this->initializeFooterCell($cell,$columnIndex); - } - } - - /** - * Databinds a cell in the column. - * This method is invoked when datagrid performs databinding. - * It populates the content of the cell with the relevant data from data source. - */ - public function dataBindColumn($sender,$param) - { - $item=$sender->getNamingContainer(); - $sender->setData($item->getData()); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TTemplateColumn class + * + * TTemplateColumn customizes the layout of controls in the column with templates. + * In particular, you can specify {@link setItemTemplate ItemTemplate}, + * {@link setEditItemTemplate EditItemTemplate}, {@link setHeaderTemplate HeaderTemplate} + * and {@link setFooterTemplate FooterTemplate} to customize specific + * type of cells in the column. + * + * Since v3.1.0, TTemplateColumn has introduced two new properties {@link setItemRenderer ItemRenderer} + * and {@link setEditItemRenderer EditItemRenderer} which can be used to specify + * the layout of the datagrid cells in browsing and editing mode. + * A renderer refers to a control class that is to be instantiated as a control. + * For more details, see {@link TRepeater} and {@link TDataList}. + * + * When a renderer and a template are both defined for a type of item, the former + * takes precedence. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTemplateColumn extends TDataGridColumn +{ + /** + * Various item templates + * @var string + */ + private $_itemTemplate=null; + private $_editItemTemplate=null; + private $_headerTemplate=null; + private $_footerTemplate=null; + + /** + * @return string the class name for the item cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the item cells of the column. + * + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the row of the data associated with the datagrid item that this cell resides in. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for the edit item cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEditItemRenderer() + { + return $this->getViewState('EditItemRenderer',''); + } + + /** + * Sets the edit item cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the item cell that is in edit mode. + * + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the row of the data associated with the datagrid item that this cell resides in. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setEditItemRenderer($value) + { + $this->setViewState('EditItemRenderer',$value,''); + } + + /** + * @return ITemplate the edit item template + */ + public function getEditItemTemplate() + { + return $this->_editItemTemplate; + } + + /** + * @param ITemplate the edit item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEditItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_editItemTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','EditItemTemplate'); + } + + /** + * @return ITemplate the item template + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','ItemTemplate'); + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template. + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','HeaderTemplate'); + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','FooterTemplate'); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It initializes the cell based on different templates + * (ItemTemplate, EditItemTemplate, HeaderTemplate, FooterTemplate). + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) + { + if($itemType===TListItemType::EditItem) + { + if(($classPath=$this->getEditItemRenderer())==='' && ($template=$this->_editItemTemplate)===null) + { + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + } + } + else + { + $template=$this->_itemTemplate; + $classPath=$this->getItemRenderer(); + } + if($classPath!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IItemDataRenderer) + { + $control->setItemIndex($cell->getParent()->getItemIndex()); + $control->setItemType($itemType); + } + if($control instanceof IDataRenderer) + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + } + else if($template!==null) + $template->instantiateIn($cell); + else if($itemType!==TListItemType::EditItem) + $cell->setText(' '); + } + else if($itemType===TListItemType::Header) + { + if(($classPath=$this->getHeaderRenderer())!=='') + $this->initializeHeaderCell($cell,$columnIndex); + else if($this->_headerTemplate!==null) + $this->_headerTemplate->instantiateIn($cell); + else + $this->initializeHeaderCell($cell,$columnIndex); + } + else if($itemType===TListItemType::Footer) + { + if(($classPath=$this->getFooterRenderer())!=='') + $this->initializeFooterCell($cell,$columnIndex); + else if($this->_footerTemplate!==null) + $this->_footerTemplate->instantiateIn($cell); + else + $this->initializeFooterCell($cell,$columnIndex); + } + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $sender->setData($item->getData()); + } +} + diff --git a/framework/Web/UI/WebControls/TTextBox.php b/framework/Web/UI/WebControls/TTextBox.php index 7a9f0445..5cd1149a 100644 --- a/framework/Web/UI/WebControls/TTextBox.php +++ b/framework/Web/UI/WebControls/TTextBox.php @@ -1,652 +1,652 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TTextBox class - * - * TTextBox displays a text box on the Web page for user input. - * The text displayed in the TTextBox control is determined by the - * {@link setText Text} property. You can create a SingleLine, - * a MultiLine, or a Password text box by setting - * the {@link setTextMode TextMode} property. If the TTextBox control - * is a multiline text box, the number of rows it displays is determined - * by the {@link setRows Rows} property, and the {@link setWrap Wrap} property - * can be used to determine whether to wrap the text in the component. - * - * To specify the display width of the text box, in characters, set - * the {@link setColumns Columns} property. To prevent the text displayed - * in the component from being modified, set the {@link setReadOnly ReadOnly} - * property to true. If you want to limit the user input to a specified number - * of characters, set the {@link setMaxLength MaxLength} property. - * To use AutoComplete feature, set the {@link setAutoCompleteType AutoCompleteType} property. - * - * If {@link setAutoPostBack AutoPostBack} is set true, updating the text box - * and then changing the focus out of it will cause postback action. - * And if {@link setCausesValidation CausesValidation} is true, validation will - * also be processed, which can be further restricted within - * a {@link setValidationGroup ValidationGroup}. - * - * WARNING: Be careful if you want to display the text collected via TTextBox. - * Malicious cross-site script may be injected in. You may use {@link getSafeText SafeText} - * to prevent this problem. - * - * NOTE: If you set {@link setWrap Wrap} to false or use {@link setAutoCompleteType AutoCompleteType}, - * the generated HTML output for the textbox will not be XHTML-compatible. - * Currently, no alternatives are available. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable, IDataRenderer -{ - /** - * Default number of rows (for MultiLine text box) - */ - const DEFAULT_ROWS=4; - /** - * Default number of columns (for MultiLine text box) - */ - const DEFAULT_COLUMNS=20; - /** - * @var mixed safe text parser - */ - private static $_safeTextParser=null; - /** - * @var string safe textbox content with javascript stripped off - */ - private $_safeText; - private $_dataChanged=false; - private $_isValid=true; - - /** - * @return string tag name of the textbox - */ - protected function getTagName() - { - return ($this->getTextMode()==='MultiLine')?'textarea':'input'; - } - - /** - * @return boolean whether to render javascript. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether to render javascript. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * Adds attribute name-value pairs to renderer. - * This method overrides the parent implementation with additional textbox specific attributes. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function addAttributesToRender($writer) - { - $page=$this->getPage(); - $page->ensureRenderInForm($this); - if(($uid=$this->getUniqueID())!=='') - $writer->addAttribute('name',$uid); - if(($textMode=$this->getTextMode())===TTextBoxMode::MultiLine) - { - if(($rows=$this->getRows())<=0) - $rows=self::DEFAULT_ROWS; - if(($cols=$this->getColumns())<=0) - $cols=self::DEFAULT_COLUMNS; - $writer->addAttribute('rows',"$rows"); - $writer->addAttribute('cols',"$cols"); - if(!$this->getWrap()) - $writer->addAttribute('wrap','off'); - } - else - { - if($textMode===TTextBoxMode::SingleLine) - { - $writer->addAttribute('type','text'); - if(($text=$this->getText())!=='') - $writer->addAttribute('value',$text); - } - else - { - if($this->getPersistPassword() && ($text=$this->getText())!=='') - $writer->addAttribute('value',$text); - $writer->addAttribute('type','password'); - } - - if(($act=$this->getAutoCompleteType())!=='None') - { - if($act==='Disabled') - $writer->addAttribute('autocomplete','off'); - else if($act==='Search') - $writer->addAttribute('vcard_name','search'); - else if($act==='HomeCountryRegion') - $writer->addAttribute('vcard_name','HomeCountry'); - else if($act==='BusinessCountryRegion') - $writer->addAttribute('vcard_name','BusinessCountry'); - else - { - if(strpos($act,'Business')===0) - $act='Business'.'.'.substr($act,8); - else if(strpos($act,'Home')===0) - $act='Home'.'.'.substr($act,4); - $writer->addAttribute('vcard_name','vCard.'.$act); - } - } - - if(($cols=$this->getColumns())>0) - $writer->addAttribute('size',"$cols"); - if(($maxLength=$this->getMaxLength())>0) - $writer->addAttribute('maxlength',"$maxLength"); - } - if($this->getReadOnly()) - $writer->addAttribute('readonly','readonly'); - $isEnabled=$this->getEnabled(true); - if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' - $writer->addAttribute('disabled','disabled'); - if($isEnabled - && $this->getEnableClientScript() - && ( $this->getAutoPostBack() || $textMode===TTextBoxMode::SingleLine) - && $page->getClientSupportsJavaScript()) - { - $this->renderClientControlScript($writer); - } - parent::addAttributesToRender($writer); - } - - /** - * Renders the javascript for textbox. - */ - protected function renderClientControlScript($writer) - { - $writer->addAttribute('id',$this->getClientID()); - $cs = $this->getPage()->getClientScript(); - $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); - } - - /** - * 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.TTextBox'; - } - - /** - * Gets the post back options for this textbox. - * @return array - */ - protected function getPostBackOptions() - { - $options['ID'] = $this->getClientID(); - $options['EventTarget'] = $this->getUniqueID(); - $options['AutoPostBack'] = $this->getAutoPostBack(); - $options['CausesValidation'] = $this->getCausesValidation(); - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['TextMode'] = $this->getTextMode(); - return $options; - } - - /** - * Loads user input data. - * This method is primarly used by framework developers. - * @param string the key that can be used to retrieve data from the input data collection - * @param array the input data collection - * @return boolean whether the data of the component has been changed - */ - public function loadPostData($key,$values) - { - $value=$values[$key]; - if($this->getAutoTrim()) - $value=trim($value); - if(!$this->getReadOnly() && $this->getText()!==$value) - { - $this->setText($value); - return $this->_dataChanged=true; - } - else - return false; - } - - /** - * Returns a value indicating whether postback has caused the control data change. - * This method is required by the IPostBackDataHandler interface. - * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. - */ - public function getDataChanged() - { - return $this->_dataChanged; - } - - /** - * Returns the value to be validated. - * This methid is required by IValidatable interface. - * @return mixed the value of the property to be validated. - */ - public function getValidationPropertyValue() - { - return $this->getText(); - } - - /** - * Returns true if this control validated successfully. - * Defaults to true. - * @return bool wether this control validated successfully. - */ - public function getIsValid() - { - return $this->_isValid; - } - /** - * @param bool wether this control is valid. - */ - public function setIsValid($value) - { - $this->_isValid=TPropertyValue::ensureBoolean($value); - } - - /** - * Raises OnTextChanged event. - * This method is invoked when the value of the {@link getText Text} - * property changes on postback. - * If you override this method, be sure to call the parent implementation to ensure - * the invocation of the attached event handlers. - * @param TEventParameter event parameter to be passed to the event handlers - */ - public function onTextChanged($param) - { - $this->raiseEvent('OnTextChanged',$this,$param); - } - - /** - * Raises postdata changed event. - * This method is required by {@link IPostBackDataHandler} interface. - * It is invoked by the framework when {@link getText Text} property - * is changed on postback. - * This method is primarly used by framework developers. - */ - public function raisePostDataChangedEvent() - { - if($this->getAutoPostBack() && $this->getCausesValidation()) - $this->getPage()->validate($this->getValidationGroup()); - $this->onTextChanged(null); - } - - /** - * Renders the body content of the textbox when it is in MultiLine text mode. - * @param THtmlWriter the writer for rendering - */ - public function renderContents($writer) - { - if($this->getTextMode()==='MultiLine') - $writer->write(THttpUtility::htmlEncode($this->getText())); - } - - /** - * Renders an additional line-break after the opening tag when it - * is in MultiLine text mode. - * @param THtmlWriter the writer used for the rendering purpose^M - */ - public function renderBeginTag($writer) - { - parent::renderBeginTag($writer); - if($this->getTextMode()==='MultiLine') - $writer->write("\n"); - } - - /** - * @return TTextBoxAutoCompleteType the AutoComplete type of the textbox - */ - public function getAutoCompleteType() - { - return $this->getViewState('AutoCompleteType',TTextBoxAutoCompleteType::None); - } - - /** - * @param TTextBoxAutoCompleteType the AutoComplete type of the textbox, default value is TTextBoxAutoCompleteType::None. - * @throws TInvalidDataValueException if the input parameter is not a valid AutoComplete type - */ - public function setAutoCompleteType($value) - { - $this->setViewState('AutoCompleteType',TPropertyValue::ensureEnum($value,'TTextBoxAutoCompleteType'),TTextBoxAutoCompleteType::None); - } - - /** - * @return boolean a value indicating whether an automatic postback to the server - * will occur whenever the user modifies the text in the TTextBox control and - * then tabs out of the component. Defaults to false. - */ - public function getAutoPostBack() - { - return $this->getViewState('AutoPostBack',false); - } - - /** - * Sets the value indicating if postback automatically. - * An automatic postback to the server will occur whenever the user - * modifies the text in the TTextBox control and then tabs out of the component. - * @param boolean the value indicating if postback automatically - */ - public function setAutoPostBack($value) - { - $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean a value indicating whether the input text should be trimmed spaces. Defaults to false. - */ - public function getAutoTrim() - { - return $this->getViewState('AutoTrim',false); - } - - /** - * Sets the value indicating if the input text should be trimmed spaces - * @param boolean the value indicating if the input text should be trimmed spaces - */ - public function setAutoTrim($value) - { - $this->setViewState('AutoTrim',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether postback event trigger by this text box will cause input validation, default is true. - */ - public function getCausesValidation() - { - return $this->getViewState('CausesValidation',true); - } - - /** - * @param boolean whether postback event trigger by this text box will cause input validation. - */ - public function setCausesValidation($value) - { - $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return integer the display width of the text box in characters, default is 0 meaning not set. - */ - public function getColumns() - { - return $this->getViewState('Columns',0); - } - - /** - * Sets the display width of the text box in characters. - * @param integer the display width, set it 0 to clear the setting - */ - public function setColumns($value) - { - $this->setViewState('Columns',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return integer the maximum number of characters allowed in the text box, default is 0 meaning not set. - */ - public function getMaxLength() - { - return $this->getViewState('MaxLength',0); - } - - /** - * Sets the maximum number of characters allowed in the text box. - * @param integer the maximum length, set it 0 to clear the setting - */ - public function setMaxLength($value) - { - $this->setViewState('MaxLength',TPropertyValue::ensureInteger($value),0); - } - - /** - * @return boolean whether the textbox is read only, default is false. - */ - public function getReadOnly() - { - return $this->getViewState('ReadOnly',false); - } - - /** - * @param boolean whether the textbox is read only - */ - public function setReadOnly($value) - { - $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return integer the number of rows displayed in a multiline text box, default is 4 - */ - public function getRows() - { - return $this->getViewState('Rows',self::DEFAULT_ROWS); - } - - /** - * Sets the number of rows displayed in a multiline text box. - * @param integer the number of rows - */ - public function setRows($value) - { - $this->setViewState('Rows',TPropertyValue::ensureInteger($value),self::DEFAULT_ROWS); - } - - /** - * @return boolean whether password should be displayed in the textbox during postback. Defaults to false. This property only applies when TextMode='Password'. - */ - public function getPersistPassword() - { - return $this->getViewState('PersistPassword',false); - } - - /** - * @param boolean whether password should be displayed in the textbox during postback. This property only applies when TextMode='Password'. - */ - public function setPersistPassword($value) - { - $this->setViewState('PersistPassword',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return string the text content of the TTextBox control. - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * Sets the text content of the TTextBox control. - * @param string the text content - */ - public function setText($value) - { - $this->setViewState('Text',$value,''); - $this->_safeText = null; - } - - /** - * Returns the text content of the TTextBox control. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link getText()}. - * @return string the text content of the TTextBox control. - * @see getText - * @since 3.1.0 - */ - public function getData() - { - return $this->getText(); - } - - /** - * Sets the text content of the TTextBox control. - * This method is required by {@link IDataRenderer}. - * It is the same as {@link setText()}. - * @param string the text content of the TTextBox control. - * @see setText - * @since 3.1.0 - */ - public function setData($value) - { - $this->setText($value); - } - - /** - * @return string safe text content with javascript stripped off - */ - public function getSafeText() - { - if($this->_safeText===null) - $this->_safeText=$this->getSafeTextParser()->parse($this->getText()); - return $this->_safeText; - } - - /** - * @return mixed safe text parser - */ - protected function getSafeTextParser() - { - if(!self::$_safeTextParser) - self::$_safeTextParser=Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); - return self::$_safeTextParser; - } - - /** - * @return TTextBoxMode the behavior mode of the TTextBox component. Defaults to TTextBoxMode::SingleLine. - */ - public function getTextMode() - { - return $this->getViewState('TextMode',TTextBoxMode::SingleLine); - } - - /** - * Sets the behavior mode of the TTextBox component. - * @param TTextBoxMode the text mode - * @throws TInvalidDataValueException if the input value is not a valid text mode. - */ - public function setTextMode($value) - { - $this->setViewState('TextMode',TPropertyValue::ensureEnum($value,'TTextBoxMode'),TTextBoxMode::SingleLine); - } - - /** - * @return string the group of validators which the text box causes validation upon postback - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group of validators which the text box causes validation upon postback - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * @return boolean whether the text content wraps within a multiline text box. Defaults to true. - */ - public function getWrap() - { - return $this->getViewState('Wrap',true); - } - - /** - * Sets the value indicating whether the text content wraps within a multiline text box. - * @param boolean whether the text content wraps within a multiline text box. - */ - public function setWrap($value) - { - $this->setViewState('Wrap',TPropertyValue::ensureBoolean($value),true); - } -} - -/** - * TTextBoxMode class. - * TTextBoxMode defines the enumerable type for the possible mode - * that a {@link TTextBox} control could be at. - * - * The following enumerable values are defined: - * - SingleLine: the textbox will be a regular single line input - * - MultiLine: the textbox will be a textarea allowing multiple line input - * - Password: the textbox will hide user input like a password input box - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTextBoxMode extends TEnumerable -{ - const SingleLine='SingleLine'; - const MultiLine='MultiLine'; - const Password='Password'; -} - -/** - * TTextBoxAutoCompleteType class. - * TTextBoxAutoCompleteType defines the possible AutoComplete type that is supported - * by a {@link TTextBox} control. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TTextBoxAutoCompleteType extends TEnumerable -{ - const BusinessCity='BusinessCity'; - const BusinessCountryRegion='BusinessCountryRegion'; - const BusinessFax='BusinessFax'; - const BusinessPhone='BusinessPhone'; - const BusinessState='BusinessState'; - const BusinessStreetAddress='BusinessStreetAddress'; - const BusinessUrl='BusinessUrl'; - const BusinessZipCode='BusinessZipCode'; - const Cellular='Cellular'; - const Company='Company'; - const Department='Department'; - const Disabled='Disabled'; - const DisplayName='DisplayName'; - const Email='Email'; - const FirstName='FirstName'; - const Gender='Gender'; - const HomeCity='HomeCity'; - const HomeCountryRegion='HomeCountryRegion'; - const HomeFax='HomeFax'; - const Homepage='Homepage'; - const HomePhone='HomePhone'; - const HomeState='HomeState'; - const HomeStreetAddress='HomeStreetAddress'; - const HomeZipCode='HomeZipCode'; - const JobTitle='JobTitle'; - const LastName='LastName'; - const MiddleName='MiddleName'; - const None='None'; - const Notes='Notes'; - const Office='Office'; - const Pager='Pager'; - const Search='Search'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TTextBox class + * + * TTextBox displays a text box on the Web page for user input. + * The text displayed in the TTextBox control is determined by the + * {@link setText Text} property. You can create a SingleLine, + * a MultiLine, or a Password text box by setting + * the {@link setTextMode TextMode} property. If the TTextBox control + * is a multiline text box, the number of rows it displays is determined + * by the {@link setRows Rows} property, and the {@link setWrap Wrap} property + * can be used to determine whether to wrap the text in the component. + * + * To specify the display width of the text box, in characters, set + * the {@link setColumns Columns} property. To prevent the text displayed + * in the component from being modified, set the {@link setReadOnly ReadOnly} + * property to true. If you want to limit the user input to a specified number + * of characters, set the {@link setMaxLength MaxLength} property. + * To use AutoComplete feature, set the {@link setAutoCompleteType AutoCompleteType} property. + * + * If {@link setAutoPostBack AutoPostBack} is set true, updating the text box + * and then changing the focus out of it will cause postback action. + * And if {@link setCausesValidation CausesValidation} is true, validation will + * also be processed, which can be further restricted within + * a {@link setValidationGroup ValidationGroup}. + * + * WARNING: Be careful if you want to display the text collected via TTextBox. + * Malicious cross-site script may be injected in. You may use {@link getSafeText SafeText} + * to prevent this problem. + * + * NOTE: If you set {@link setWrap Wrap} to false or use {@link setAutoCompleteType AutoCompleteType}, + * the generated HTML output for the textbox will not be XHTML-compatible. + * Currently, no alternatives are available. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable, IDataRenderer +{ + /** + * Default number of rows (for MultiLine text box) + */ + const DEFAULT_ROWS=4; + /** + * Default number of columns (for MultiLine text box) + */ + const DEFAULT_COLUMNS=20; + /** + * @var mixed safe text parser + */ + private static $_safeTextParser=null; + /** + * @var string safe textbox content with javascript stripped off + */ + private $_safeText; + private $_dataChanged=false; + private $_isValid=true; + + /** + * @return string tag name of the textbox + */ + protected function getTagName() + { + return ($this->getTextMode()==='MultiLine')?'textarea':'input'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional textbox specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + if(($uid=$this->getUniqueID())!=='') + $writer->addAttribute('name',$uid); + if(($textMode=$this->getTextMode())===TTextBoxMode::MultiLine) + { + if(($rows=$this->getRows())<=0) + $rows=self::DEFAULT_ROWS; + if(($cols=$this->getColumns())<=0) + $cols=self::DEFAULT_COLUMNS; + $writer->addAttribute('rows',"$rows"); + $writer->addAttribute('cols',"$cols"); + if(!$this->getWrap()) + $writer->addAttribute('wrap','off'); + } + else + { + if($textMode===TTextBoxMode::SingleLine) + { + $writer->addAttribute('type','text'); + if(($text=$this->getText())!=='') + $writer->addAttribute('value',$text); + } + else + { + if($this->getPersistPassword() && ($text=$this->getText())!=='') + $writer->addAttribute('value',$text); + $writer->addAttribute('type','password'); + } + + if(($act=$this->getAutoCompleteType())!=='None') + { + if($act==='Disabled') + $writer->addAttribute('autocomplete','off'); + else if($act==='Search') + $writer->addAttribute('vcard_name','search'); + else if($act==='HomeCountryRegion') + $writer->addAttribute('vcard_name','HomeCountry'); + else if($act==='BusinessCountryRegion') + $writer->addAttribute('vcard_name','BusinessCountry'); + else + { + if(strpos($act,'Business')===0) + $act='Business'.'.'.substr($act,8); + else if(strpos($act,'Home')===0) + $act='Home'.'.'.substr($act,4); + $writer->addAttribute('vcard_name','vCard.'.$act); + } + } + + if(($cols=$this->getColumns())>0) + $writer->addAttribute('size',"$cols"); + if(($maxLength=$this->getMaxLength())>0) + $writer->addAttribute('maxlength',"$maxLength"); + } + if($this->getReadOnly()) + $writer->addAttribute('readonly','readonly'); + $isEnabled=$this->getEnabled(true); + if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + if($isEnabled + && $this->getEnableClientScript() + && ( $this->getAutoPostBack() || $textMode===TTextBoxMode::SingleLine) + && $page->getClientSupportsJavaScript()) + { + $this->renderClientControlScript($writer); + } + parent::addAttributesToRender($writer); + } + + /** + * Renders the javascript for textbox. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * 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.TTextBox'; + } + + /** + * Gets the post back options for this textbox. + * @return array + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['AutoPostBack'] = $this->getAutoPostBack(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['TextMode'] = $this->getTextMode(); + return $options; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=$values[$key]; + if($this->getAutoTrim()) + $value=trim($value); + if(!$this->getReadOnly() && $this->getText()!==$value) + { + $this->setText($value); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getText(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + + /** + * Raises OnTextChanged event. + * This method is invoked when the value of the {@link getText Text} + * property changes on postback. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onTextChanged($param) + { + $this->raiseEvent('OnTextChanged',$this,$param); + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getText Text} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onTextChanged(null); + } + + /** + * Renders the body content of the textbox when it is in MultiLine text mode. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if($this->getTextMode()==='MultiLine') + $writer->write(THttpUtility::htmlEncode($this->getText())); + } + + /** + * Renders an additional line-break after the opening tag when it + * is in MultiLine text mode. + * @param THtmlWriter the writer used for the rendering purpose^M + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if($this->getTextMode()==='MultiLine') + $writer->write("\n"); + } + + /** + * @return TTextBoxAutoCompleteType the AutoComplete type of the textbox + */ + public function getAutoCompleteType() + { + return $this->getViewState('AutoCompleteType',TTextBoxAutoCompleteType::None); + } + + /** + * @param TTextBoxAutoCompleteType the AutoComplete type of the textbox, default value is TTextBoxAutoCompleteType::None. + * @throws TInvalidDataValueException if the input parameter is not a valid AutoComplete type + */ + public function setAutoCompleteType($value) + { + $this->setViewState('AutoCompleteType',TPropertyValue::ensureEnum($value,'TTextBoxAutoCompleteType'),TTextBoxAutoCompleteType::None); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user modifies the text in the TTextBox control and + * then tabs out of the component. Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * modifies the text in the TTextBox control and then tabs out of the component. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean a value indicating whether the input text should be trimmed spaces. Defaults to false. + */ + public function getAutoTrim() + { + return $this->getViewState('AutoTrim',false); + } + + /** + * Sets the value indicating if the input text should be trimmed spaces + * @param boolean the value indicating if the input text should be trimmed spaces + */ + public function setAutoTrim($value) + { + $this->setViewState('AutoTrim',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether postback event trigger by this text box will cause input validation, default is true. + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this text box will cause input validation. + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return integer the display width of the text box in characters, default is 0 meaning not set. + */ + public function getColumns() + { + return $this->getViewState('Columns',0); + } + + /** + * Sets the display width of the text box in characters. + * @param integer the display width, set it 0 to clear the setting + */ + public function setColumns($value) + { + $this->setViewState('Columns',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the maximum number of characters allowed in the text box, default is 0 meaning not set. + */ + public function getMaxLength() + { + return $this->getViewState('MaxLength',0); + } + + /** + * Sets the maximum number of characters allowed in the text box. + * @param integer the maximum length, set it 0 to clear the setting + */ + public function setMaxLength($value) + { + $this->setViewState('MaxLength',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return boolean whether the textbox is read only, default is false. + */ + public function getReadOnly() + { + return $this->getViewState('ReadOnly',false); + } + + /** + * @param boolean whether the textbox is read only + */ + public function setReadOnly($value) + { + $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer the number of rows displayed in a multiline text box, default is 4 + */ + public function getRows() + { + return $this->getViewState('Rows',self::DEFAULT_ROWS); + } + + /** + * Sets the number of rows displayed in a multiline text box. + * @param integer the number of rows + */ + public function setRows($value) + { + $this->setViewState('Rows',TPropertyValue::ensureInteger($value),self::DEFAULT_ROWS); + } + + /** + * @return boolean whether password should be displayed in the textbox during postback. Defaults to false. This property only applies when TextMode='Password'. + */ + public function getPersistPassword() + { + return $this->getViewState('PersistPassword',false); + } + + /** + * @param boolean whether password should be displayed in the textbox during postback. This property only applies when TextMode='Password'. + */ + public function setPersistPassword($value) + { + $this->setViewState('PersistPassword',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return string the text content of the TTextBox control. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text content of the TTextBox control. + * @param string the text content + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + $this->_safeText = null; + } + + /** + * Returns the text content of the TTextBox control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text content of the TTextBox control. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text content of the TTextBox control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text content of the TTextBox control. + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string safe text content with javascript stripped off + */ + public function getSafeText() + { + if($this->_safeText===null) + $this->_safeText=$this->getSafeTextParser()->parse($this->getText()); + return $this->_safeText; + } + + /** + * @return mixed safe text parser + */ + protected function getSafeTextParser() + { + if(!self::$_safeTextParser) + self::$_safeTextParser=Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); + return self::$_safeTextParser; + } + + /** + * @return TTextBoxMode the behavior mode of the TTextBox component. Defaults to TTextBoxMode::SingleLine. + */ + public function getTextMode() + { + return $this->getViewState('TextMode',TTextBoxMode::SingleLine); + } + + /** + * Sets the behavior mode of the TTextBox component. + * @param TTextBoxMode the text mode + * @throws TInvalidDataValueException if the input value is not a valid text mode. + */ + public function setTextMode($value) + { + $this->setViewState('TextMode',TPropertyValue::ensureEnum($value,'TTextBoxMode'),TTextBoxMode::SingleLine); + } + + /** + * @return string the group of validators which the text box causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the text box causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return boolean whether the text content wraps within a multiline text box. Defaults to true. + */ + public function getWrap() + { + return $this->getViewState('Wrap',true); + } + + /** + * Sets the value indicating whether the text content wraps within a multiline text box. + * @param boolean whether the text content wraps within a multiline text box. + */ + public function setWrap($value) + { + $this->setViewState('Wrap',TPropertyValue::ensureBoolean($value),true); + } +} + +/** + * TTextBoxMode class. + * TTextBoxMode defines the enumerable type for the possible mode + * that a {@link TTextBox} control could be at. + * + * The following enumerable values are defined: + * - SingleLine: the textbox will be a regular single line input + * - MultiLine: the textbox will be a textarea allowing multiple line input + * - Password: the textbox will hide user input like a password input box + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTextBoxMode extends TEnumerable +{ + const SingleLine='SingleLine'; + const MultiLine='MultiLine'; + const Password='Password'; +} + +/** + * TTextBoxAutoCompleteType class. + * TTextBoxAutoCompleteType defines the possible AutoComplete type that is supported + * by a {@link TTextBox} control. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTextBoxAutoCompleteType extends TEnumerable +{ + const BusinessCity='BusinessCity'; + const BusinessCountryRegion='BusinessCountryRegion'; + const BusinessFax='BusinessFax'; + const BusinessPhone='BusinessPhone'; + const BusinessState='BusinessState'; + const BusinessStreetAddress='BusinessStreetAddress'; + const BusinessUrl='BusinessUrl'; + const BusinessZipCode='BusinessZipCode'; + const Cellular='Cellular'; + const Company='Company'; + const Department='Department'; + const Disabled='Disabled'; + const DisplayName='DisplayName'; + const Email='Email'; + const FirstName='FirstName'; + const Gender='Gender'; + const HomeCity='HomeCity'; + const HomeCountryRegion='HomeCountryRegion'; + const HomeFax='HomeFax'; + const Homepage='Homepage'; + const HomePhone='HomePhone'; + const HomeState='HomeState'; + const HomeStreetAddress='HomeStreetAddress'; + const HomeZipCode='HomeZipCode'; + const JobTitle='JobTitle'; + const LastName='LastName'; + const MiddleName='MiddleName'; + const None='None'; + const Notes='Notes'; + const Office='Office'; + const Pager='Pager'; + const Search='Search'; +} + diff --git a/framework/Web/UI/WebControls/TTextProcessor.php b/framework/Web/UI/WebControls/TTextProcessor.php index 6d95a482..60d047fe 100644 --- a/framework/Web/UI/WebControls/TTextProcessor.php +++ b/framework/Web/UI/WebControls/TTextProcessor.php @@ -1,86 +1,86 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TTextProcessor class. - * - * TTextProcessor is the base class for classes that process or transform - * text content into different forms. The text content to be processed - * is specified by {@link setText Text} property. If it is not set, the body - * content enclosed within the processor control will be processed and rendered. - * The body content includes static text strings and the rendering result - * of child controls. - * - * Note, all child classes must implement {@link processText} method. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI - * @since 3.0.1 - */ -abstract class TTextProcessor extends TWebControl -{ - /** - * Processes a text string. - * This method must be implemented by child classes. - * @param string text string to be processed - * @return string the processed text result - */ - abstract public function processText($text); - - /** - * HTML-decodes static text. - * This method overrides parent implementation. - * @param mixed object to be added as body content - */ - public function addParsedObject($object) - { - if(is_string($object)) - $object=html_entity_decode($object,ENT_QUOTES,'UTF-8'); - parent::addParsedObject($object); - } - - /** - * @return string text to be processed - */ - public function getText() - { - return $this->getViewState('Text',''); - } - - /** - * @param string text to be processed - */ - public function setText($value) - { - $this->setViewState('Text',$value); - } - - /** - * Renders body content. - * This method overrides the parent implementation by replacing - * the body content with the processed text content. - * @param THtmlWriter writer - */ - public function renderContents($writer) - { - if(($text=$this->getText())==='' && $this->getHasControls()) - { - $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); - parent::renderContents($htmlWriter); - $text=$htmlWriter->flush(); - } - if($text!=='') - $writer->write($this->processText($text)); - } - -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TTextProcessor class. + * + * TTextProcessor is the base class for classes that process or transform + * text content into different forms. The text content to be processed + * is specified by {@link setText Text} property. If it is not set, the body + * content enclosed within the processor control will be processed and rendered. + * The body content includes static text strings and the rendering result + * of child controls. + * + * Note, all child classes must implement {@link processText} method. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI + * @since 3.0.1 + */ +abstract class TTextProcessor extends TWebControl +{ + /** + * Processes a text string. + * This method must be implemented by child classes. + * @param string text string to be processed + * @return string the processed text result + */ + abstract public function processText($text); + + /** + * HTML-decodes static text. + * This method overrides parent implementation. + * @param mixed object to be added as body content + */ + public function addParsedObject($object) + { + if(is_string($object)) + $object=html_entity_decode($object,ENT_QUOTES,'UTF-8'); + parent::addParsedObject($object); + } + + /** + * @return string text to be processed + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string text to be processed + */ + public function setText($value) + { + $this->setViewState('Text',$value); + } + + /** + * Renders body content. + * This method overrides the parent implementation by replacing + * the body content with the processed text content. + * @param THtmlWriter writer + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='' && $this->getHasControls()) + { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + parent::renderContents($htmlWriter); + $text=$htmlWriter->flush(); + } + if($text!=='') + $writer->write($this->processText($text)); + } + +} diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php index 6c258927..c915d163 100644 --- a/framework/Web/UI/WebControls/TValidationSummary.php +++ b/framework/Web/UI/WebControls/TValidationSummary.php @@ -1,536 +1,536 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TValidationSummary class - * - * TValidationSummary displays a summary of validation errors inline on a Web page, - * in a message box, or both. By default, a validation summary will collect - * {@link TBaseValidator::getErrorMessage ErrorMessage} of all failed validators - * on the page. If {@link getValidationGroup ValidationGroup} is not - * empty, only those validators who belong to the group will show their error messages - * in the summary. - * - * The summary can be displayed as a list, as a bulleted list, or as a single - * paragraph based on the {@link setDisplayMode DisplayMode} property. - * The messages shown can be prefixed with {@link setHeaderText HeaderText}. - * - * The summary can be displayed on the Web page and in a message box by setting - * the {@link setShowSummary ShowSummary} and {@link setShowMessageBox ShowMessageBox} - * properties, respectively. Note, the latter is only effective when - * {@link setEnableClientScript EnableClientScript} is true. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TValidationSummary extends TWebControl -{ - /** - * @var TClientSideValidationSummaryOptions validation client side options. - */ - private $_clientSide; - - /** - * Constructor. - * This method sets the foreground color to red. - */ - public function __construct() - { - parent::__construct(); - $this->setForeColor('red'); - } - - /** - * @return TValidationSummaryDisplayStyle the style of displaying the error messages. Defaults to TValidationSummaryDisplayStyle::Fixed. - */ - public function getDisplay() - { - return $this->getViewState('Display',TValidationSummaryDisplayStyle::Fixed); - } - - /** - * @param TValidationSummaryDisplayStyle the style of displaying the error messages - */ - public function setDisplay($value) - { - $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayStyle'),TValidationSummaryDisplayStyle::Fixed); - } - - /** - * @return string the header text displayed at the top of the summary - */ - public function getHeaderText() - { - return $this->getViewState('HeaderText',''); - } - - /** - * Sets the header text to be displayed at the top of the summary - * @param string the header text - */ - public function setHeaderText($value) - { - $this->setViewState('HeaderText',$value,''); - } - - /** - * @return TValidationSummaryDisplayMode the mode of displaying error messages. Defaults to TValidationSummaryDisplayMode::BulletList. - */ - public function getDisplayMode() - { - return $this->getViewState('DisplayMode',TValidationSummaryDisplayMode::BulletList); - } - - /** - * @param TValidationSummaryDisplayMode the mode of displaying error messages - */ - public function setDisplayMode($value) - { - $this->setViewState('DisplayMode',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayMode'),TValidationSummaryDisplayMode::BulletList); - } - - /** - * @return boolean whether the TValidationSummary component updates itself using client-side script. Defaults to true. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether the TValidationSummary component updates itself using client-side script. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the validation summary is displayed in a message box. Defaults to false. - */ - public function getShowMessageBox() - { - return $this->getViewState('ShowMessageBox',false); - } - - /** - * @param boolean whether the validation summary is displayed in a message box. - */ - public function setShowMessageBox($value) - { - $this->setViewState('ShowMessageBox',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether the validation summary is displayed inline. Defaults to true. - */ - public function getShowSummary() - { - return $this->getViewState('ShowSummary',true); - } - - /** - * @param boolean whether the validation summary is displayed inline. - */ - public function setShowSummary($value) - { - $this->setViewState('ShowSummary',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether scroll summary into viewport or not. Defaults to true. - */ - public function getScrollToSummary() - { - return $this->getViewState('ScrollToSummary',true); - } - - /** - * @param boolean whether scroll summary into viewport or not. - */ - public function setScrollToSummary($value) - { - $this->setViewState('ScrollToSummary',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return boolean whether the validation summary should be anchored. Defaults to false. - */ - public function getShowAnchor() - { - return $this->getViewState('ShowAnchor',false); - } - - /** - * @param boolean whether the validation summary should be anchored. - */ - public function setShowAnchor($value) - { - $this->setViewState('ShowAnchor',TPropertyValue::ensureBoolean($value),false); - } - - /** - * Gets the auto-update for this summary. - * @return boolean automatic client-side summary updates. Defaults to true. - */ - public function getAutoUpdate() - { - return $this->getViewState('AutoUpdate', true); - } - - /** - * Sets the summary to auto-update on the client-side - * @param boolean true for automatic summary updates. - */ - public function setAutoUpdate($value) - { - $this->setViewState('AutoUpdate', TPropertyValue::ensureBoolean($value), true); - } - - /** - * @return string the group which this validator belongs to - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group which this validator belongs to - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - protected function addAttributesToRender($writer) - { - $display=$this->getDisplay(); - $visible=$this->getEnabled(true) && count($this->getErrorMessages()) > 0; - if(!$visible) - { - if($display===TValidationSummaryDisplayStyle::None || $display===TValidationSummaryDisplayStyle::Dynamic) - $writer->addStyleAttribute('display','none'); - else - $writer->addStyleAttribute('visibility','hidden'); - } - $writer->addAttribute('id',$this->getClientID()); - parent::addAttributesToRender($writer); - } - - /** - * Render the javascript for validation summary. - * @param array list of options for validation summary. - */ - protected function renderJsSummary() - { - if(!$this->getEnabled(true) || !$this->getEnableClientScript()) - return; - $cs = $this->getPage()->getClientScript(); - $cs->registerPradoScript('validator'); - - //need to register the validation manager is validation summary is alone. - $formID=$this->getPage()->getForm()->getClientID(); - $scriptKey = "TBaseValidator:$formID"; - if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey)) - { - $manager['FormID'] = $formID; - $options = TJavaScript::encode($manager); - $cs->registerPradoScript('validator'); - $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); - } - - - $options=TJavaScript::encode($this->getClientScriptOptions()); - $script = "new Prado.WebUI.TValidationSummary({$options});"; - $cs->registerEndScript($this->getClientID(), $script); - } - - /** - * Get a list of options for the client-side javascript validation summary. - * @return array list of options for the summary - */ - protected function getClientScriptOptions() - { - $options['ID'] = $this->getClientID(); - $options['FormID'] = $this->getPage()->getForm()->getClientID(); - if($this->getShowMessageBox()) - $options['ShowMessageBox']=true; - if(!$this->getShowSummary()) - $options['ShowSummary']=false; - - $options['ScrollToSummary']=$this->getScrollToSummary(); - $options['HeaderText']=$this->getHeaderText(); - $options['DisplayMode']=$this->getDisplayMode(); - - $options['Refresh'] = $this->getAutoUpdate(); - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['Display'] = $this->getDisplay(); - - if($this->_clientSide!==null) - $options = array_merge($options,$this->_clientSide->getOptions()->toArray()); - - return $options; - } - - /** - * @return TClientSideValidationSummaryOptions client-side validation summary - * event options. - */ - public function getClientSide() - { - if($this->_clientSide===null) - $this->_clientSide = $this->createClientScript(); - return $this->_clientSide; - } - - /** - * @return TClientSideValidationSummaryOptions javascript validation summary - * event options. - */ - protected function createClientScript() - { - return new TClientSideValidationSummaryOptions; - } - /** - * Get the list of validation error messages. - * @return array list of validator error messages. - */ - protected function getErrorMessages() - { - $validators=$this->getPage()->getValidators($this->getValidationGroup()); - $messages = array(); - foreach($validators as $validator) - { - if(!$validator->getIsValid() && ($msg=$validator->getErrorMessage())!=='') - //$messages[] = $validator->getAnchoredMessage($msg); - $messages[] = $msg; - } - return $messages; - } - - /** - * Overrides parent implementation by rendering TValidationSummary-specific presentation. - * @return string the rendering result - */ - public function renderContents($writer) - { - $this->renderJsSummary(); - if($this->getShowSummary()) - { -// $this->setStyle('display:block'); - switch($this->getDisplayMode()) - { - case TValidationSummaryDisplayMode::SimpleList: - $this->renderList($writer); - break; - case TValidationSummaryDisplayMode::SingleParagraph: - $this->renderSingleParagraph($writer); - break; - case TValidationSummaryDisplayMode::BulletList: - $this->renderBulletList($writer); - break; - case TValidationSummaryDisplayMode::HeaderOnly: - $this->renderHeaderOnly($writer); - break; - } - } - } - - /** - * Render the validation summary as a simple list. - * @param array list of messages - * @param string the header text - * @return string summary list - */ - protected function renderList($writer) - { - $header=$this->getHeaderText(); - $messages=$this->getErrorMessages(); - $content = ''; - if(strlen($header)) - $content.= $header."
    \n"; - foreach($messages as $message) - $content.="$message
    \n"; - $writer->write($content); - } - - /** - * Render the validation summary as a paragraph. - * @param array list of messages - * @param string the header text - * @return string summary paragraph - */ - protected function renderSingleParagraph($writer) - { - $header=$this->getHeaderText(); - $messages=$this->getErrorMessages(); - $content = $header; - foreach($messages as $message) - $content.= ' '.$message; - $writer->write($content); - } - - /** - * Render the validation summary as a bullet list. - * @param array list of messages - * @param string the header text - * @return string summary bullet list - */ - protected function renderBulletList($writer) - { - $header=$this->getHeaderText(); - $messages=$this->getErrorMessages(); - $content = $header; - if(count($messages)>0) - { - $content .= "
      \n"; - foreach($messages as $message) - $content.= '
    • '.$message."
    • \n"; - $content .= "
    \n"; - } - $writer->write($content); - } - - /** - * Render the validation summary header text only. - * @param THtmlWriter the writer used for the rendering purpose - */ - protected function renderHeaderOnly($writer) - { - $writer->write($this->getHeaderText()); - } -} - -/** - * TClientSideValidationSummaryOptions class. - * - * Client-side validation summary events such as {@link setOnHideSummary - * OnHideSummary} and {@link setOnShowSummary OnShowSummary} can be modified - * through the {@link TBaseValidator:: getClientSide ClientSide} property of a - * validation summary. - * - * The OnHideSummary event is raise when the validation summary - * requests to hide the messages. - * - * The OnShowSummary event is raised when the validation summary - * requests to show the messages. - * - * See the quickstart documentation for further details. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TClientSideValidationSummaryOptions extends TClientSideOptions -{ - /** - * @return string javascript code for client-side OnHideSummary event. - */ - public function getOnHideSummary() - { - return $this->getOption('OnHideSummary'); - } - - /** - * Client-side OnHideSummary validation summary event is raise when all the - * validators are valid. This will override the default client-side - * validation summary behaviour. - * @param string javascript code for client-side OnHideSummary event. - */ - public function setOnHideSummary($javascript) - { - $this->setFunction('OnHideSummary', $javascript); - } - - /** - * Client-side OnShowSummary event is raise when one or more validators are - * not valid. This will override the default client-side validation summary - * behaviour. - * @param string javascript code for client-side OnShowSummary event. - */ - public function setOnShowSummary($javascript) - { - $this->setFunction('OnShowSummary', $javascript); - } - - /** - * @return string javascript code for client-side OnShowSummary event. - */ - public function getOnShowSummary() - { - return $this->getOption('OnShowSummary'); - } - - /** - * Ensure the string is a valid javascript function. The code block - * is enclosed with "function(summary, validators){ }" block. - * @param string javascript code. - * @return string javascript function code. - */ - protected function ensureFunction($javascript) - { - return "function(summary, validators){ {$javascript} }"; - } -} - - -/** - * TValidationSummaryDisplayMode class. - * TValidationSummaryDisplayMode defines the enumerable type for the possible modes - * that a {@link TValidationSummary} can organize and display the collected error messages. - * - * The following enumerable values are defined: - * - SimpleList: the error messages are displayed as a list without any decorations. - * - SingleParagraph: the error messages are concatenated together into a paragraph. - * - BulletList: the error messages are displayed as a bulleted list. - * - HeaderOnly: only the HeaderText will be display. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TValidationSummaryDisplayMode extends TEnumerable -{ - const SimpleList='SimpleList'; - const SingleParagraph='SingleParagraph'; - const BulletList='BulletList'; - const HeaderOnly='HeaderOnly'; -} - - -/** - * TValidationSummaryDisplay class. - * TValidationSummaryDisplay defines the enumerable type for the possible styles - * that a {@link TValidationSummary} can display the collected error messages. - * - * The following enumerable values are defined: - * - None: the error messages are not displayed - * - Dynamic: the error messages are dynamically added to display as the corresponding validators fail - * - Fixed: Similar to Dynamic except that the error messages physically occupy the page layout (even though they may not be visible) - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TValidationSummaryDisplayStyle extends TEnumerable -{ - const None='None'; - const Dynamic='Dynamic'; - const Fixed='Fixed'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TValidationSummary class + * + * TValidationSummary displays a summary of validation errors inline on a Web page, + * in a message box, or both. By default, a validation summary will collect + * {@link TBaseValidator::getErrorMessage ErrorMessage} of all failed validators + * on the page. If {@link getValidationGroup ValidationGroup} is not + * empty, only those validators who belong to the group will show their error messages + * in the summary. + * + * The summary can be displayed as a list, as a bulleted list, or as a single + * paragraph based on the {@link setDisplayMode DisplayMode} property. + * The messages shown can be prefixed with {@link setHeaderText HeaderText}. + * + * The summary can be displayed on the Web page and in a message box by setting + * the {@link setShowSummary ShowSummary} and {@link setShowMessageBox ShowMessageBox} + * properties, respectively. Note, the latter is only effective when + * {@link setEnableClientScript EnableClientScript} is true. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TValidationSummary extends TWebControl +{ + /** + * @var TClientSideValidationSummaryOptions validation client side options. + */ + private $_clientSide; + + /** + * Constructor. + * This method sets the foreground color to red. + */ + public function __construct() + { + parent::__construct(); + $this->setForeColor('red'); + } + + /** + * @return TValidationSummaryDisplayStyle the style of displaying the error messages. Defaults to TValidationSummaryDisplayStyle::Fixed. + */ + public function getDisplay() + { + return $this->getViewState('Display',TValidationSummaryDisplayStyle::Fixed); + } + + /** + * @param TValidationSummaryDisplayStyle the style of displaying the error messages + */ + public function setDisplay($value) + { + $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayStyle'),TValidationSummaryDisplayStyle::Fixed); + } + + /** + * @return string the header text displayed at the top of the summary + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * Sets the header text to be displayed at the top of the summary + * @param string the header text + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',$value,''); + } + + /** + * @return TValidationSummaryDisplayMode the mode of displaying error messages. Defaults to TValidationSummaryDisplayMode::BulletList. + */ + public function getDisplayMode() + { + return $this->getViewState('DisplayMode',TValidationSummaryDisplayMode::BulletList); + } + + /** + * @param TValidationSummaryDisplayMode the mode of displaying error messages + */ + public function setDisplayMode($value) + { + $this->setViewState('DisplayMode',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayMode'),TValidationSummaryDisplayMode::BulletList); + } + + /** + * @return boolean whether the TValidationSummary component updates itself using client-side script. Defaults to true. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether the TValidationSummary component updates itself using client-side script. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the validation summary is displayed in a message box. Defaults to false. + */ + public function getShowMessageBox() + { + return $this->getViewState('ShowMessageBox',false); + } + + /** + * @param boolean whether the validation summary is displayed in a message box. + */ + public function setShowMessageBox($value) + { + $this->setViewState('ShowMessageBox',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the validation summary is displayed inline. Defaults to true. + */ + public function getShowSummary() + { + return $this->getViewState('ShowSummary',true); + } + + /** + * @param boolean whether the validation summary is displayed inline. + */ + public function setShowSummary($value) + { + $this->setViewState('ShowSummary',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether scroll summary into viewport or not. Defaults to true. + */ + public function getScrollToSummary() + { + return $this->getViewState('ScrollToSummary',true); + } + + /** + * @param boolean whether scroll summary into viewport or not. + */ + public function setScrollToSummary($value) + { + $this->setViewState('ScrollToSummary',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the validation summary should be anchored. Defaults to false. + */ + public function getShowAnchor() + { + return $this->getViewState('ShowAnchor',false); + } + + /** + * @param boolean whether the validation summary should be anchored. + */ + public function setShowAnchor($value) + { + $this->setViewState('ShowAnchor',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Gets the auto-update for this summary. + * @return boolean automatic client-side summary updates. Defaults to true. + */ + public function getAutoUpdate() + { + return $this->getViewState('AutoUpdate', true); + } + + /** + * Sets the summary to auto-update on the client-side + * @param boolean true for automatic summary updates. + */ + public function setAutoUpdate($value) + { + $this->setViewState('AutoUpdate', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return string the group which this validator belongs to + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group which this validator belongs to + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + protected function addAttributesToRender($writer) + { + $display=$this->getDisplay(); + $visible=$this->getEnabled(true) && count($this->getErrorMessages()) > 0; + if(!$visible) + { + if($display===TValidationSummaryDisplayStyle::None || $display===TValidationSummaryDisplayStyle::Dynamic) + $writer->addStyleAttribute('display','none'); + else + $writer->addStyleAttribute('visibility','hidden'); + } + $writer->addAttribute('id',$this->getClientID()); + parent::addAttributesToRender($writer); + } + + /** + * Render the javascript for validation summary. + * @param array list of options for validation summary. + */ + protected function renderJsSummary() + { + if(!$this->getEnabled(true) || !$this->getEnableClientScript()) + return; + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript('validator'); + + //need to register the validation manager is validation summary is alone. + $formID=$this->getPage()->getForm()->getClientID(); + $scriptKey = "TBaseValidator:$formID"; + if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey)) + { + $manager['FormID'] = $formID; + $options = TJavaScript::encode($manager); + $cs->registerPradoScript('validator'); + $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); + } + + + $options=TJavaScript::encode($this->getClientScriptOptions()); + $script = "new Prado.WebUI.TValidationSummary({$options});"; + $cs->registerEndScript($this->getClientID(), $script); + } + + /** + * Get a list of options for the client-side javascript validation summary. + * @return array list of options for the summary + */ + protected function getClientScriptOptions() + { + $options['ID'] = $this->getClientID(); + $options['FormID'] = $this->getPage()->getForm()->getClientID(); + if($this->getShowMessageBox()) + $options['ShowMessageBox']=true; + if(!$this->getShowSummary()) + $options['ShowSummary']=false; + + $options['ScrollToSummary']=$this->getScrollToSummary(); + $options['HeaderText']=$this->getHeaderText(); + $options['DisplayMode']=$this->getDisplayMode(); + + $options['Refresh'] = $this->getAutoUpdate(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['Display'] = $this->getDisplay(); + + if($this->_clientSide!==null) + $options = array_merge($options,$this->_clientSide->getOptions()->toArray()); + + return $options; + } + + /** + * @return TClientSideValidationSummaryOptions client-side validation summary + * event options. + */ + public function getClientSide() + { + if($this->_clientSide===null) + $this->_clientSide = $this->createClientScript(); + return $this->_clientSide; + } + + /** + * @return TClientSideValidationSummaryOptions javascript validation summary + * event options. + */ + protected function createClientScript() + { + return new TClientSideValidationSummaryOptions; + } + /** + * Get the list of validation error messages. + * @return array list of validator error messages. + */ + protected function getErrorMessages() + { + $validators=$this->getPage()->getValidators($this->getValidationGroup()); + $messages = array(); + foreach($validators as $validator) + { + if(!$validator->getIsValid() && ($msg=$validator->getErrorMessage())!=='') + //$messages[] = $validator->getAnchoredMessage($msg); + $messages[] = $msg; + } + return $messages; + } + + /** + * Overrides parent implementation by rendering TValidationSummary-specific presentation. + * @return string the rendering result + */ + public function renderContents($writer) + { + $this->renderJsSummary(); + if($this->getShowSummary()) + { +// $this->setStyle('display:block'); + switch($this->getDisplayMode()) + { + case TValidationSummaryDisplayMode::SimpleList: + $this->renderList($writer); + break; + case TValidationSummaryDisplayMode::SingleParagraph: + $this->renderSingleParagraph($writer); + break; + case TValidationSummaryDisplayMode::BulletList: + $this->renderBulletList($writer); + break; + case TValidationSummaryDisplayMode::HeaderOnly: + $this->renderHeaderOnly($writer); + break; + } + } + } + + /** + * Render the validation summary as a simple list. + * @param array list of messages + * @param string the header text + * @return string summary list + */ + protected function renderList($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = ''; + if(strlen($header)) + $content.= $header."
    \n"; + foreach($messages as $message) + $content.="$message
    \n"; + $writer->write($content); + } + + /** + * Render the validation summary as a paragraph. + * @param array list of messages + * @param string the header text + * @return string summary paragraph + */ + protected function renderSingleParagraph($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = $header; + foreach($messages as $message) + $content.= ' '.$message; + $writer->write($content); + } + + /** + * Render the validation summary as a bullet list. + * @param array list of messages + * @param string the header text + * @return string summary bullet list + */ + protected function renderBulletList($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = $header; + if(count($messages)>0) + { + $content .= "
      \n"; + foreach($messages as $message) + $content.= '
    • '.$message."
    • \n"; + $content .= "
    \n"; + } + $writer->write($content); + } + + /** + * Render the validation summary header text only. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function renderHeaderOnly($writer) + { + $writer->write($this->getHeaderText()); + } +} + +/** + * TClientSideValidationSummaryOptions class. + * + * Client-side validation summary events such as {@link setOnHideSummary + * OnHideSummary} and {@link setOnShowSummary OnShowSummary} can be modified + * through the {@link TBaseValidator:: getClientSide ClientSide} property of a + * validation summary. + * + * The OnHideSummary event is raise when the validation summary + * requests to hide the messages. + * + * The OnShowSummary event is raised when the validation summary + * requests to show the messages. + * + * See the quickstart documentation for further details. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TClientSideValidationSummaryOptions extends TClientSideOptions +{ + /** + * @return string javascript code for client-side OnHideSummary event. + */ + public function getOnHideSummary() + { + return $this->getOption('OnHideSummary'); + } + + /** + * Client-side OnHideSummary validation summary event is raise when all the + * validators are valid. This will override the default client-side + * validation summary behaviour. + * @param string javascript code for client-side OnHideSummary event. + */ + public function setOnHideSummary($javascript) + { + $this->setFunction('OnHideSummary', $javascript); + } + + /** + * Client-side OnShowSummary event is raise when one or more validators are + * not valid. This will override the default client-side validation summary + * behaviour. + * @param string javascript code for client-side OnShowSummary event. + */ + public function setOnShowSummary($javascript) + { + $this->setFunction('OnShowSummary', $javascript); + } + + /** + * @return string javascript code for client-side OnShowSummary event. + */ + public function getOnShowSummary() + { + return $this->getOption('OnShowSummary'); + } + + /** + * Ensure the string is a valid javascript function. The code block + * is enclosed with "function(summary, validators){ }" block. + * @param string javascript code. + * @return string javascript function code. + */ + protected function ensureFunction($javascript) + { + return "function(summary, validators){ {$javascript} }"; + } +} + + +/** + * TValidationSummaryDisplayMode class. + * TValidationSummaryDisplayMode defines the enumerable type for the possible modes + * that a {@link TValidationSummary} can organize and display the collected error messages. + * + * The following enumerable values are defined: + * - SimpleList: the error messages are displayed as a list without any decorations. + * - SingleParagraph: the error messages are concatenated together into a paragraph. + * - BulletList: the error messages are displayed as a bulleted list. + * - HeaderOnly: only the HeaderText will be display. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TValidationSummaryDisplayMode extends TEnumerable +{ + const SimpleList='SimpleList'; + const SingleParagraph='SingleParagraph'; + const BulletList='BulletList'; + const HeaderOnly='HeaderOnly'; +} + + +/** + * TValidationSummaryDisplay class. + * TValidationSummaryDisplay defines the enumerable type for the possible styles + * that a {@link TValidationSummary} can display the collected error messages. + * + * The following enumerable values are defined: + * - None: the error messages are not displayed + * - Dynamic: the error messages are dynamically added to display as the corresponding validators fail + * - Fixed: Similar to Dynamic except that the error messages physically occupy the page layout (even though they may not be visible) + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TValidationSummaryDisplayStyle extends TEnumerable +{ + const None='None'; + const Dynamic='Dynamic'; + const Fixed='Fixed'; +} + diff --git a/framework/Web/UI/WebControls/TWebControlAdapter.php b/framework/Web/UI/WebControls/TWebControlAdapter.php index 68fecf1c..ad1e1642 100644 --- a/framework/Web/UI/WebControls/TWebControlAdapter.php +++ b/framework/Web/UI/WebControls/TWebControlAdapter.php @@ -1,71 +1,71 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * TWebControlAdapter class - * - * TWebControlAdapter is the base class for adapters that customize - * rendering for the Web control to which the adapter is attached. - * It may be used to modify the default markup or behavior for specific - * browsers. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWebControlAdapter extends TControlAdapter -{ - /** - * Renders the control to which the adapter is attached. - * It calls {@link renderBeginTag}, {@link renderContents} and - * {@link renderEndTag} in order. - * @param THtmlWriter writer for the rendering purpose - */ - public function render($writer) - { - $this->renderBeginTag($writer); - $this->renderContents($writer); - $this->renderEndTag($writer); - } - - /** - * Renders the openning tag for the attached control. - * Default implementation calls the attached control's corresponding method. - * @param THtmlWriter writer for the rendering purpose - */ - public function renderBeginTag($writer) - { - $this->getControl()->renderBeginTag($writer); - } - - /** - * Renders the body contents within the attached control tag. - * Default implementation calls the attached control's corresponding method. - * @param THtmlWriter writer for the rendering purpose - */ - public function renderContents($writer) - { - $this->getControl()->renderContents($writer); - } - - /** - * Renders the closing tag for the attached control. - * Default implementation calls the attached control's corresponding method. - * @param THtmlWriter writer for the rendering purpose - */ - public function renderEndTag($writer) - { - $this->getControl()->renderEndTag($writer); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +/** + * TWebControlAdapter class + * + * TWebControlAdapter is the base class for adapters that customize + * rendering for the Web control to which the adapter is attached. + * It may be used to modify the default markup or behavior for specific + * browsers. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWebControlAdapter extends TControlAdapter +{ + /** + * Renders the control to which the adapter is attached. + * It calls {@link renderBeginTag}, {@link renderContents} and + * {@link renderEndTag} in order. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + + /** + * Renders the openning tag for the attached control. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderBeginTag($writer) + { + $this->getControl()->renderBeginTag($writer); + } + + /** + * Renders the body contents within the attached control tag. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderContents($writer) + { + $this->getControl()->renderContents($writer); + } + + /** + * Renders the closing tag for the attached control. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderEndTag($writer) + { + $this->getControl()->renderEndTag($writer); + } +} + diff --git a/framework/Web/UI/WebControls/TWizard.php b/framework/Web/UI/WebControls/TWizard.php index 25d3a41b..b497d719 100644 --- a/framework/Web/UI/WebControls/TWizard.php +++ b/framework/Web/UI/WebControls/TWizard.php @@ -1,1402 +1,1402 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -Prado::using('System.Web.UI.WebControls.TMultiView'); -Prado::using('System.Web.UI.WebControls.TPanel'); -Prado::using('System.Web.UI.WebControls.TButton'); -Prado::using('System.Web.UI.WebControls.TLinkButton'); -Prado::using('System.Web.UI.WebControls.TImageButton'); -Prado::using('System.Web.UI.WebControls.TDataList'); -Prado::using('System.Web.UI.WebControls.TWizardNavigationButtonStyle'); - -/** - * Class TWizard. - * - * TWizard splits a large form and presents the user with a series of smaller - * forms to complete. TWizard is analogous to the installation wizard commonly - * used to install software in Windows. - * - * The smaller forms are called wizard steps ({@link TWizardStep}, which can be accessed via - * {@link getWizardSteps WizardSteps}. In template, wizard steps can be added - * into a wizard using the following syntax, - * - * - * - * content in step 1, may contain other controls - * - * - * content in step 2, may contain other controls - * - * - * - * - * Each wizard step can be one of the following types: - * - Start : the first step in the wizard. - * - Step : the internal steps in the wizard. - * - Finish : the last step that allows user interaction. - * - Complete : the step that shows a summary to user (no interaction is allowed). - * - Auto : the step type is determined by wizard automatically. - * At any time, only one step is visible to end-users, which can be obtained - * by {@link getActiveStep ActiveStep}. Its index in the step collection is given by - * {@link getActiveStepIndex ActiveStepIndex}. - * - * Wizard content can be customized in many ways. - * - * The layout of a wizard consists of four parts: header, step content, navigation - * and side bar. Their content are affected by the following properties, respectively, - * - header: {@link setHeaderText HeaderText} and {@link setHeaderTemplate HeaderTemplate}. - * If both are present, the latter takes precedence. - * - step: {@link getWizardSteps WizardSteps}. - * - navigation: {@link setStartNavigationTemplate StartNavigationTemplate}, - * {@link setStepNavigationTemplate StepNavigationTemplate}, - * {@link setFinishNavigationTemplate FinishNavigationTemplate}. - * Default templates will be used if above templates are not set. - * - side bar: {@link setSideBarTemplate SideBarTemplate}. - * A default template will be used if this template is not set. - * Its visibility is toggled by {@link setShowSideBar ShowSideBar}. - * - * The style of these wizard layout components can be customized via the following style properties, - * - header: {@link getHeaderStyle HeaderStyle}. - * - step: {@link getStepStyle StepStyle}. - * - navigation: {@link getNavigationStyle NavigationStyle}, - * {@link getStartNextButtonStyle StartNextButtonStyle}, - * {@link getStepNextButtonStyle StepNextButtonStyle}, - * {@link getStepPreviousButtonStyle StepPreviousButtonStyle}, - * {@link getFinishPreviousButtonStyle FinishPreviousButtonStyle}, - * {@link getFinishCompleteButtonStyle FinishCompleteButtonStyle}, - * {@link getCancelButtonStyle CancelButtonStyle}. - * - side bar: {@link getSideBarStyle SideBarStyle} and {@link getSideBarButtonStyle SideBarButtonStyle}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizard extends TWebControl implements INamingContainer -{ - /** - * Wizard step types. - * @deprecated deprecated since version 3.0.4 (use TWizardStepType constants instead) - */ - const ST_AUTO='Auto'; - const ST_START='Start'; - const ST_STEP='Step'; - const ST_FINISH='Finish'; - const ST_COMPLETE='Complete'; - /** - * Navigation commands. - */ - const CMD_PREVIOUS='PreviousStep'; - const CMD_NEXT='NextStep'; - const CMD_CANCEL='Cancel'; - const CMD_COMPLETE='Complete'; - const CMD_MOVETO='MoveTo'; - /** - * Side bar button ID - */ - const ID_SIDEBAR_BUTTON='SideBarButton'; - /** - * Side bar data list - */ - const ID_SIDEBAR_LIST='SideBarList'; - - /** - * @var TMultiView multiview that contains the wizard steps - */ - private $_multiView=null; - /** - * @var mixed navigation template for the start step. - */ - private $_startNavigationTemplate=null; - /** - * @var mixed navigation template for internal steps. - */ - private $_stepNavigationTemplate=null; - /** - * @var mixed navigation template for the finish step. - */ - private $_finishNavigationTemplate=null; - /** - * @var mixed template for wizard header. - */ - private $_headerTemplate=null; - /** - * @var mixed template for the side bar. - */ - private $_sideBarTemplate=null; - /** - * @var TWizardStepCollection - */ - private $_wizardSteps=null; - /** - * @var TPanel container of the wizard header - */ - private $_header; - /** - * @var TPanel container of the wizard step content - */ - private $_stepContent; - /** - * @var TPanel container of the wizard side bar - */ - private $_sideBar; - /** - * @var TPanel navigation panel - */ - private $_navigation; - /** - * @var TWizardNavigationContainer container of the start navigation - */ - private $_startNavigation; - /** - * @var TWizardNavigationContainer container of the step navigation - */ - private $_stepNavigation; - /** - * @var TWizardNavigationContainer container of the finish navigation - */ - private $_finishNavigation; - /** - * @var boolean whether ActiveStepIndex was already set - */ - private $_activeStepIndexSet=false; - /** - * @var TDataList side bar data list. - */ - private $_sideBarDataList; - /** - * @var boolean whether navigation should be cancelled (a status set in OnSideBarButtonClick) - */ - private $_cancelNavigation=false; - - /** - * @return string tag name for the wizard - */ - protected function getTagName() - { - return 'div'; - } - - /** - * Adds {@link TWizardStep} objects into step collection. - * This method overrides the parent implementation and is - * invoked when template is being instantiated. - * @param mixed object instantiated in template - */ - public function addParsedObject($object) - { - if($object instanceof TWizardStep) - $this->getWizardSteps()->add($object); - } - - /** - * @return TWizardStep the currently active wizard step - */ - public function getActiveStep() - { - return $this->getMultiView()->getActiveView(); - } - - /** - * @param TWizardStep step to be activated - * @throws TInvalidOperationException if the step is not in the wizard step collection - */ - public function setActiveStep($step) - { - if(($index=$this->getWizardSteps()->indexOf($step))<0) - throw new TInvalidOperationException('wizard_step_invalid'); - $this->setActiveStepIndex($index); - } - - /** - * @return integer the zero-based index of the active wizard step - */ - public function getActiveStepIndex() - { - return $this->getMultiView()->getActiveViewIndex(); - } - - /** - * @param integer the zero-based index of the wizard step to be activated - */ - public function setActiveStepIndex($value) - { - $value=TPropertyValue::ensureInteger($value); - $multiView=$this->getMultiView(); - if($multiView->getActiveViewIndex()!==$value) - { - $multiView->setActiveViewIndex($value); - $this->_activeStepIndexSet=true; - if($this->_sideBarDataList!==null && $this->getSideBarTemplate()!==null) - { - $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); - $this->_sideBarDataList->dataBind(); - } - } - } - - /** - * @return TWizardStepCollection collection of wizard steps - */ - public function getWizardSteps() - { - if($this->_wizardSteps===null) - $this->_wizardSteps=new TWizardStepCollection($this); - return $this->_wizardSteps; - } - - /** - * @return boolean whether to display a cancel button in each wizard step. Defaults to false. - */ - public function getShowCancelButton() - { - return $this->getViewState('ShowCancelButton',false); - } - - /** - * @param boolean whether to display a cancel button in each wizard step. - */ - public function setShowCancelButton($value) - { - $this->setViewState('ShowCancelButton',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return boolean whether to display a side bar that contains links to wizard steps. Defaults to true. - */ - public function getShowSideBar() - { - return $this->getViewState('ShowSideBar',true); - } - - /** - * @param boolean whether to display a side bar that contains links to wizard steps. - */ - public function setShowSideBar($value) - { - $this->setViewState('ShowSideBar',TPropertyValue::ensureBoolean($value),true); - $this->requiresControlsRecreation(); - } - - /** - * @return ITemplate navigation template for the start step. Defaults to null. - */ - public function getStartNavigationTemplate() - { - return $this->_startNavigationTemplate; - } - - /** - * @param ITemplate navigation template for the start step. - */ - public function setStartNavigationTemplate($value) - { - $this->_startNavigationTemplate=$value; - $this->requiresControlsRecreation(); - } - - /** - * @return ITemplate navigation template for internal steps. Defaults to null. - */ - public function getStepNavigationTemplate() - { - return $this->_stepNavigationTemplate; - } - - /** - * @param ITemplate navigation template for internal steps. - */ - public function setStepNavigationTemplate($value) - { - $this->_stepNavigationTemplate=$value; - $this->requiresControlsRecreation(); - } - - /** - * @return ITemplate navigation template for the finish step. Defaults to null. - */ - public function getFinishNavigationTemplate() - { - return $this->_finishNavigationTemplate; - } - - /** - * @param ITemplate navigation template for the finish step. - */ - public function setFinishNavigationTemplate($value) - { - $this->_finishNavigationTemplate=$value; - $this->requiresControlsRecreation(); - } - - /** - * @return ITemplate template for wizard header. Defaults to null. - */ - public function getHeaderTemplate() - { - return $this->_headerTemplate; - } - - /** - * @param ITemplate template for wizard header. - */ - public function setHeaderTemplate($value) - { - $this->_headerTemplate=$value; - $this->requiresControlsRecreation(); - } - - /** - * @return ITemplate template for the side bar. Defaults to null. - */ - public function getSideBarTemplate() - { - return $this->_sideBarTemplate; - } - - /** - * @param ITemplate template for the side bar. - */ - public function setSideBarTemplate($value) - { - $this->_sideBarTemplate=$value; - $this->requiresControlsRecreation(); - } - - /** - * @return string header text. Defaults to ''. - */ - public function getHeaderText() - { - return $this->getViewState('HeaderText',''); - } - - /** - * @param string header text. - */ - public function setHeaderText($value) - { - $this->setViewState('HeaderText',TPropertyValue::ensureString($value),''); - } - - /** - * @return string the URL that the browser will be redirected to if the cancel button in the - * wizard is clicked. Defaults to ''. - */ - public function getCancelDestinationUrl() - { - return $this->getViewState('CancelDestinationUrl',''); - } - - /** - * @param string the URL that the browser will be redirected to if the cancel button in the - * wizard is clicked. - */ - public function setCancelDestinationUrl($value) - { - $this->setViewState('CancelDestinationUrl',TPropertyValue::ensureString($value),''); - } - - /** - * @return string the URL that the browser will be redirected to if the wizard finishes. - * Defaults to ''. - */ - public function getFinishDestinationUrl() - { - return $this->getViewState('FinishDestinationUrl',''); - } - - /** - * @param string the URL that the browser will be redirected to if the wizard finishes. - */ - public function setFinishDestinationUrl($value) - { - $this->setViewState('FinishDestinationUrl',TPropertyValue::ensureString($value),''); - } - - /** - * @return TStyle the style for the buttons displayed in the side bar. - */ - public function getSideBarButtonStyle() - { - if(($style=$this->getViewState('SideBarButtonStyle',null))===null) - { - $style=new TStyle; - $this->setViewState('SideBarButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TStyle the style common for all navigation buttons. - */ - public function getNavigationButtonStyle() - { - if(($style=$this->getViewState('NavigationButtonStyle',null))===null) - { - $style=new TStyle; - $this->setViewState('NavigationButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step. - */ - public function getStartNextButtonStyle() - { - if(($style=$this->getViewState('StartNextButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Next'); - $this->setViewState('StartNextButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step. - */ - public function getStepNextButtonStyle() - { - if(($style=$this->getViewState('StepNextButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Next'); - $this->setViewState('StepNextButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. - */ - public function getStepPreviousButtonStyle() - { - if(($style=$this->getViewState('StepPreviousButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Previous'); - $this->setViewState('StepPreviousButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step. - */ - public function getFinishCompleteButtonStyle() - { - if(($style=$this->getViewState('FinishCompleteButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Complete'); - $this->setViewState('FinishCompleteButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. - */ - public function getFinishPreviousButtonStyle() - { - if(($style=$this->getViewState('FinishPreviousButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Previous'); - $this->setViewState('FinishPreviousButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TWizardNavigationButtonStyle the style for the cancel button - */ - public function getCancelButtonStyle() - { - if(($style=$this->getViewState('CancelButtonStyle',null))===null) - { - $style=new TWizardNavigationButtonStyle; - $style->setButtonText('Cancel'); - $this->setViewState('CancelButtonStyle',$style,null); - } - return $style; - } - - /** - * @return TPanelStyle the style for the side bar. - */ - public function getSideBarStyle() - { - if(($style=$this->getViewState('SideBarStyle',null))===null) - { - $style=new TPanelStyle; - $this->setViewState('SideBarStyle',$style,null); - } - return $style; - } - - /** - * @return TPanelStyle the style for the header. - */ - public function getHeaderStyle() - { - if(($style=$this->getViewState('HeaderStyle',null))===null) - { - $style=new TPanelStyle; - $this->setViewState('HeaderStyle',$style,null); - } - return $style; - } - - /** - * @return TPanelStyle the style for each internal wizard step. - */ - public function getStepStyle() - { - if(($style=$this->getViewState('StepStyle',null))===null) - { - $style=new TPanelStyle; - $this->setViewState('StepStyle',$style,null); - } - return $style; - } - - /** - * @return TPanelStyle the style for the navigation panel. - */ - public function getNavigationStyle() - { - if(($style=$this->getViewState('NavigationStyle',null))===null) - { - $style=new TPanelStyle; - $this->setViewState('NavigationStyle',$style,null); - } - return $style; - } - - /** - * @return boolean whether to use default layout to arrange side bar and the rest wizard components. Defaults to true. - */ - public function getUseDefaultLayout() - { - return $this->getViewState('UseDefaultLayout',true); - } - - /** - * @param boolean whether to use default layout to arrange side bar and the rest wizard components. - * If true, an HTML table will be used which places the side bar in the left cell - * while the rest components in the right cell. - */ - public function setUseDefaultLayout($value) - { - $this->setViewState('UseDefaultLayout',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return TPanel container of the wizard header - */ - public function getHeader() - { - return $this->_header; - } - - /** - * @return TPanel container of the wizard step content - */ - public function getStepContent() - { - return $this->_stepContent; - } - - /** - * @return TPanel container of the wizard side bar - */ - public function getSideBar() - { - return $this->_sideBar; - } - - /** - * @return TWizardNavigationContainer container of the start navigation - */ - public function getStartNavigation() - { - return $this->_startNavigation; - } - - /** - * @return TWizardNavigationContainer container of the step navigation - */ - public function getStepNavigation() - { - return $this->_stepNavigation; - } - - /** - * @return TWizardNavigationContainer container of the finish navigation - */ - public function getFinishNavigation() - { - return $this->_finishNavigation; - } - - /** - * Raises OnActiveStepChanged event. - * This event is raised when the current visible step is changed in the - * wizard. - * @param TEventParameter event parameter - */ - public function onActiveStepChanged($param) - { - $this->raiseEvent('OnActiveStepChanged',$this,$param); - } - - /** - * Raises OnCancelButtonClick event. - * This event is raised when a cancel navigation button is clicked in the - * current active step. - * @param TEventParameter event parameter - */ - public function onCancelButtonClick($param) - { - $this->raiseEvent('OnCancelButtonClick',$this,$param); - if(($url=$this->getCancelDestinationUrl())!=='') - $this->getResponse()->redirect($url); - } - - /** - * Raises OnCompleteButtonClick event. - * This event is raised when a finish navigation button is clicked in the - * current active step. - * @param TWizardNavigationEventParameter event parameter - */ - public function onCompleteButtonClick($param) - { - $this->raiseEvent('OnCompleteButtonClick',$this,$param); - if(($url=$this->getFinishDestinationUrl())!=='') - $this->getResponse()->redirect($url); - } - - /** - * Raises OnNextButtonClick event. - * This event is raised when a next navigation button is clicked in the - * current active step. - * @param TWizardNavigationEventParameter event parameter - */ - public function onNextButtonClick($param) - { - $this->raiseEvent('OnNextButtonClick',$this,$param); - } - - /** - * Raises OnPreviousButtonClick event. - * This event is raised when a previous navigation button is clicked in the - * current active step. - * @param TWizardNavigationEventParameter event parameter - */ - public function onPreviousButtonClick($param) - { - $this->raiseEvent('OnPreviousButtonClick',$this,$param); - } - - /** - * Raises OnSideBarButtonClick event. - * This event is raised when a link button in the side bar is clicked. - * @param TWizardNavigationEventParameter event parameter - */ - public function onSideBarButtonClick($param) - { - $this->raiseEvent('OnSideBarButtonClick',$this,$param); - } - - /** - * Returns the multiview that holds the wizard steps. - * This method should only be used by control developers. - * @return TMultiView the multiview holding wizard steps - */ - public function getMultiView() - { - if($this->_multiView===null) - { - $this->_multiView=new TMultiView; - $this->_multiView->setID('WizardMultiView'); - $this->_multiView->attachEventHandler('OnActiveViewChanged',array($this,'onActiveStepChanged')); - $this->_multiView->ignoreBubbleEvents(); - } - return $this->_multiView; - } - - /** - * Adds a wizard step to the multiview. - * This method should only be used by control developers. - * It is invoked when a step is added into the step collection of the wizard. - * @param TWizardStep wizard step to be added into multiview. - */ - public function addedWizardStep($step) - { - if(($wizard=$step->getWizard())!==null) - $wizard->getWizardSteps()->remove($step); - $step->setWizard($this); - $this->wizardStepsChanged(); - } - - /** - * Removes a wizard step from the multiview. - * This method should only be used by control developers. - * It is invoked when a step is removed from the step collection of the wizard. - * @param TWizardStep wizard step to be removed from multiview. - */ - public function removedWizardStep($step) - { - $step->setWizard(null); - $this->wizardStepsChanged(); - } - - /** - * Creates the child controls of the wizard. - * This method overrides the parent implementation. - * @param TEventParameter event parameter - */ - public function onInit($param) - { - parent::onInit($param); - $this->ensureChildControls(); - if($this->getActiveStepIndex()<0 && $this->getWizardSteps()->getCount()>0) - $this->setActiveStepIndex(0); - } - - /** - * Saves the current active step index into history. - * This method is invoked by the framework when the control state is being saved. - */ - public function saveState() - { - $index=$this->getActiveStepIndex(); - $history=$this->getHistory(); - if(!$history->getCount() || $history->peek()!==$index) - $history->push($index); - } - - /** - * Indicates the wizard needs to recreate all child controls. - */ - protected function requiresControlsRecreation() - { - if($this->getChildControlsCreated()) - $this->setChildControlsCreated(false); - } - - /** - * Renders the wizard. - * @param THtmlWriter - */ - public function render($writer) - { - $this->ensureChildControls(); - if($this->getHasControls()) - { - if($this->getUseDefaultLayout()) - { - $this->applyControlProperties(); - $this->renderBeginTag($writer); - $writer->write("\n\n
    \n"); - $this->_sideBar->renderControl($writer); - $writer->write("\n\n"); - $this->_header->renderControl($writer); - $this->_stepContent->renderControl($writer); - $this->_navigation->renderControl($writer); - $writer->write("\n
    \n"); - $this->renderEndTag($writer); - } - else - { - $this->applyControlProperties(); - $this->renderBeginTag($writer); - $this->_sideBar->renderControl($writer); - $this->_header->renderControl($writer); - $this->_stepContent->renderControl($writer); - $this->_navigation->renderControl($writer); - $this->renderEndTag($writer); - } - } - } - - /** - * Applies various properties to the components of wizard - */ - protected function applyControlProperties() - { - $this->applyHeaderProperties(); - $this->applySideBarProperties(); - $this->applyStepContentProperties(); - $this->applyNavigationProperties(); - } - - /** - * Applies properties to the wizard header - */ - protected function applyHeaderProperties() - { - if(($style=$this->getViewState('HeaderStyle',null))!==null) - $this->_header->getStyle()->mergeWith($style); - if($this->getHeaderTemplate()===null) - { - $this->_header->getControls()->clear(); - $this->_header->getControls()->add($this->getHeaderText()); - } - } - - /** - * Applies properties to the wizard sidebar - */ - protected function applySideBarProperties() - { - $this->_sideBar->setVisible($this->getShowSideBar()); - if($this->_sideBarDataList!==null && $this->getShowSideBar()) - { - $this->_sideBarDataList->setDataSource($this->getWizardSteps()); - $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); - $this->_sideBarDataList->dataBind(); - if(($style=$this->getViewState('SideBarButtonStyle',null))!==null) - { - foreach($this->_sideBarDataList->getItems() as $item) - { - if(($button=$item->findControl('SideBarButton'))!==null) - $button->getStyle()->mergeWith($style); - } - } - } - if(($style=$this->getViewState('SideBarStyle',null))!==null) - $this->_sideBar->getStyle()->mergeWith($style); - } - - /** - * Applies properties to the wizard step content - */ - protected function applyStepContentProperties() - { - if(($style=$this->getViewState('StepStyle',null))!==null) - $this->_stepContent->getStyle()->mergeWith($style); - } - - /** - * Apply properties to various navigation panels. - */ - protected function applyNavigationProperties() - { - $wizardSteps=$this->getWizardSteps(); - $activeStep=$this->getActiveStep(); - $activeStepIndex=$this->getActiveStepIndex(); - - if(!$this->_navigation) - return; - else if($activeStepIndex<0 || $activeStepIndex>=$wizardSteps->getCount()) - { - $this->_navigation->setVisible(false); - return; - } - - // set visibility of different types of navigation panel - $showStandard=true; - foreach($wizardSteps as $step) - { - if(($step instanceof TTemplatedWizardStep) && ($container=$step->getNavigationContainer())!==null) - { - if($activeStep===$step) - { - $container->setVisible(true); - $showStandard=false; - } - else - $container->setVisible(false); - } - } - $activeStepType=$this->getStepType($activeStep); - if($activeStepType===TWizardStepType::Complete) - { - $this->_sideBar->setVisible(false); - $this->_header->setVisible(false); - } - $this->_startNavigation->setVisible($showStandard && $activeStepType===self::ST_START); - $this->_stepNavigation->setVisible($showStandard && $activeStepType===self::ST_STEP); - $this->_finishNavigation->setVisible($showStandard && $activeStepType===self::ST_FINISH); - - if(($navigationStyle=$this->getViewState('NavigationStyle',null))!==null) - $this->_navigation->getStyle()->mergeWith($navigationStyle); - - $displayCancelButton=$this->getShowCancelButton(); - $cancelButtonStyle=$this->getCancelButtonStyle(); - $buttonStyle=$this->getViewState('NavigationButtonStyle',null); - if($buttonStyle!==null) - $cancelButtonStyle->mergeWith($buttonStyle); - - // apply styles to start navigation buttons - if(($cancelButton=$this->_startNavigation->getCancelButton())!==null) - { - $cancelButton->setVisible($displayCancelButton); - $cancelButtonStyle->apply($cancelButton); - } - if(($button=$this->_startNavigation->getNextButton())!==null) - { - $button->setVisible(true); - $style=$this->getStartNextButtonStyle(); - if($buttonStyle!==null) - $style->mergeWith($buttonStyle); - $style->apply($button); - if($activeStepType===TWizardStepType::Start) - $this->getPage()->getClientScript()->registerDefaultButton($this, $button); - } - - // apply styles to finish navigation buttons - if(($cancelButton=$this->_finishNavigation->getCancelButton())!==null) - { - $cancelButton->setVisible($displayCancelButton); - $cancelButtonStyle->apply($cancelButton); - } - if(($button=$this->_finishNavigation->getPreviousButton())!==null) - { - $button->setVisible($this->allowNavigationToPreviousStep()); - $style=$this->getFinishPreviousButtonStyle(); - if($buttonStyle!==null) - $style->mergeWith($buttonStyle); - $style->apply($button); - } - if(($button=$this->_finishNavigation->getCompleteButton())!==null) - { - $button->setVisible(true); - $style=$this->getFinishCompleteButtonStyle(); - if($buttonStyle!==null) - $style->mergeWith($buttonStyle); - $style->apply($button); - if($activeStepType===TWizardStepType::Finish) - $this->getPage()->getClientScript()->registerDefaultButton($this, $button); - } - - // apply styles to step navigation buttons - if(($cancelButton=$this->_stepNavigation->getCancelButton())!==null) - { - $cancelButton->setVisible($displayCancelButton); - $cancelButtonStyle->apply($cancelButton); - } - if(($button=$this->_stepNavigation->getPreviousButton())!==null) - { - $button->setVisible($this->allowNavigationToPreviousStep()); - $style=$this->getStepPreviousButtonStyle(); - if($buttonStyle!==null) - $style->mergeWith($buttonStyle); - $style->apply($button); - } - if(($button=$this->_stepNavigation->getNextButton())!==null) - { - $button->setVisible(true); - $style=$this->getStepNextButtonStyle(); - if($buttonStyle!==null) - $style->mergeWith($buttonStyle); - $style->apply($button); - if($activeStepType===TWizardStepType::Step) - $this->getPage()->getClientScript()->registerDefaultButton($this, $button); - } - } - - /** - * @return TStack history containing step indexes that were navigated before - */ - protected function getHistory() - { - if(($history=$this->getControlState('History',null))===null) - { - $history=new TStack; - $this->setControlState('History',$history); - } - return $history; - } - - /** - * Determines the type of the specified wizard step. - * @param TWizardStep - * @return TWizardStepType type of the step - */ - protected function getStepType($wizardStep) - { - if(($type=$wizardStep->getStepType())===TWizardStepType::Auto) - { - $steps=$this->getWizardSteps(); - if(($index=$steps->indexOf($wizardStep))>=0) - { - $stepCount=$steps->getCount(); - if($stepCount===1 || ($index<$stepCount-1 && $steps->itemAt($index+1)->getStepType()===TWizardStepType::Complete)) - return TWizardStepType::Finish; - else if($index===0) - return TWizardStepType::Start; - else if($index===$stepCount-1) - return TWizardStepType::Finish; - else - return TWizardStepType::Step; - } - else - return $type; - } - else - return $type; - } - - /** - * Clears up everything within the wizard. - */ - protected function reset() - { - $this->getControls()->clear(); - $this->_header=null; - $this->_stepContent=null; - $this->_sideBar=null; - $this->_sideBarDataList=null; - $this->_navigation=null; - $this->_startNavigation=null; - $this->_stepNavigation=null; - $this->_finishNavigation=null; - } - - /** - * Creates child controls within the wizard - */ - public function createChildControls() - { - $this->reset(); - $this->createSideBar(); - $this->createHeader(); - $this->createStepContent(); - $this->createNavigation(); -// $this->clearChildState(); - } - - /** - * Creates the wizard header. - */ - protected function createHeader() - { - $this->_header=new TPanel; - if(($template=$this->getHeaderTemplate())!==null) - $template->instantiateIn($this->_header); - else - $this->_header->getControls()->add($this->getHeaderText()); - $this->getControls()->add($this->_header); - } - - /** - * Creates the wizard side bar - */ - protected function createSideBar() - { - if($this->getShowSideBar()) - { - if(($template=$this->getSideBarTemplate())===null) - $template=new TWizardSideBarTemplate; - $this->_sideBar=new TPanel; - $template->instantiateIn($this->_sideBar); - $this->getControls()->add($this->_sideBar); - - if(($this->_sideBarDataList=$this->_sideBar->findControl(self::ID_SIDEBAR_LIST))!==null) - { - $this->_sideBarDataList->attachEventHandler('OnItemCommand',array($this,'dataListItemCommand')); - $this->_sideBarDataList->attachEventHandler('OnItemDataBound',array($this,'dataListItemDataBound')); - $this->_sideBarDataList->setDataSource($this->getWizardSteps()); - $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); - $this->_sideBarDataList->dataBind(); - } - } - else - { - $this->_sideBar=new TPanel; - $this->getControls()->add($this->_sideBar); - } - } - - /** - * Event handler for sidebar datalist's OnItemCommand event. - * This method is used internally by wizard. It mainly - * sets the active step index according to the button clicked in the sidebar. - * @param mixed sender of the event - * @param TDataListCommandEventParameter - */ - public function dataListItemCommand($sender,$param) - { - $item=$param->getItem(); - if($param->getCommandName()===self::CMD_MOVETO) - { - $stepIndex=$this->getActiveStepIndex(); - $newStepIndex=TPropertyValue::ensureInteger($param->getCommandParameter()); - $navParam=new TWizardNavigationEventParameter($stepIndex); - $navParam->setNextStepIndex($newStepIndex); - - // if the button clicked causes validation which fails, - // by default we will cancel navigation to the new step - $button=$param->getCommandSource(); - if(($button instanceof IButtonControl) && $button->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) - $navParam->setCancelNavigation(true); - - $this->_activeStepIndexSet=false; - $this->onSideBarButtonClick($navParam); - $this->_cancelNavigation=$navParam->getCancelNavigation(); - if(!$this->_cancelNavigation) - { - if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex)) - $this->setActiveStepIndex($newStepIndex); - } - else - $this->setActiveStepIndex($stepIndex); - } - } - - /** - * Event handler for sidebar datalist's OnItemDataBound event. - * This method is used internally by wizard. It mainly configures - * the buttons in the sidebar datalist. - * @param mixed sender of the event - * @param TDataListItemEventParameter - */ - public function dataListItemDataBound($sender,$param) - { - $item=$param->getItem(); - $itemType=$item->getItemType(); - if($itemType==='Item' || $itemType==='AlternatingItem' || $itemType==='SelectedItem' || $itemType==='EditItem') - { - if(($button=$item->findControl(self::ID_SIDEBAR_BUTTON))!==null) - { - $step=$item->getData(); - if(($this->getStepType($step)===TWizardStepType::Complete)) - $button->setEnabled(false); - if(($title=$step->getTitle())!=='') - $button->setText($title); - else - $button->setText($step->getID(false)); - $index=$this->getWizardSteps()->indexOf($step); - $button->setCommandName(self::CMD_MOVETO); - $button->setCommandParameter("$index"); - } - } - } - - /** - * Creates wizard step content. - */ - protected function createStepContent() - { - foreach($this->getWizardSteps() as $step) - { - if($step instanceof TTemplatedWizardStep) - $step->ensureChildControls(); - } - $multiView=$this->getMultiView(); - $this->_stepContent=new TPanel; - $this->_stepContent->getControls()->add($multiView); - $this->getControls()->add($this->_stepContent); - if($multiView->getViews()->getCount()) - $multiView->setActiveViewIndex(0); - } - - /** - * Creates navigation panel. - */ - protected function createNavigation() - { - $this->_navigation=new TPanel; - $this->getControls()->add($this->_navigation); - $controls=$this->_navigation->getControls(); - foreach($this->getWizardSteps() as $step) - { - if($step instanceof TTemplatedWizardStep) - { - $step->instantiateNavigationTemplate(); - if(($panel=$step->getNavigationContainer())!==null) - $controls->add($panel); - } - } - $this->_startNavigation=$this->createStartNavigation(); - $controls->add($this->_startNavigation); - $this->_stepNavigation=$this->createStepNavigation(); - $controls->add($this->_stepNavigation); - $this->_finishNavigation=$this->createFinishNavigation(); - $controls->add($this->_finishNavigation); - } - - /** - * Creates start navigation panel. - */ - protected function createStartNavigation() - { - if(($template=$this->getStartNavigationTemplate())===null) - $template=new TWizardStartNavigationTemplate($this); - $navigation=new TWizardNavigationContainer; - $template->instantiateIn($navigation); - return $navigation; - } - - /** - * Creates step navigation panel. - */ - protected function createStepNavigation() - { - if(($template=$this->getStepNavigationTemplate())===null) - $template=new TWizardStepNavigationTemplate($this); - $navigation=new TWizardNavigationContainer; - $template->instantiateIn($navigation); - return $navigation; - } - - /** - * Creates finish navigation panel. - */ - protected function createFinishNavigation() - { - if(($template=$this->getFinishNavigationTemplate())===null) - $template=new TWizardFinishNavigationTemplate($this); - $navigation=new TWizardNavigationContainer; - $template->instantiateIn($navigation); - return $navigation; - } - - /** - * Updates the sidebar datalist if any. - * This method is invoked when any wizard step is changed. - */ - public function wizardStepsChanged() - { - if($this->_sideBarDataList!==null) - { - $this->_sideBarDataList->setDataSource($this->getWizardSteps()); - $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); - $this->_sideBarDataList->dataBind(); - } - } - - /** - * Determines the index of the previous step based on history. - * @param boolean whether the first item in the history stack should be popped - * up after calling this method. - */ - protected function getPreviousStepIndex($popStack) - { - $history=$this->getHistory(); - if($history->getCount()>=0) - { - $activeStepIndex=$this->getActiveStepIndex(); - $previousStepIndex=-1; - if($popStack) - { - $previousStepIndex=$history->pop(); - if($activeStepIndex===$previousStepIndex && $history->getCount()>0) - $previousStepIndex=$history->pop(); - } - else - { - $previousStepIndex=$history->peek(); - if($activeStepIndex===$previousStepIndex && $history->getCount()>1) - { - $saveIndex=$history->pop(); - $previousStepIndex=$history->peek(); - $history->push($saveIndex); - } - } - return $activeStepIndex===$previousStepIndex ? -1 : $previousStepIndex; - } - else - return -1; - } - - /** - * @return boolean whether navigation to the previous step is allowed - */ - protected function allowNavigationToPreviousStep() - { - if(($index=$this->getPreviousStepIndex(false))!==-1) - return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); - else - return false; - } - - /** - * @param integer index of the step - * @return boolean whether navigation to the specified step is allowed - */ - protected function allowNavigationToStep($index) - { - if($this->getHistory()->contains($index)) - return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); - else - return true; - } - - /** - * Handles bubbled events. - * This method mainly translate certain command events into - * wizard-specific events. - * @param mixed sender of the original command event - * @param TEventParameter event parameter - * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter - */ - public function bubbleEvent($sender,$param) - { - if($param instanceof TCommandEventParameter) - { - $command=$param->getCommandName(); - if(strcasecmp($command,self::CMD_CANCEL)===0) - { - $this->onCancelButtonClick($param); - return true; - } - - $type=$this->getStepType($this->getActiveStep()); - $index=$this->getActiveStepIndex(); - $navParam=new TWizardNavigationEventParameter($index); - if(($sender instanceof IButtonControl) && $sender->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) - $navParam->setCancelNavigation(true); - - $handled=false; - $movePrev=false; - $this->_activeStepIndexSet=false; - - if(strcasecmp($command,self::CMD_NEXT)===0) - { - if($type!==self::ST_START && $type!==self::ST_STEP) - throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_NEXT); - if($index<$this->getWizardSteps()->getCount()-1) - $navParam->setNextStepIndex($index+1); - $this->onNextButtonClick($navParam); - $handled=true; - } - else if(strcasecmp($command,self::CMD_PREVIOUS)===0) - { - if($type!==self::ST_FINISH && $type!==self::ST_STEP) - throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_PREVIOUS); - $movePrev=true; - if(($prevIndex=$this->getPreviousStepIndex(false))>=0) - $navParam->setNextStepIndex($prevIndex); - $this->onPreviousButtonClick($navParam); - $handled=true; - } - else if(strcasecmp($command,self::CMD_COMPLETE)===0) - { - if($type!==self::ST_FINISH) - throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_COMPLETE); - if($index<$this->getWizardSteps()->getCount()-1) - $navParam->setNextStepIndex($index+1); - $this->onCompleteButtonClick($navParam); - $handled=true; - } - else if(strcasecmp($command,self::CMD_MOVETO)===0) - { - if($this->_cancelNavigation) // may be set in onSideBarButtonClick + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TMultiView'); +Prado::using('System.Web.UI.WebControls.TPanel'); +Prado::using('System.Web.UI.WebControls.TButton'); +Prado::using('System.Web.UI.WebControls.TLinkButton'); +Prado::using('System.Web.UI.WebControls.TImageButton'); +Prado::using('System.Web.UI.WebControls.TDataList'); +Prado::using('System.Web.UI.WebControls.TWizardNavigationButtonStyle'); + +/** + * Class TWizard. + * + * TWizard splits a large form and presents the user with a series of smaller + * forms to complete. TWizard is analogous to the installation wizard commonly + * used to install software in Windows. + * + * The smaller forms are called wizard steps ({@link TWizardStep}, which can be accessed via + * {@link getWizardSteps WizardSteps}. In template, wizard steps can be added + * into a wizard using the following syntax, + * + * + * + * content in step 1, may contain other controls + * + * + * content in step 2, may contain other controls + * + * + * + * + * Each wizard step can be one of the following types: + * - Start : the first step in the wizard. + * - Step : the internal steps in the wizard. + * - Finish : the last step that allows user interaction. + * - Complete : the step that shows a summary to user (no interaction is allowed). + * - Auto : the step type is determined by wizard automatically. + * At any time, only one step is visible to end-users, which can be obtained + * by {@link getActiveStep ActiveStep}. Its index in the step collection is given by + * {@link getActiveStepIndex ActiveStepIndex}. + * + * Wizard content can be customized in many ways. + * + * The layout of a wizard consists of four parts: header, step content, navigation + * and side bar. Their content are affected by the following properties, respectively, + * - header: {@link setHeaderText HeaderText} and {@link setHeaderTemplate HeaderTemplate}. + * If both are present, the latter takes precedence. + * - step: {@link getWizardSteps WizardSteps}. + * - navigation: {@link setStartNavigationTemplate StartNavigationTemplate}, + * {@link setStepNavigationTemplate StepNavigationTemplate}, + * {@link setFinishNavigationTemplate FinishNavigationTemplate}. + * Default templates will be used if above templates are not set. + * - side bar: {@link setSideBarTemplate SideBarTemplate}. + * A default template will be used if this template is not set. + * Its visibility is toggled by {@link setShowSideBar ShowSideBar}. + * + * The style of these wizard layout components can be customized via the following style properties, + * - header: {@link getHeaderStyle HeaderStyle}. + * - step: {@link getStepStyle StepStyle}. + * - navigation: {@link getNavigationStyle NavigationStyle}, + * {@link getStartNextButtonStyle StartNextButtonStyle}, + * {@link getStepNextButtonStyle StepNextButtonStyle}, + * {@link getStepPreviousButtonStyle StepPreviousButtonStyle}, + * {@link getFinishPreviousButtonStyle FinishPreviousButtonStyle}, + * {@link getFinishCompleteButtonStyle FinishCompleteButtonStyle}, + * {@link getCancelButtonStyle CancelButtonStyle}. + * - side bar: {@link getSideBarStyle SideBarStyle} and {@link getSideBarButtonStyle SideBarButtonStyle}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizard extends TWebControl implements INamingContainer +{ + /** + * Wizard step types. + * @deprecated deprecated since version 3.0.4 (use TWizardStepType constants instead) + */ + const ST_AUTO='Auto'; + const ST_START='Start'; + const ST_STEP='Step'; + const ST_FINISH='Finish'; + const ST_COMPLETE='Complete'; + /** + * Navigation commands. + */ + const CMD_PREVIOUS='PreviousStep'; + const CMD_NEXT='NextStep'; + const CMD_CANCEL='Cancel'; + const CMD_COMPLETE='Complete'; + const CMD_MOVETO='MoveTo'; + /** + * Side bar button ID + */ + const ID_SIDEBAR_BUTTON='SideBarButton'; + /** + * Side bar data list + */ + const ID_SIDEBAR_LIST='SideBarList'; + + /** + * @var TMultiView multiview that contains the wizard steps + */ + private $_multiView=null; + /** + * @var mixed navigation template for the start step. + */ + private $_startNavigationTemplate=null; + /** + * @var mixed navigation template for internal steps. + */ + private $_stepNavigationTemplate=null; + /** + * @var mixed navigation template for the finish step. + */ + private $_finishNavigationTemplate=null; + /** + * @var mixed template for wizard header. + */ + private $_headerTemplate=null; + /** + * @var mixed template for the side bar. + */ + private $_sideBarTemplate=null; + /** + * @var TWizardStepCollection + */ + private $_wizardSteps=null; + /** + * @var TPanel container of the wizard header + */ + private $_header; + /** + * @var TPanel container of the wizard step content + */ + private $_stepContent; + /** + * @var TPanel container of the wizard side bar + */ + private $_sideBar; + /** + * @var TPanel navigation panel + */ + private $_navigation; + /** + * @var TWizardNavigationContainer container of the start navigation + */ + private $_startNavigation; + /** + * @var TWizardNavigationContainer container of the step navigation + */ + private $_stepNavigation; + /** + * @var TWizardNavigationContainer container of the finish navigation + */ + private $_finishNavigation; + /** + * @var boolean whether ActiveStepIndex was already set + */ + private $_activeStepIndexSet=false; + /** + * @var TDataList side bar data list. + */ + private $_sideBarDataList; + /** + * @var boolean whether navigation should be cancelled (a status set in OnSideBarButtonClick) + */ + private $_cancelNavigation=false; + + /** + * @return string tag name for the wizard + */ + protected function getTagName() + { + return 'div'; + } + + /** + * Adds {@link TWizardStep} objects into step collection. + * This method overrides the parent implementation and is + * invoked when template is being instantiated. + * @param mixed object instantiated in template + */ + public function addParsedObject($object) + { + if($object instanceof TWizardStep) + $this->getWizardSteps()->add($object); + } + + /** + * @return TWizardStep the currently active wizard step + */ + public function getActiveStep() + { + return $this->getMultiView()->getActiveView(); + } + + /** + * @param TWizardStep step to be activated + * @throws TInvalidOperationException if the step is not in the wizard step collection + */ + public function setActiveStep($step) + { + if(($index=$this->getWizardSteps()->indexOf($step))<0) + throw new TInvalidOperationException('wizard_step_invalid'); + $this->setActiveStepIndex($index); + } + + /** + * @return integer the zero-based index of the active wizard step + */ + public function getActiveStepIndex() + { + return $this->getMultiView()->getActiveViewIndex(); + } + + /** + * @param integer the zero-based index of the wizard step to be activated + */ + public function setActiveStepIndex($value) + { + $value=TPropertyValue::ensureInteger($value); + $multiView=$this->getMultiView(); + if($multiView->getActiveViewIndex()!==$value) + { + $multiView->setActiveViewIndex($value); + $this->_activeStepIndexSet=true; + if($this->_sideBarDataList!==null && $this->getSideBarTemplate()!==null) + { + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + } + + /** + * @return TWizardStepCollection collection of wizard steps + */ + public function getWizardSteps() + { + if($this->_wizardSteps===null) + $this->_wizardSteps=new TWizardStepCollection($this); + return $this->_wizardSteps; + } + + /** + * @return boolean whether to display a cancel button in each wizard step. Defaults to false. + */ + public function getShowCancelButton() + { + return $this->getViewState('ShowCancelButton',false); + } + + /** + * @param boolean whether to display a cancel button in each wizard step. + */ + public function setShowCancelButton($value) + { + $this->setViewState('ShowCancelButton',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether to display a side bar that contains links to wizard steps. Defaults to true. + */ + public function getShowSideBar() + { + return $this->getViewState('ShowSideBar',true); + } + + /** + * @param boolean whether to display a side bar that contains links to wizard steps. + */ + public function setShowSideBar($value) + { + $this->setViewState('ShowSideBar',TPropertyValue::ensureBoolean($value),true); + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate navigation template for the start step. Defaults to null. + */ + public function getStartNavigationTemplate() + { + return $this->_startNavigationTemplate; + } + + /** + * @param ITemplate navigation template for the start step. + */ + public function setStartNavigationTemplate($value) + { + $this->_startNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate navigation template for internal steps. Defaults to null. + */ + public function getStepNavigationTemplate() + { + return $this->_stepNavigationTemplate; + } + + /** + * @param ITemplate navigation template for internal steps. + */ + public function setStepNavigationTemplate($value) + { + $this->_stepNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate navigation template for the finish step. Defaults to null. + */ + public function getFinishNavigationTemplate() + { + return $this->_finishNavigationTemplate; + } + + /** + * @param ITemplate navigation template for the finish step. + */ + public function setFinishNavigationTemplate($value) + { + $this->_finishNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate template for wizard header. Defaults to null. + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate template for wizard header. + */ + public function setHeaderTemplate($value) + { + $this->_headerTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate template for the side bar. Defaults to null. + */ + public function getSideBarTemplate() + { + return $this->_sideBarTemplate; + } + + /** + * @param ITemplate template for the side bar. + */ + public function setSideBarTemplate($value) + { + $this->_sideBarTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return string header text. Defaults to ''. + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * @param string header text. + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the URL that the browser will be redirected to if the cancel button in the + * wizard is clicked. Defaults to ''. + */ + public function getCancelDestinationUrl() + { + return $this->getViewState('CancelDestinationUrl',''); + } + + /** + * @param string the URL that the browser will be redirected to if the cancel button in the + * wizard is clicked. + */ + public function setCancelDestinationUrl($value) + { + $this->setViewState('CancelDestinationUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the URL that the browser will be redirected to if the wizard finishes. + * Defaults to ''. + */ + public function getFinishDestinationUrl() + { + return $this->getViewState('FinishDestinationUrl',''); + } + + /** + * @param string the URL that the browser will be redirected to if the wizard finishes. + */ + public function setFinishDestinationUrl($value) + { + $this->setViewState('FinishDestinationUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return TStyle the style for the buttons displayed in the side bar. + */ + public function getSideBarButtonStyle() + { + if(($style=$this->getViewState('SideBarButtonStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('SideBarButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style common for all navigation buttons. + */ + public function getNavigationButtonStyle() + { + if(($style=$this->getViewState('NavigationButtonStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('NavigationButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step. + */ + public function getStartNextButtonStyle() + { + if(($style=$this->getViewState('StartNextButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Next'); + $this->setViewState('StartNextButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step. + */ + public function getStepNextButtonStyle() + { + if(($style=$this->getViewState('StepNextButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Next'); + $this->setViewState('StepNextButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. + */ + public function getStepPreviousButtonStyle() + { + if(($style=$this->getViewState('StepPreviousButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Previous'); + $this->setViewState('StepPreviousButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step. + */ + public function getFinishCompleteButtonStyle() + { + if(($style=$this->getViewState('FinishCompleteButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Complete'); + $this->setViewState('FinishCompleteButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. + */ + public function getFinishPreviousButtonStyle() + { + if(($style=$this->getViewState('FinishPreviousButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Previous'); + $this->setViewState('FinishPreviousButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the cancel button + */ + public function getCancelButtonStyle() + { + if(($style=$this->getViewState('CancelButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Cancel'); + $this->setViewState('CancelButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the side bar. + */ + public function getSideBarStyle() + { + if(($style=$this->getViewState('SideBarStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('SideBarStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the header. + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for each internal wizard step. + */ + public function getStepStyle() + { + if(($style=$this->getViewState('StepStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('StepStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the navigation panel. + */ + public function getNavigationStyle() + { + if(($style=$this->getViewState('NavigationStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('NavigationStyle',$style,null); + } + return $style; + } + + /** + * @return boolean whether to use default layout to arrange side bar and the rest wizard components. Defaults to true. + */ + public function getUseDefaultLayout() + { + return $this->getViewState('UseDefaultLayout',true); + } + + /** + * @param boolean whether to use default layout to arrange side bar and the rest wizard components. + * If true, an HTML table will be used which places the side bar in the left cell + * while the rest components in the right cell. + */ + public function setUseDefaultLayout($value) + { + $this->setViewState('UseDefaultLayout',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TPanel container of the wizard header + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TPanel container of the wizard step content + */ + public function getStepContent() + { + return $this->_stepContent; + } + + /** + * @return TPanel container of the wizard side bar + */ + public function getSideBar() + { + return $this->_sideBar; + } + + /** + * @return TWizardNavigationContainer container of the start navigation + */ + public function getStartNavigation() + { + return $this->_startNavigation; + } + + /** + * @return TWizardNavigationContainer container of the step navigation + */ + public function getStepNavigation() + { + return $this->_stepNavigation; + } + + /** + * @return TWizardNavigationContainer container of the finish navigation + */ + public function getFinishNavigation() + { + return $this->_finishNavigation; + } + + /** + * Raises OnActiveStepChanged event. + * This event is raised when the current visible step is changed in the + * wizard. + * @param TEventParameter event parameter + */ + public function onActiveStepChanged($param) + { + $this->raiseEvent('OnActiveStepChanged',$this,$param); + } + + /** + * Raises OnCancelButtonClick event. + * This event is raised when a cancel navigation button is clicked in the + * current active step. + * @param TEventParameter event parameter + */ + public function onCancelButtonClick($param) + { + $this->raiseEvent('OnCancelButtonClick',$this,$param); + if(($url=$this->getCancelDestinationUrl())!=='') + $this->getResponse()->redirect($url); + } + + /** + * Raises OnCompleteButtonClick event. + * This event is raised when a finish navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onCompleteButtonClick($param) + { + $this->raiseEvent('OnCompleteButtonClick',$this,$param); + if(($url=$this->getFinishDestinationUrl())!=='') + $this->getResponse()->redirect($url); + } + + /** + * Raises OnNextButtonClick event. + * This event is raised when a next navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onNextButtonClick($param) + { + $this->raiseEvent('OnNextButtonClick',$this,$param); + } + + /** + * Raises OnPreviousButtonClick event. + * This event is raised when a previous navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onPreviousButtonClick($param) + { + $this->raiseEvent('OnPreviousButtonClick',$this,$param); + } + + /** + * Raises OnSideBarButtonClick event. + * This event is raised when a link button in the side bar is clicked. + * @param TWizardNavigationEventParameter event parameter + */ + public function onSideBarButtonClick($param) + { + $this->raiseEvent('OnSideBarButtonClick',$this,$param); + } + + /** + * Returns the multiview that holds the wizard steps. + * This method should only be used by control developers. + * @return TMultiView the multiview holding wizard steps + */ + public function getMultiView() + { + if($this->_multiView===null) + { + $this->_multiView=new TMultiView; + $this->_multiView->setID('WizardMultiView'); + $this->_multiView->attachEventHandler('OnActiveViewChanged',array($this,'onActiveStepChanged')); + $this->_multiView->ignoreBubbleEvents(); + } + return $this->_multiView; + } + + /** + * Adds a wizard step to the multiview. + * This method should only be used by control developers. + * It is invoked when a step is added into the step collection of the wizard. + * @param TWizardStep wizard step to be added into multiview. + */ + public function addedWizardStep($step) + { + if(($wizard=$step->getWizard())!==null) + $wizard->getWizardSteps()->remove($step); + $step->setWizard($this); + $this->wizardStepsChanged(); + } + + /** + * Removes a wizard step from the multiview. + * This method should only be used by control developers. + * It is invoked when a step is removed from the step collection of the wizard. + * @param TWizardStep wizard step to be removed from multiview. + */ + public function removedWizardStep($step) + { + $step->setWizard(null); + $this->wizardStepsChanged(); + } + + /** + * Creates the child controls of the wizard. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->ensureChildControls(); + if($this->getActiveStepIndex()<0 && $this->getWizardSteps()->getCount()>0) + $this->setActiveStepIndex(0); + } + + /** + * Saves the current active step index into history. + * This method is invoked by the framework when the control state is being saved. + */ + public function saveState() + { + $index=$this->getActiveStepIndex(); + $history=$this->getHistory(); + if(!$history->getCount() || $history->peek()!==$index) + $history->push($index); + } + + /** + * Indicates the wizard needs to recreate all child controls. + */ + protected function requiresControlsRecreation() + { + if($this->getChildControlsCreated()) + $this->setChildControlsCreated(false); + } + + /** + * Renders the wizard. + * @param THtmlWriter + */ + public function render($writer) + { + $this->ensureChildControls(); + if($this->getHasControls()) + { + if($this->getUseDefaultLayout()) + { + $this->applyControlProperties(); + $this->renderBeginTag($writer); + $writer->write("\n\n
    \n"); + $this->_sideBar->renderControl($writer); + $writer->write("\n\n"); + $this->_header->renderControl($writer); + $this->_stepContent->renderControl($writer); + $this->_navigation->renderControl($writer); + $writer->write("\n
    \n"); + $this->renderEndTag($writer); + } + else + { + $this->applyControlProperties(); + $this->renderBeginTag($writer); + $this->_sideBar->renderControl($writer); + $this->_header->renderControl($writer); + $this->_stepContent->renderControl($writer); + $this->_navigation->renderControl($writer); + $this->renderEndTag($writer); + } + } + } + + /** + * Applies various properties to the components of wizard + */ + protected function applyControlProperties() + { + $this->applyHeaderProperties(); + $this->applySideBarProperties(); + $this->applyStepContentProperties(); + $this->applyNavigationProperties(); + } + + /** + * Applies properties to the wizard header + */ + protected function applyHeaderProperties() + { + if(($style=$this->getViewState('HeaderStyle',null))!==null) + $this->_header->getStyle()->mergeWith($style); + if($this->getHeaderTemplate()===null) + { + $this->_header->getControls()->clear(); + $this->_header->getControls()->add($this->getHeaderText()); + } + } + + /** + * Applies properties to the wizard sidebar + */ + protected function applySideBarProperties() + { + $this->_sideBar->setVisible($this->getShowSideBar()); + if($this->_sideBarDataList!==null && $this->getShowSideBar()) + { + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + if(($style=$this->getViewState('SideBarButtonStyle',null))!==null) + { + foreach($this->_sideBarDataList->getItems() as $item) + { + if(($button=$item->findControl('SideBarButton'))!==null) + $button->getStyle()->mergeWith($style); + } + } + } + if(($style=$this->getViewState('SideBarStyle',null))!==null) + $this->_sideBar->getStyle()->mergeWith($style); + } + + /** + * Applies properties to the wizard step content + */ + protected function applyStepContentProperties() + { + if(($style=$this->getViewState('StepStyle',null))!==null) + $this->_stepContent->getStyle()->mergeWith($style); + } + + /** + * Apply properties to various navigation panels. + */ + protected function applyNavigationProperties() + { + $wizardSteps=$this->getWizardSteps(); + $activeStep=$this->getActiveStep(); + $activeStepIndex=$this->getActiveStepIndex(); + + if(!$this->_navigation) + return; + else if($activeStepIndex<0 || $activeStepIndex>=$wizardSteps->getCount()) + { + $this->_navigation->setVisible(false); + return; + } + + // set visibility of different types of navigation panel + $showStandard=true; + foreach($wizardSteps as $step) + { + if(($step instanceof TTemplatedWizardStep) && ($container=$step->getNavigationContainer())!==null) + { + if($activeStep===$step) + { + $container->setVisible(true); + $showStandard=false; + } + else + $container->setVisible(false); + } + } + $activeStepType=$this->getStepType($activeStep); + if($activeStepType===TWizardStepType::Complete) + { + $this->_sideBar->setVisible(false); + $this->_header->setVisible(false); + } + $this->_startNavigation->setVisible($showStandard && $activeStepType===self::ST_START); + $this->_stepNavigation->setVisible($showStandard && $activeStepType===self::ST_STEP); + $this->_finishNavigation->setVisible($showStandard && $activeStepType===self::ST_FINISH); + + if(($navigationStyle=$this->getViewState('NavigationStyle',null))!==null) + $this->_navigation->getStyle()->mergeWith($navigationStyle); + + $displayCancelButton=$this->getShowCancelButton(); + $cancelButtonStyle=$this->getCancelButtonStyle(); + $buttonStyle=$this->getViewState('NavigationButtonStyle',null); + if($buttonStyle!==null) + $cancelButtonStyle->mergeWith($buttonStyle); + + // apply styles to start navigation buttons + if(($cancelButton=$this->_startNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_startNavigation->getNextButton())!==null) + { + $button->setVisible(true); + $style=$this->getStartNextButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Start) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + + // apply styles to finish navigation buttons + if(($cancelButton=$this->_finishNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_finishNavigation->getPreviousButton())!==null) + { + $button->setVisible($this->allowNavigationToPreviousStep()); + $style=$this->getFinishPreviousButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + } + if(($button=$this->_finishNavigation->getCompleteButton())!==null) + { + $button->setVisible(true); + $style=$this->getFinishCompleteButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Finish) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + + // apply styles to step navigation buttons + if(($cancelButton=$this->_stepNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_stepNavigation->getPreviousButton())!==null) + { + $button->setVisible($this->allowNavigationToPreviousStep()); + $style=$this->getStepPreviousButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + } + if(($button=$this->_stepNavigation->getNextButton())!==null) + { + $button->setVisible(true); + $style=$this->getStepNextButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Step) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + } + + /** + * @return TStack history containing step indexes that were navigated before + */ + protected function getHistory() + { + if(($history=$this->getControlState('History',null))===null) + { + $history=new TStack; + $this->setControlState('History',$history); + } + return $history; + } + + /** + * Determines the type of the specified wizard step. + * @param TWizardStep + * @return TWizardStepType type of the step + */ + protected function getStepType($wizardStep) + { + if(($type=$wizardStep->getStepType())===TWizardStepType::Auto) + { + $steps=$this->getWizardSteps(); + if(($index=$steps->indexOf($wizardStep))>=0) + { + $stepCount=$steps->getCount(); + if($stepCount===1 || ($index<$stepCount-1 && $steps->itemAt($index+1)->getStepType()===TWizardStepType::Complete)) + return TWizardStepType::Finish; + else if($index===0) + return TWizardStepType::Start; + else if($index===$stepCount-1) + return TWizardStepType::Finish; + else + return TWizardStepType::Step; + } + else + return $type; + } + else + return $type; + } + + /** + * Clears up everything within the wizard. + */ + protected function reset() + { + $this->getControls()->clear(); + $this->_header=null; + $this->_stepContent=null; + $this->_sideBar=null; + $this->_sideBarDataList=null; + $this->_navigation=null; + $this->_startNavigation=null; + $this->_stepNavigation=null; + $this->_finishNavigation=null; + } + + /** + * Creates child controls within the wizard + */ + public function createChildControls() + { + $this->reset(); + $this->createSideBar(); + $this->createHeader(); + $this->createStepContent(); + $this->createNavigation(); +// $this->clearChildState(); + } + + /** + * Creates the wizard header. + */ + protected function createHeader() + { + $this->_header=new TPanel; + if(($template=$this->getHeaderTemplate())!==null) + $template->instantiateIn($this->_header); + else + $this->_header->getControls()->add($this->getHeaderText()); + $this->getControls()->add($this->_header); + } + + /** + * Creates the wizard side bar + */ + protected function createSideBar() + { + if($this->getShowSideBar()) + { + if(($template=$this->getSideBarTemplate())===null) + $template=new TWizardSideBarTemplate; + $this->_sideBar=new TPanel; + $template->instantiateIn($this->_sideBar); + $this->getControls()->add($this->_sideBar); + + if(($this->_sideBarDataList=$this->_sideBar->findControl(self::ID_SIDEBAR_LIST))!==null) + { + $this->_sideBarDataList->attachEventHandler('OnItemCommand',array($this,'dataListItemCommand')); + $this->_sideBarDataList->attachEventHandler('OnItemDataBound',array($this,'dataListItemDataBound')); + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + else + { + $this->_sideBar=new TPanel; + $this->getControls()->add($this->_sideBar); + } + } + + /** + * Event handler for sidebar datalist's OnItemCommand event. + * This method is used internally by wizard. It mainly + * sets the active step index according to the button clicked in the sidebar. + * @param mixed sender of the event + * @param TDataListCommandEventParameter + */ + public function dataListItemCommand($sender,$param) + { + $item=$param->getItem(); + if($param->getCommandName()===self::CMD_MOVETO) + { + $stepIndex=$this->getActiveStepIndex(); + $newStepIndex=TPropertyValue::ensureInteger($param->getCommandParameter()); + $navParam=new TWizardNavigationEventParameter($stepIndex); + $navParam->setNextStepIndex($newStepIndex); + + // if the button clicked causes validation which fails, + // by default we will cancel navigation to the new step + $button=$param->getCommandSource(); + if(($button instanceof IButtonControl) && $button->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) + $navParam->setCancelNavigation(true); + + $this->_activeStepIndexSet=false; + $this->onSideBarButtonClick($navParam); + $this->_cancelNavigation=$navParam->getCancelNavigation(); + if(!$this->_cancelNavigation) + { + if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex)) + $this->setActiveStepIndex($newStepIndex); + } + else + $this->setActiveStepIndex($stepIndex); + } + } + + /** + * Event handler for sidebar datalist's OnItemDataBound event. + * This method is used internally by wizard. It mainly configures + * the buttons in the sidebar datalist. + * @param mixed sender of the event + * @param TDataListItemEventParameter + */ + public function dataListItemDataBound($sender,$param) + { + $item=$param->getItem(); + $itemType=$item->getItemType(); + if($itemType==='Item' || $itemType==='AlternatingItem' || $itemType==='SelectedItem' || $itemType==='EditItem') + { + if(($button=$item->findControl(self::ID_SIDEBAR_BUTTON))!==null) + { + $step=$item->getData(); + if(($this->getStepType($step)===TWizardStepType::Complete)) + $button->setEnabled(false); + if(($title=$step->getTitle())!=='') + $button->setText($title); + else + $button->setText($step->getID(false)); + $index=$this->getWizardSteps()->indexOf($step); + $button->setCommandName(self::CMD_MOVETO); + $button->setCommandParameter("$index"); + } + } + } + + /** + * Creates wizard step content. + */ + protected function createStepContent() + { + foreach($this->getWizardSteps() as $step) + { + if($step instanceof TTemplatedWizardStep) + $step->ensureChildControls(); + } + $multiView=$this->getMultiView(); + $this->_stepContent=new TPanel; + $this->_stepContent->getControls()->add($multiView); + $this->getControls()->add($this->_stepContent); + if($multiView->getViews()->getCount()) + $multiView->setActiveViewIndex(0); + } + + /** + * Creates navigation panel. + */ + protected function createNavigation() + { + $this->_navigation=new TPanel; + $this->getControls()->add($this->_navigation); + $controls=$this->_navigation->getControls(); + foreach($this->getWizardSteps() as $step) + { + if($step instanceof TTemplatedWizardStep) + { + $step->instantiateNavigationTemplate(); + if(($panel=$step->getNavigationContainer())!==null) + $controls->add($panel); + } + } + $this->_startNavigation=$this->createStartNavigation(); + $controls->add($this->_startNavigation); + $this->_stepNavigation=$this->createStepNavigation(); + $controls->add($this->_stepNavigation); + $this->_finishNavigation=$this->createFinishNavigation(); + $controls->add($this->_finishNavigation); + } + + /** + * Creates start navigation panel. + */ + protected function createStartNavigation() + { + if(($template=$this->getStartNavigationTemplate())===null) + $template=new TWizardStartNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Creates step navigation panel. + */ + protected function createStepNavigation() + { + if(($template=$this->getStepNavigationTemplate())===null) + $template=new TWizardStepNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Creates finish navigation panel. + */ + protected function createFinishNavigation() + { + if(($template=$this->getFinishNavigationTemplate())===null) + $template=new TWizardFinishNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Updates the sidebar datalist if any. + * This method is invoked when any wizard step is changed. + */ + public function wizardStepsChanged() + { + if($this->_sideBarDataList!==null) + { + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + + /** + * Determines the index of the previous step based on history. + * @param boolean whether the first item in the history stack should be popped + * up after calling this method. + */ + protected function getPreviousStepIndex($popStack) + { + $history=$this->getHistory(); + if($history->getCount()>=0) + { + $activeStepIndex=$this->getActiveStepIndex(); + $previousStepIndex=-1; + if($popStack) + { + $previousStepIndex=$history->pop(); + if($activeStepIndex===$previousStepIndex && $history->getCount()>0) + $previousStepIndex=$history->pop(); + } + else + { + $previousStepIndex=$history->peek(); + if($activeStepIndex===$previousStepIndex && $history->getCount()>1) + { + $saveIndex=$history->pop(); + $previousStepIndex=$history->peek(); + $history->push($saveIndex); + } + } + return $activeStepIndex===$previousStepIndex ? -1 : $previousStepIndex; + } + else + return -1; + } + + /** + * @return boolean whether navigation to the previous step is allowed + */ + protected function allowNavigationToPreviousStep() + { + if(($index=$this->getPreviousStepIndex(false))!==-1) + return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); + else + return false; + } + + /** + * @param integer index of the step + * @return boolean whether navigation to the specified step is allowed + */ + protected function allowNavigationToStep($index) + { + if($this->getHistory()->contains($index)) + return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); + else + return true; + } + + /** + * Handles bubbled events. + * This method mainly translate certain command events into + * wizard-specific events. + * @param mixed sender of the original command event + * @param TEventParameter event parameter + * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelButtonClick($param); + return true; + } + + $type=$this->getStepType($this->getActiveStep()); + $index=$this->getActiveStepIndex(); + $navParam=new TWizardNavigationEventParameter($index); + if(($sender instanceof IButtonControl) && $sender->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) + $navParam->setCancelNavigation(true); + + $handled=false; + $movePrev=false; + $this->_activeStepIndexSet=false; + + if(strcasecmp($command,self::CMD_NEXT)===0) + { + if($type!==self::ST_START && $type!==self::ST_STEP) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_NEXT); + if($index<$this->getWizardSteps()->getCount()-1) + $navParam->setNextStepIndex($index+1); + $this->onNextButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_PREVIOUS)===0) + { + if($type!==self::ST_FINISH && $type!==self::ST_STEP) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_PREVIOUS); + $movePrev=true; + if(($prevIndex=$this->getPreviousStepIndex(false))>=0) + $navParam->setNextStepIndex($prevIndex); + $this->onPreviousButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_COMPLETE)===0) + { + if($type!==self::ST_FINISH) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_COMPLETE); + if($index<$this->getWizardSteps()->getCount()-1) + $navParam->setNextStepIndex($index+1); + $this->onCompleteButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_MOVETO)===0) + { + if($this->_cancelNavigation) // may be set in onSideBarButtonClick $navParam->setCancelNavigation(true); $requestedStep=$param->getCommandParameter(); if (!is_numeric($requestedStep)) @@ -1412,750 +1412,750 @@ class TWizard extends TWebControl implements INamingContainer throw new TConfigurationException('wizard_step_invalid'); } else - $requestedIndex=TPropertyValue::ensureInteger($requestedStep); - $navParam->setNextStepIndex($requestedIndex); - $handled=true; - } - - if($handled) - { - if(!$navParam->getCancelNavigation()) - { - $nextStepIndex=$navParam->getNextStepIndex(); - if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex)) - { - if($movePrev) - $this->getPreviousStepIndex(true); // pop out the previous move from history - $this->setActiveStepIndex($nextStepIndex); - } - } - else - $this->setActiveStepIndex($index); - return true; - } - } - return false; - } -} - - -/** - * TWizardStep class. - * - * TWizardStep represents a wizard step. The wizard owning the step - * can be obtained by {@link getWizard Wizard}. - * To specify the type of the step, set {@link setStepType StepType}; - * For step title, set {@link setTitle Title}. If a step can be re-visited, - * set {@link setAllowReturn AllowReturn} to true. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardStep extends TView -{ - private $_wizard; - - /** - * @return TWizard the wizard owning this step - */ - public function getWizard() - { - return $this->_wizard; - } - - /** - * Sets the wizard owning this step. - * This method is used internally by {@link TWizard}. - * @param TWizard the wizard owning this step - */ - public function setWizard($wizard) - { - $this->_wizard=$wizard; - } - - /** - * @return string the title for this step. - */ - public function getTitle() - { - return $this->getViewState('Title',''); - } - - /** - * @param string the title for this step. - */ - public function setTitle($value) - { - $this->setViewState('Title',$value,''); - if($this->_wizard) - $this->_wizard->wizardStepsChanged(); - } - - /** - * @return boolean whether this step can be re-visited. Default to true. - */ - public function getAllowReturn() - { - return $this->getViewState('AllowReturn',true); - } - - /** - * @param boolean whether this step can be re-visited. - */ - public function setAllowReturn($value) - { - $this->setViewState('AllowReturn',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return TWizardStepType the wizard step type. Defaults to TWizardStepType::Auto. - */ - public function getStepType() - { - return $this->getViewState('StepType',TWizardStepType::Auto); - } - - /** - * @param TWizardStepType the wizard step type. - */ - public function setStepType($type) - { - $type=TPropertyValue::ensureEnum($type,'TWizardStepType'); - if($type!==$this->getStepType()) - { - $this->setViewState('StepType',$type,TWizardStepType::Auto); - if($this->_wizard) - $this->_wizard->wizardStepsChanged(); - } - } -} - - -/** - * TCompleteWizardStep class. - * - * TCompleteWizardStep represents a wizard step of type TWizardStepType::Complete. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TCompleteWizardStep extends TWizardStep -{ - /** - * @return TWizardStepType the wizard step type. Always TWizardStepType::Complete. - */ - public function getStepType() - { - return TWizardStepType::Complete; - } - - /** - * @param string the wizard step type. - * @throws TInvalidOperationException whenever this method is invoked. - */ - public function setStepType($value) - { - throw new TInvalidOperationException('completewizardstep_steptype_readonly'); - } -} - - -/** - * TTemplatedWizardStep class. - * - * TTemplatedWizardStep represents a wizard step whose content and navigation - * can be customized using templates. To customize the step content, specify - * {@link setContentTemplate ContentTemplate}. To customize navigation specific - * to the step, specify {@link setNavigationTemplate NavigationTemplate}. Note, - * if the navigation template is not specified, default navigation will be used. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TTemplatedWizardStep extends TWizardStep implements INamingContainer -{ - /** - * @var ITemplate the template for displaying the navigation UI of a wizard step. - */ - private $_navigationTemplate=null; - /** - * @var ITemplate the template for displaying the content within the wizard step. - */ - private $_contentTemplate=null; - /** - * @var TWizardNavigationContainer - */ - private $_navigationContainer=null; - - /** - * Creates child controls. - * This method mainly instantiates the content template, if any. - */ - public function createChildControls() - { - $this->getControls()->clear(); - if($this->_contentTemplate) - $this->_contentTemplate->instantiateIn($this); - } - - /** - * Ensures child controls are created. - * @param mixed event parameter - */ - public function onInit($param) - { - parent::onInit($param); - $this->ensureChildControls(); - } - - /** - * @return ITemplate the template for the content of the wizard step. - */ - public function getContentTemplate() - { - return $this->_contentTemplate; - } - - /** - * @param ITemplate the template for the content of the wizard step. - */ - public function setContentTemplate($value) - { - $this->_contentTemplate=$value; - } - - /** - * @return ITemplate the template for displaying the navigation UI of a wizard step. Defaults to null. - */ - public function getNavigationTemplate() - { - return $this->_navigationTemplate; - } - - /** - * @param ITemplate the template for displaying the navigation UI of a wizard step. - */ - public function setNavigationTemplate($value) - { - $this->_navigationTemplate=$value; - } - - /** - * @return TWizardNavigationContainer the control containing the navigation. - * It could be null if no navigation template is specified. - */ - public function getNavigationContainer() - { - return $this->_navigationContainer; - } - - /** - * Instantiates the navigation template if any - */ - public function instantiateNavigationTemplate() - { - if(!$this->_navigationContainer && $this->_navigationTemplate) - { - $this->_navigationContainer=new TWizardNavigationContainer; - $this->_navigationTemplate->instantiateIn($this->_navigationContainer); - } - } -} - - -/** - * TWizardStepCollection class. - * - * TWizardStepCollection represents the collection of wizard steps owned - * by a {@link TWizard}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardStepCollection extends TList -{ - /** - * @var TWizard - */ - private $_wizard; - - /** - * Constructor. - * @param TWizard wizard that owns this collection - */ - public function __construct(TWizard $wizard) - { - $this->_wizard=$wizard; - } - - /** - * Inserts an item at the specified position. - * This method overrides the parent implementation by checking if - * the item being added is a {@link TWizardStep}. - * @param integer the speicified position. - * @param mixed new item - * @throws TInvalidDataTypeException if the item being added is not TWizardStep. - */ - public function insertAt($index,$item) - { - if($item instanceof TWizardStep) - { - parent::insertAt($index,$item); - $this->_wizard->getMultiView()->getViews()->insertAt($index,$item); - $this->_wizard->addedWizardStep($item); - } - else - throw new TInvalidDataTypeException('wizardstepcollection_wizardstep_required'); - } - - /** - * Removes an item at the specified position. - * @param integer the index of the item to be removed. - * @return mixed the removed item. - */ - public function removeAt($index) - { - $step=parent::removeAt($index); - $this->_wizard->getMultiView()->getViews()->remove($step); - $this->_wizard->removedWizardStep($step); - return $step; - } -} - - -/** - * TWizardNavigationContainer class. - * - * TWizardNavigationContainer represents a control containing - * a wizard navigation. The navigation may contain a few buttons, including - * {@link getPreviousButton PreviousButton}, {@link getNextButton NextButton}, - * {@link getCancelButton CancelButton}, {@link getCompleteButton CompleteButton}. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardNavigationContainer extends TControl implements INamingContainer -{ - private $_previousButton=null; - private $_nextButton=null; - private $_cancelButton=null; - private $_completeButton=null; - - /** - * @return mixed the previous button - */ - public function getPreviousButton() - { - return $this->_previousButton; - } - - /** - * @param mixed the previous button - */ - public function setPreviousButton($value) - { - $this->_previousButton=$value; - } - - /** - * @return mixed the next button - */ - public function getNextButton() - { - return $this->_nextButton; - } - - /** - * @param mixed the next button - */ - public function setNextButton($value) - { - $this->_nextButton=$value; - } - - /** - * @return mixed the cancel button - */ - public function getCancelButton() - { - return $this->_cancelButton; - } - - /** - * @param mixed the cancel button - */ - public function setCancelButton($value) - { - $this->_cancelButton=$value; - } - - /** - * @return mixed the complete button - */ - public function getCompleteButton() - { - return $this->_completeButton; - } - - /** - * @param mixed the complete button - */ - public function setCompleteButton($value) - { - $this->_completeButton=$value; - } -} - - -/** - * TWizardNavigationEventParameter class. - * - * TWizardNavigationEventParameter represents the parameter for - * {@link TWizard}'s navigation events. - * - * The index of the currently active step can be obtained from - * {@link getCurrentStepIndex CurrentStepIndex}, while the index - * of the candidate new step is in {@link getNextStepIndex NextStepIndex}. - * By modifying {@link setNextStepIndex NextStepIndex}, the new step - * can be changed to another one. If there is anything wrong with - * the navigation and it is not wanted, set {@link setCancelNavigation CancelNavigation} - * to true. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardNavigationEventParameter extends TEventParameter -{ - private $_cancel=false; - private $_currentStep; - private $_nextStep; - - /** - * Constructor. - * @param integer current step index - */ - public function __construct($currentStep) - { - $this->_currentStep=$currentStep; - $this->_nextStep=$currentStep; - } - - /** - * @return integer the zero-based index of the currently active step. - */ - public function getCurrentStepIndex() - { - return $this->_currentStep; - } - - /** - * @return integer the zero-based index of the next step. Default to {@link getCurrentStepIndex CurrentStepIndex}. - */ - public function getNextStepIndex() - { - return $this->_nextStep; - } - - /** - * @param integer the zero-based index of the next step. - */ - public function setNextStepIndex($index) - { - $this->_nextStep=TPropertyValue::ensureInteger($index); - } - - /** - * @return boolean whether navigation to the next step should be canceled. Default to false. - */ - public function getCancelNavigation() - { - return $this->_cancel; - } - - /** - * @param boolean whether navigation to the next step should be canceled. - */ - public function setCancelNavigation($value) - { - $this->_cancel=TPropertyValue::ensureBoolean($value); - } -} - -/** - * TWizardSideBarTemplate class. - * TWizardSideBarTemplate is the default template for wizard sidebar. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardSideBarTemplate extends TComponent implements ITemplate -{ - /** - * Instantiates the template. - * It creates a {@link TDataList} control. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $dataList=new TDataList; - $dataList->setID(TWizard::ID_SIDEBAR_LIST); - $dataList->getSelectedItemStyle()->getFont()->setBold(true); - $dataList->setItemTemplate(new TWizardSideBarListItemTemplate); - $parent->getControls()->add($dataList); - } -} - -/** - * TWizardSideBarListItemTemplate class. - * TWizardSideBarListItemTemplate is the default template for each item in the sidebar datalist. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardSideBarListItemTemplate extends TComponent implements ITemplate -{ - /** - * Instantiates the template. - * It creates a {@link TLinkButton}. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $button=new TLinkButton; - $button->setID(TWizard::ID_SIDEBAR_BUTTON); - $parent->getControls()->add($button); - } -} - -/** - * TWizardNavigationTemplate class. - * TWizardNavigationTemplate is the base class for various navigation templates. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardNavigationTemplate extends TComponent implements ITemplate -{ - private $_wizard; - - /** - * Constructor. - * @param TWizard the wizard owning this template - */ - public function __construct($wizard) - { - $this->_wizard=$wizard; - } - - /** - * @return TWizard the wizard owning this template - */ - public function getWizard() - { - return $this->_wizard; - } - - /** - * Instantiates the template. - * Derived classes should override this method. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - } - - /** - * Creates a navigation button. - * It creates a {@link TButton}, {@link TLinkButton}, or {@link TImageButton}, - * depending on the given parameters. - * @param TWizardNavigationButtonStyle button style - * @param boolean whether the button should cause validation - * @param string command name for the button's OnCommand event - * @throws TInvalidDataValueException if the button type is not recognized - */ - protected function createNavigationButton($buttonStyle,$causesValidation,$commandName) - { - switch($buttonStyle->getButtonType()) - { - case TWizardNavigationButtonType::Button: - $button=new TButton; - break; - case TWizardNavigationButtonType::Link: - $button=new TLinkButton; - break; - case TWizardNavigationButtonType::Image: - $button=new TImageButton; - $button->setImageUrl($buttonStyle->getImageUrl()); - break; - default: - throw new TInvalidDataValueException('wizard_buttontype_unknown',$buttonStyle->getButtonType()); - } - $button->setText($buttonStyle->getButtonText()); - $button->setCausesValidation($causesValidation); - $button->setCommandName($commandName); - return $button; - } -} - -/** - * TWizardStartNavigationTemplate class. - * TWizardStartNavigationTemplate is the template used as default wizard start navigation panel. - * It consists of two buttons, Next and Cancel. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardStartNavigationTemplate extends TWizardNavigationTemplate -{ - /** - * Instantiates the template. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $nextButton=$this->createNavigationButton($this->getWizard()->getStartNextButtonStyle(),true,TWizard::CMD_NEXT); - $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); - - $controls=$parent->getControls(); - $controls->add($nextButton); - $controls->add("\n"); - $controls->add($cancelButton); - - $parent->setNextButton($nextButton); - $parent->setCancelButton($cancelButton); - } -} - -/** - * TWizardFinishNavigationTemplate class. - * TWizardFinishNavigationTemplate is the template used as default wizard finish navigation panel. - * It consists of three buttons, Previous, Complete and Cancel. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardFinishNavigationTemplate extends TWizardNavigationTemplate -{ - /** - * Instantiates the template. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $previousButton=$this->createNavigationButton($this->getWizard()->getFinishPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); - $completeButton=$this->createNavigationButton($this->getWizard()->getFinishCompleteButtonStyle(),true,TWizard::CMD_COMPLETE); - $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); - - $controls=$parent->getControls(); - $controls->add($previousButton); - $controls->add("\n"); - $controls->add($completeButton); - $controls->add("\n"); - $controls->add($cancelButton); - - $parent->setPreviousButton($previousButton); - $parent->setCompleteButton($completeButton); - $parent->setCancelButton($cancelButton); - } -} - -/** - * TWizardStepNavigationTemplate class. - * TWizardStepNavigationTemplate is the template used as default wizard step navigation panel. - * It consists of three buttons, Previous, Next and Cancel. - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardStepNavigationTemplate extends TWizardNavigationTemplate -{ - /** - * Instantiates the template. - * @param TControl parent to hold the content within the template - */ - public function instantiateIn($parent) - { - $previousButton=$this->createNavigationButton($this->getWizard()->getStepPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); - $nextButton=$this->createNavigationButton($this->getWizard()->getStepNextButtonStyle(),true,TWizard::CMD_NEXT); - $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); - - $controls=$parent->getControls(); - $controls->add($previousButton); - $controls->add("\n"); - $controls->add($nextButton); - $controls->add("\n"); - $controls->add($cancelButton); - - $parent->setPreviousButton($previousButton); - $parent->setNextButton($nextButton); - $parent->setCancelButton($cancelButton); - } -} - - -/** - * TWizardNavigationButtonType class. - * TWizardNavigationButtonType defines the enumerable type for the possible types of buttons - * that can be used in the navigation part of a {@link TWizard}. - * - * The following enumerable values are defined: - * - Button: a regular click button - * - Image: an image button - * - Link: a hyperlink button - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TWizardNavigationButtonType extends TEnumerable -{ - const Button='Button'; - const Image='Image'; - const Link='Link'; -} - - -/** - * TWizardStepType class. - * TWizardStepType defines the enumerable type for the possible types of {@link TWizard wizard} steps. - * - * The following enumerable values are defined: - * - Auto: the type is automatically determined based on the location of the wizard step in the whole step collection. - * - Complete: the step is the last summary step. - * - Start: the step is the first step - * - Step: the step is between the begin and the end steps. - * - Finish: the last step before the Complete step. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TWizardStepType extends TEnumerable -{ - const Auto='Auto'; - const Complete='Complete'; - const Start='Start'; - const Step='Step'; - const Finish='Finish'; -} - + $requestedIndex=TPropertyValue::ensureInteger($requestedStep); + $navParam->setNextStepIndex($requestedIndex); + $handled=true; + } + + if($handled) + { + if(!$navParam->getCancelNavigation()) + { + $nextStepIndex=$navParam->getNextStepIndex(); + if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex)) + { + if($movePrev) + $this->getPreviousStepIndex(true); // pop out the previous move from history + $this->setActiveStepIndex($nextStepIndex); + } + } + else + $this->setActiveStepIndex($index); + return true; + } + } + return false; + } +} + + +/** + * TWizardStep class. + * + * TWizardStep represents a wizard step. The wizard owning the step + * can be obtained by {@link getWizard Wizard}. + * To specify the type of the step, set {@link setStepType StepType}; + * For step title, set {@link setTitle Title}. If a step can be re-visited, + * set {@link setAllowReturn AllowReturn} to true. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStep extends TView +{ + private $_wizard; + + /** + * @return TWizard the wizard owning this step + */ + public function getWizard() + { + return $this->_wizard; + } + + /** + * Sets the wizard owning this step. + * This method is used internally by {@link TWizard}. + * @param TWizard the wizard owning this step + */ + public function setWizard($wizard) + { + $this->_wizard=$wizard; + } + + /** + * @return string the title for this step. + */ + public function getTitle() + { + return $this->getViewState('Title',''); + } + + /** + * @param string the title for this step. + */ + public function setTitle($value) + { + $this->setViewState('Title',$value,''); + if($this->_wizard) + $this->_wizard->wizardStepsChanged(); + } + + /** + * @return boolean whether this step can be re-visited. Default to true. + */ + public function getAllowReturn() + { + return $this->getViewState('AllowReturn',true); + } + + /** + * @param boolean whether this step can be re-visited. + */ + public function setAllowReturn($value) + { + $this->setViewState('AllowReturn',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TWizardStepType the wizard step type. Defaults to TWizardStepType::Auto. + */ + public function getStepType() + { + return $this->getViewState('StepType',TWizardStepType::Auto); + } + + /** + * @param TWizardStepType the wizard step type. + */ + public function setStepType($type) + { + $type=TPropertyValue::ensureEnum($type,'TWizardStepType'); + if($type!==$this->getStepType()) + { + $this->setViewState('StepType',$type,TWizardStepType::Auto); + if($this->_wizard) + $this->_wizard->wizardStepsChanged(); + } + } +} + + +/** + * TCompleteWizardStep class. + * + * TCompleteWizardStep represents a wizard step of type TWizardStepType::Complete. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCompleteWizardStep extends TWizardStep +{ + /** + * @return TWizardStepType the wizard step type. Always TWizardStepType::Complete. + */ + public function getStepType() + { + return TWizardStepType::Complete; + } + + /** + * @param string the wizard step type. + * @throws TInvalidOperationException whenever this method is invoked. + */ + public function setStepType($value) + { + throw new TInvalidOperationException('completewizardstep_steptype_readonly'); + } +} + + +/** + * TTemplatedWizardStep class. + * + * TTemplatedWizardStep represents a wizard step whose content and navigation + * can be customized using templates. To customize the step content, specify + * {@link setContentTemplate ContentTemplate}. To customize navigation specific + * to the step, specify {@link setNavigationTemplate NavigationTemplate}. Note, + * if the navigation template is not specified, default navigation will be used. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTemplatedWizardStep extends TWizardStep implements INamingContainer +{ + /** + * @var ITemplate the template for displaying the navigation UI of a wizard step. + */ + private $_navigationTemplate=null; + /** + * @var ITemplate the template for displaying the content within the wizard step. + */ + private $_contentTemplate=null; + /** + * @var TWizardNavigationContainer + */ + private $_navigationContainer=null; + + /** + * Creates child controls. + * This method mainly instantiates the content template, if any. + */ + public function createChildControls() + { + $this->getControls()->clear(); + if($this->_contentTemplate) + $this->_contentTemplate->instantiateIn($this); + } + + /** + * Ensures child controls are created. + * @param mixed event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->ensureChildControls(); + } + + /** + * @return ITemplate the template for the content of the wizard step. + */ + public function getContentTemplate() + { + return $this->_contentTemplate; + } + + /** + * @param ITemplate the template for the content of the wizard step. + */ + public function setContentTemplate($value) + { + $this->_contentTemplate=$value; + } + + /** + * @return ITemplate the template for displaying the navigation UI of a wizard step. Defaults to null. + */ + public function getNavigationTemplate() + { + return $this->_navigationTemplate; + } + + /** + * @param ITemplate the template for displaying the navigation UI of a wizard step. + */ + public function setNavigationTemplate($value) + { + $this->_navigationTemplate=$value; + } + + /** + * @return TWizardNavigationContainer the control containing the navigation. + * It could be null if no navigation template is specified. + */ + public function getNavigationContainer() + { + return $this->_navigationContainer; + } + + /** + * Instantiates the navigation template if any + */ + public function instantiateNavigationTemplate() + { + if(!$this->_navigationContainer && $this->_navigationTemplate) + { + $this->_navigationContainer=new TWizardNavigationContainer; + $this->_navigationTemplate->instantiateIn($this->_navigationContainer); + } + } +} + + +/** + * TWizardStepCollection class. + * + * TWizardStepCollection represents the collection of wizard steps owned + * by a {@link TWizard}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStepCollection extends TList +{ + /** + * @var TWizard + */ + private $_wizard; + + /** + * Constructor. + * @param TWizard wizard that owns this collection + */ + public function __construct(TWizard $wizard) + { + $this->_wizard=$wizard; + } + + /** + * Inserts an item at the specified position. + * This method overrides the parent implementation by checking if + * the item being added is a {@link TWizardStep}. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item being added is not TWizardStep. + */ + public function insertAt($index,$item) + { + if($item instanceof TWizardStep) + { + parent::insertAt($index,$item); + $this->_wizard->getMultiView()->getViews()->insertAt($index,$item); + $this->_wizard->addedWizardStep($item); + } + else + throw new TInvalidDataTypeException('wizardstepcollection_wizardstep_required'); + } + + /** + * Removes an item at the specified position. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $step=parent::removeAt($index); + $this->_wizard->getMultiView()->getViews()->remove($step); + $this->_wizard->removedWizardStep($step); + return $step; + } +} + + +/** + * TWizardNavigationContainer class. + * + * TWizardNavigationContainer represents a control containing + * a wizard navigation. The navigation may contain a few buttons, including + * {@link getPreviousButton PreviousButton}, {@link getNextButton NextButton}, + * {@link getCancelButton CancelButton}, {@link getCompleteButton CompleteButton}. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationContainer extends TControl implements INamingContainer +{ + private $_previousButton=null; + private $_nextButton=null; + private $_cancelButton=null; + private $_completeButton=null; + + /** + * @return mixed the previous button + */ + public function getPreviousButton() + { + return $this->_previousButton; + } + + /** + * @param mixed the previous button + */ + public function setPreviousButton($value) + { + $this->_previousButton=$value; + } + + /** + * @return mixed the next button + */ + public function getNextButton() + { + return $this->_nextButton; + } + + /** + * @param mixed the next button + */ + public function setNextButton($value) + { + $this->_nextButton=$value; + } + + /** + * @return mixed the cancel button + */ + public function getCancelButton() + { + return $this->_cancelButton; + } + + /** + * @param mixed the cancel button + */ + public function setCancelButton($value) + { + $this->_cancelButton=$value; + } + + /** + * @return mixed the complete button + */ + public function getCompleteButton() + { + return $this->_completeButton; + } + + /** + * @param mixed the complete button + */ + public function setCompleteButton($value) + { + $this->_completeButton=$value; + } +} + + +/** + * TWizardNavigationEventParameter class. + * + * TWizardNavigationEventParameter represents the parameter for + * {@link TWizard}'s navigation events. + * + * The index of the currently active step can be obtained from + * {@link getCurrentStepIndex CurrentStepIndex}, while the index + * of the candidate new step is in {@link getNextStepIndex NextStepIndex}. + * By modifying {@link setNextStepIndex NextStepIndex}, the new step + * can be changed to another one. If there is anything wrong with + * the navigation and it is not wanted, set {@link setCancelNavigation CancelNavigation} + * to true. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationEventParameter extends TEventParameter +{ + private $_cancel=false; + private $_currentStep; + private $_nextStep; + + /** + * Constructor. + * @param integer current step index + */ + public function __construct($currentStep) + { + $this->_currentStep=$currentStep; + $this->_nextStep=$currentStep; + } + + /** + * @return integer the zero-based index of the currently active step. + */ + public function getCurrentStepIndex() + { + return $this->_currentStep; + } + + /** + * @return integer the zero-based index of the next step. Default to {@link getCurrentStepIndex CurrentStepIndex}. + */ + public function getNextStepIndex() + { + return $this->_nextStep; + } + + /** + * @param integer the zero-based index of the next step. + */ + public function setNextStepIndex($index) + { + $this->_nextStep=TPropertyValue::ensureInteger($index); + } + + /** + * @return boolean whether navigation to the next step should be canceled. Default to false. + */ + public function getCancelNavigation() + { + return $this->_cancel; + } + + /** + * @param boolean whether navigation to the next step should be canceled. + */ + public function setCancelNavigation($value) + { + $this->_cancel=TPropertyValue::ensureBoolean($value); + } +} + +/** + * TWizardSideBarTemplate class. + * TWizardSideBarTemplate is the default template for wizard sidebar. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardSideBarTemplate extends TComponent implements ITemplate +{ + /** + * Instantiates the template. + * It creates a {@link TDataList} control. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $dataList=new TDataList; + $dataList->setID(TWizard::ID_SIDEBAR_LIST); + $dataList->getSelectedItemStyle()->getFont()->setBold(true); + $dataList->setItemTemplate(new TWizardSideBarListItemTemplate); + $parent->getControls()->add($dataList); + } +} + +/** + * TWizardSideBarListItemTemplate class. + * TWizardSideBarListItemTemplate is the default template for each item in the sidebar datalist. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardSideBarListItemTemplate extends TComponent implements ITemplate +{ + /** + * Instantiates the template. + * It creates a {@link TLinkButton}. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $button=new TLinkButton; + $button->setID(TWizard::ID_SIDEBAR_BUTTON); + $parent->getControls()->add($button); + } +} + +/** + * TWizardNavigationTemplate class. + * TWizardNavigationTemplate is the base class for various navigation templates. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationTemplate extends TComponent implements ITemplate +{ + private $_wizard; + + /** + * Constructor. + * @param TWizard the wizard owning this template + */ + public function __construct($wizard) + { + $this->_wizard=$wizard; + } + + /** + * @return TWizard the wizard owning this template + */ + public function getWizard() + { + return $this->_wizard; + } + + /** + * Instantiates the template. + * Derived classes should override this method. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + } + + /** + * Creates a navigation button. + * It creates a {@link TButton}, {@link TLinkButton}, or {@link TImageButton}, + * depending on the given parameters. + * @param TWizardNavigationButtonStyle button style + * @param boolean whether the button should cause validation + * @param string command name for the button's OnCommand event + * @throws TInvalidDataValueException if the button type is not recognized + */ + protected function createNavigationButton($buttonStyle,$causesValidation,$commandName) + { + switch($buttonStyle->getButtonType()) + { + case TWizardNavigationButtonType::Button: + $button=new TButton; + break; + case TWizardNavigationButtonType::Link: + $button=new TLinkButton; + break; + case TWizardNavigationButtonType::Image: + $button=new TImageButton; + $button->setImageUrl($buttonStyle->getImageUrl()); + break; + default: + throw new TInvalidDataValueException('wizard_buttontype_unknown',$buttonStyle->getButtonType()); + } + $button->setText($buttonStyle->getButtonText()); + $button->setCausesValidation($causesValidation); + $button->setCommandName($commandName); + return $button; + } +} + +/** + * TWizardStartNavigationTemplate class. + * TWizardStartNavigationTemplate is the template used as default wizard start navigation panel. + * It consists of two buttons, Next and Cancel. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStartNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $nextButton=$this->createNavigationButton($this->getWizard()->getStartNextButtonStyle(),true,TWizard::CMD_NEXT); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($nextButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setNextButton($nextButton); + $parent->setCancelButton($cancelButton); + } +} + +/** + * TWizardFinishNavigationTemplate class. + * TWizardFinishNavigationTemplate is the template used as default wizard finish navigation panel. + * It consists of three buttons, Previous, Complete and Cancel. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardFinishNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $previousButton=$this->createNavigationButton($this->getWizard()->getFinishPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); + $completeButton=$this->createNavigationButton($this->getWizard()->getFinishCompleteButtonStyle(),true,TWizard::CMD_COMPLETE); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($previousButton); + $controls->add("\n"); + $controls->add($completeButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setPreviousButton($previousButton); + $parent->setCompleteButton($completeButton); + $parent->setCancelButton($cancelButton); + } +} + +/** + * TWizardStepNavigationTemplate class. + * TWizardStepNavigationTemplate is the template used as default wizard step navigation panel. + * It consists of three buttons, Previous, Next and Cancel. + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStepNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $previousButton=$this->createNavigationButton($this->getWizard()->getStepPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); + $nextButton=$this->createNavigationButton($this->getWizard()->getStepNextButtonStyle(),true,TWizard::CMD_NEXT); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($previousButton); + $controls->add("\n"); + $controls->add($nextButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setPreviousButton($previousButton); + $parent->setNextButton($nextButton); + $parent->setCancelButton($cancelButton); + } +} + + +/** + * TWizardNavigationButtonType class. + * TWizardNavigationButtonType defines the enumerable type for the possible types of buttons + * that can be used in the navigation part of a {@link TWizard}. + * + * The following enumerable values are defined: + * - Button: a regular click button + * - Image: an image button + * - Link: a hyperlink button + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TWizardNavigationButtonType extends TEnumerable +{ + const Button='Button'; + const Image='Image'; + const Link='Link'; +} + + +/** + * TWizardStepType class. + * TWizardStepType defines the enumerable type for the possible types of {@link TWizard wizard} steps. + * + * The following enumerable values are defined: + * - Auto: the type is automatically determined based on the location of the wizard step in the whole step collection. + * - Complete: the step is the last summary step. + * - Start: the step is the first step + * - Step: the step is between the begin and the end steps. + * - Finish: the last step before the Complete step. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TWizardStepType extends TEnumerable +{ + const Auto='Auto'; + const Complete='Complete'; + const Start='Start'; + const Step='Step'; + const Finish='Finish'; +} + diff --git a/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php b/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php index 0e05c556..c3b4b603 100644 --- a/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php +++ b/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php @@ -1,155 +1,155 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id $ - * @package System.Web.UI.WebControls - */ - -/** - * Includes TStyle class file - */ -Prado::using('System.Web.UI.WebControls.TStyle'); - -/** - * TWizardNavigationButtonStyle class. - * TWizardNavigationButtonStyle defines the style applied to a wizard navigation button. - * The button type can be specified via {@link setButtonType ButtonType}, which - * can be 'Button', 'Image' or 'Link'. - * If the button is an image button, {@link setImageUrl ImageUrl} will be - * used to load the image for the button. - * Otherwise, {@link setButtonText ButtonText} will be displayed as the button caption. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TWizardNavigationButtonStyle extends TStyle -{ - private $_imageUrl=null; - private $_buttonText=null; - private $_buttonType=null; - - /** - * Sets the style attributes to default values. - * This method overrides the parent implementation by - * resetting additional TWizardNavigationButtonStyle specific attributes. - */ - public function reset() - { - parent::reset(); - $this->_imageUrl=null; - $this->_buttonText=null; - $this->_buttonType=null; - } - - /** - * Copies the fields in a new style to this style. - * If a style field is set in the new style, the corresponding field - * in this style will be overwritten. - * @param TStyle the new style - */ - public function copyFrom($style) - { - parent::copyFrom($style); - if($style instanceof TWizardNavigationButtonStyle) - { - if($this->_imageUrl===null && $style->_imageUrl!==null) - $this->_imageUrl=$style->_imageUrl; - if($this->_buttonText===null && $style->_buttonText!==null) - $this->_buttonText=$style->_buttonText; - if($this->_buttonType===null && $style->_buttonType!==null) - $this->_buttonType=$style->_buttonType; - } - } - - /** - * Merges the style with a new one. - * If a style field is not set in this style, it will be overwritten by - * the new one. - * @param TStyle the new style - */ - public function mergeWith($style) - { - parent::mergeWith($style); - if($style instanceof TWizardNavigationButtonStyle) - { - if($style->_imageUrl!==null) - $this->_imageUrl=$style->_imageUrl; - if($style->_buttonText!==null) - $this->_buttonText=$style->_buttonText; - if($style->_buttonType!==null) - $this->_buttonType=$style->_buttonType; - } - } - - /** - * @return string image URL for the image button - */ - public function getImageUrl() - { - return $this->_imageUrl===null?'':$this->_imageUrl; - } - - /** - * @param string image URL for the image button - */ - public function setImageUrl($value) - { - $this->_imageUrl=$value; - } - - /** - * @return string button caption - */ - public function getButtonText() - { - return $this->_buttonText===null?'':$this->_buttonText; - } - - /** - * @param string button caption - */ - public function setButtonText($value) - { - $this->_buttonText=$value; - } - - /** - * @return TWizardNavigationButtonType button type. Default to TWizardNavigationButtonType::Button. - */ - public function getButtonType() - { - return $this->_buttonType===null? TWizardNavigationButtonType::Button :$this->_buttonType; - } - - /** - * @param TWizardNavigationButtonType button type. - */ - public function setButtonType($value) - { - $this->_buttonType=TPropertyValue::ensureEnum($value,'TWizardNavigationButtonType'); - } - - /** - * Applies this style to the specified button - * @param mixed button to be applied with this style - */ - public function apply($button) - { - if($button instanceof TImageButton) - { - if($button->getImageUrl()==='') - $button->setImageUrl($this->getImageUrl()); - } - if($button->getText()==='') - $button->setText($this->getButtonText()); - $button->getStyle()->mergeWith($this); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TStyle class file + */ +Prado::using('System.Web.UI.WebControls.TStyle'); + +/** + * TWizardNavigationButtonStyle class. + * TWizardNavigationButtonStyle defines the style applied to a wizard navigation button. + * The button type can be specified via {@link setButtonType ButtonType}, which + * can be 'Button', 'Image' or 'Link'. + * If the button is an image button, {@link setImageUrl ImageUrl} will be + * used to load the image for the button. + * Otherwise, {@link setButtonText ButtonText} will be displayed as the button caption. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationButtonStyle extends TStyle +{ + private $_imageUrl=null; + private $_buttonText=null; + private $_buttonType=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TWizardNavigationButtonStyle specific attributes. + */ + public function reset() + { + parent::reset(); + $this->_imageUrl=null; + $this->_buttonText=null; + $this->_buttonType=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TWizardNavigationButtonStyle) + { + if($this->_imageUrl===null && $style->_imageUrl!==null) + $this->_imageUrl=$style->_imageUrl; + if($this->_buttonText===null && $style->_buttonText!==null) + $this->_buttonText=$style->_buttonText; + if($this->_buttonType===null && $style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TWizardNavigationButtonStyle) + { + if($style->_imageUrl!==null) + $this->_imageUrl=$style->_imageUrl; + if($style->_buttonText!==null) + $this->_buttonText=$style->_buttonText; + if($style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * @return string image URL for the image button + */ + public function getImageUrl() + { + return $this->_imageUrl===null?'':$this->_imageUrl; + } + + /** + * @param string image URL for the image button + */ + public function setImageUrl($value) + { + $this->_imageUrl=$value; + } + + /** + * @return string button caption + */ + public function getButtonText() + { + return $this->_buttonText===null?'':$this->_buttonText; + } + + /** + * @param string button caption + */ + public function setButtonText($value) + { + $this->_buttonText=$value; + } + + /** + * @return TWizardNavigationButtonType button type. Default to TWizardNavigationButtonType::Button. + */ + public function getButtonType() + { + return $this->_buttonType===null? TWizardNavigationButtonType::Button :$this->_buttonType; + } + + /** + * @param TWizardNavigationButtonType button type. + */ + public function setButtonType($value) + { + $this->_buttonType=TPropertyValue::ensureEnum($value,'TWizardNavigationButtonType'); + } + + /** + * Applies this style to the specified button + * @param mixed button to be applied with this style + */ + public function apply($button) + { + if($button instanceof TImageButton) + { + if($button->getImageUrl()==='') + $button->setImageUrl($this->getImageUrl()); + } + if($button->getText()==='') + $button->setText($this->getButtonText()); + $button->getStyle()->mergeWith($this); + } +} + diff --git a/framework/Web/UI/WebControls/assets/captcha.php b/framework/Web/UI/WebControls/assets/captcha.php index b26df895..08a857b5 100644 --- a/framework/Web/UI/WebControls/assets/captcha.php +++ b/framework/Web/UI/WebControls/assets/captcha.php @@ -1,224 +1,224 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls.assets - */ - -define('THEME_OPAQUE_BACKGROUND',0x0001); -define('THEME_NOISY_BACKGROUND',0x0002); -define('THEME_HAS_GRID',0x0004); -define('THEME_HAS_SCRIBBLE',0x0008); -define('THEME_MORPH_BACKGROUND',0x0010); -define('THEME_SHADOWED_TEXT',0x0020); - -require_once(dirname(__FILE__).'/captcha_key.php'); - -$token='error'; -$theme=0; - -if(isset($_GET['options'])) -{ - $str=base64_decode($_GET['options']); - if(strlen($str)>32) - { - $hash=substr($str,0,32); - $str=substr($str,32); - if(md5($privateKey.$str)===$hash) - { - $options=unserialize($str); - $publicKey=$options['publicKey']; - $tokenLength=$options['tokenLength']; - $caseSensitive=$options['caseSensitive']; - $alphabet=$options['alphabet']; - $fontSize=$options['fontSize']; - $theme=$options['theme']; - if(($randomSeed=$options['randomSeed'])>0) - srand($randomSeed); - else - srand((int)(microtime()*1000000)); - $token=generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive); - } - } -} - -displayToken($token,$fontSize,$theme); - -function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive) -{ - $token=substr(hash2string(md5($publicKey.$privateKey),$alphabet).hash2string(md5($privateKey.$publicKey),$alphabet),0,$tokenLength); - return $caseSensitive?$token:strtoupper($token); -} - -function hash2string($hex,$alphabet) -{ - if(strlen($alphabet)<2) - $alphabet='234578adefhijmnrtABDEFGHJLMNRT'; - $hexLength=strlen($hex); - $base=strlen($alphabet); - $result=''; - for($i=0;$i<$hexLength;$i+=6) - { - $number=hexdec(substr($hex,$i,6)); - while($number) - { - $result.=$alphabet[$number%$base]; - $number=floor($number/$base); - } - } - return $result; -} - -function displayToken($token,$fontSize,$theme) -{ - if(($fontSize=(int)$fontSize)<22) - $fontSize=22; - if($fontSize>100) - $fontSize=100; - $length=strlen($token); - $padding=10; - $fontWidth=$fontSize; - $fontHeight=floor($fontWidth*1.5); - $width=$fontWidth*$length+$padding*2; - $height=$fontHeight; - $image=imagecreatetruecolor($width,$height); - - addBackground - ( - $image, $width, $height, - $theme&THEME_OPAQUE_BACKGROUND, - $theme&THEME_NOISY_BACKGROUND, - $theme&THEME_HAS_GRID, - $theme&THEME_HAS_SCRIBBLE, - $theme&THEME_MORPH_BACKGROUND - ); - - $font=dirname(__FILE__).DIRECTORY_SEPARATOR.'verase.ttf'; - - if(function_exists('imagefilter')) - imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); - - $hasShadow=($theme&THEME_SHADOWED_TEXT); - for($i=0;$i<$length;$i++) - { - $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); - $size=rand($fontWidth-10,$fontWidth); - $angle=rand(-30,30); - $x=$padding+$i*$fontWidth; - $y=rand($fontHeight-15,$fontHeight-10); - imagettftext($image,$size,$angle,$x,$y,$color,$font,$token[$i]); - if($hasShadow) - imagettftext($image,$size,$angle,$x+2,$y+2,$color,$font,$token[$i]); - imagecolordeallocate($image,$color); - } - - header('Content-Type: image/png'); - imagepng($image); - imagedestroy($image); -} - -function addBackground($image,$width,$height,$opaque,$noisy,$hasGrid,$hasScribble,$morph) -{ - $background=imagecreatetruecolor($width*2,$height*2); - $white=imagecolorallocate($background,255,255,255); - imagefill($background,0,0,$white); - - if($opaque) - imagefill($background,0,0,imagecolorallocate($background,100,100,100)); - - if($noisy) - addNoise($background,$width*2,$height*2); - - if($hasGrid) - addGrid($background,$width*2,$height*2); - - if($hasScribble) - addScribble($background,$width*2,$height*2); - - if($morph) - morphImage($background,$width*2,$height*2); - - imagecopy($image,$background,0,0,30,30,$width,$height); - - if(!$opaque) - imagecolortransparent($image,$white); -} - -function addNoise($image,$width,$height) -{ - for($x=0;$x<$width;++$x) - { - for($y=0;$y<$height;++$y) - { - if(rand(0,100)<25) - { - $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); - imagesetpixel($image,$x,$y,$color); - imagecolordeallocate($image,$color); - } - } - } -} - -function addGrid($image,$width,$height) -{ - for($i=0;$i<$width;$i+=rand(15,25)) - { - imagesetthickness($image,rand(2,6)); - $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); - imageline($image,$i+rand(-10,20),0,$i+rand(-10,20),$height,$color); - imagecolordeallocate($image,$color); - } - for($i=0;$i<$height;$i+=rand(15,25)) - { - imagesetthickness($image,rand(2,6)); - $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); - imageline($image,0,$i+rand(-10,20),$width,$i+rand(-10,20),$color); - imagecolordeallocate($image,$color); - } -} - -function addScribble($image,$width,$height) -{ - for($i=0;$i<8;$i++) - { - $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); - $points=array(); - for($j=1;$j=$height) $y=$height-5; - if($y<0) $y=5; - imagecopy($tempImage,$image,$x,0,$x,$y,$chunk,$height); - } - for($x=$y=0;$y<$height;$y+=$chunk) - { - $chunk=rand(1,5); - $x+=rand(-1,1); - if($x>=$width) $x=$width-5; - if($x<0) $x=5; - imagecopy($image,$tempImage,$x,$y,0,$y,$width,$chunk); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.WebControls.assets + */ + +define('THEME_OPAQUE_BACKGROUND',0x0001); +define('THEME_NOISY_BACKGROUND',0x0002); +define('THEME_HAS_GRID',0x0004); +define('THEME_HAS_SCRIBBLE',0x0008); +define('THEME_MORPH_BACKGROUND',0x0010); +define('THEME_SHADOWED_TEXT',0x0020); + +require_once(dirname(__FILE__).'/captcha_key.php'); + +$token='error'; +$theme=0; + +if(isset($_GET['options'])) +{ + $str=base64_decode($_GET['options']); + if(strlen($str)>32) + { + $hash=substr($str,0,32); + $str=substr($str,32); + if(md5($privateKey.$str)===$hash) + { + $options=unserialize($str); + $publicKey=$options['publicKey']; + $tokenLength=$options['tokenLength']; + $caseSensitive=$options['caseSensitive']; + $alphabet=$options['alphabet']; + $fontSize=$options['fontSize']; + $theme=$options['theme']; + if(($randomSeed=$options['randomSeed'])>0) + srand($randomSeed); + else + srand((int)(microtime()*1000000)); + $token=generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive); + } + } +} + +displayToken($token,$fontSize,$theme); + +function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive) +{ + $token=substr(hash2string(md5($publicKey.$privateKey),$alphabet).hash2string(md5($privateKey.$publicKey),$alphabet),0,$tokenLength); + return $caseSensitive?$token:strtoupper($token); +} + +function hash2string($hex,$alphabet) +{ + if(strlen($alphabet)<2) + $alphabet='234578adefhijmnrtABDEFGHJLMNRT'; + $hexLength=strlen($hex); + $base=strlen($alphabet); + $result=''; + for($i=0;$i<$hexLength;$i+=6) + { + $number=hexdec(substr($hex,$i,6)); + while($number) + { + $result.=$alphabet[$number%$base]; + $number=floor($number/$base); + } + } + return $result; +} + +function displayToken($token,$fontSize,$theme) +{ + if(($fontSize=(int)$fontSize)<22) + $fontSize=22; + if($fontSize>100) + $fontSize=100; + $length=strlen($token); + $padding=10; + $fontWidth=$fontSize; + $fontHeight=floor($fontWidth*1.5); + $width=$fontWidth*$length+$padding*2; + $height=$fontHeight; + $image=imagecreatetruecolor($width,$height); + + addBackground + ( + $image, $width, $height, + $theme&THEME_OPAQUE_BACKGROUND, + $theme&THEME_NOISY_BACKGROUND, + $theme&THEME_HAS_GRID, + $theme&THEME_HAS_SCRIBBLE, + $theme&THEME_MORPH_BACKGROUND + ); + + $font=dirname(__FILE__).DIRECTORY_SEPARATOR.'verase.ttf'; + + if(function_exists('imagefilter')) + imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); + + $hasShadow=($theme&THEME_SHADOWED_TEXT); + for($i=0;$i<$length;$i++) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + $size=rand($fontWidth-10,$fontWidth); + $angle=rand(-30,30); + $x=$padding+$i*$fontWidth; + $y=rand($fontHeight-15,$fontHeight-10); + imagettftext($image,$size,$angle,$x,$y,$color,$font,$token[$i]); + if($hasShadow) + imagettftext($image,$size,$angle,$x+2,$y+2,$color,$font,$token[$i]); + imagecolordeallocate($image,$color); + } + + header('Content-Type: image/png'); + imagepng($image); + imagedestroy($image); +} + +function addBackground($image,$width,$height,$opaque,$noisy,$hasGrid,$hasScribble,$morph) +{ + $background=imagecreatetruecolor($width*2,$height*2); + $white=imagecolorallocate($background,255,255,255); + imagefill($background,0,0,$white); + + if($opaque) + imagefill($background,0,0,imagecolorallocate($background,100,100,100)); + + if($noisy) + addNoise($background,$width*2,$height*2); + + if($hasGrid) + addGrid($background,$width*2,$height*2); + + if($hasScribble) + addScribble($background,$width*2,$height*2); + + if($morph) + morphImage($background,$width*2,$height*2); + + imagecopy($image,$background,0,0,30,30,$width,$height); + + if(!$opaque) + imagecolortransparent($image,$white); +} + +function addNoise($image,$width,$height) +{ + for($x=0;$x<$width;++$x) + { + for($y=0;$y<$height;++$y) + { + if(rand(0,100)<25) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + imagesetpixel($image,$x,$y,$color); + imagecolordeallocate($image,$color); + } + } + } +} + +function addGrid($image,$width,$height) +{ + for($i=0;$i<$width;$i+=rand(15,25)) + { + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,$i+rand(-10,20),0,$i+rand(-10,20),$height,$color); + imagecolordeallocate($image,$color); + } + for($i=0;$i<$height;$i+=rand(15,25)) + { + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,0,$i+rand(-10,20),$width,$i+rand(-10,20),$color); + imagecolordeallocate($image,$color); + } +} + +function addScribble($image,$width,$height) +{ + for($i=0;$i<8;$i++) + { + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + $points=array(); + for($j=1;$j=$height) $y=$height-5; + if($y<0) $y=5; + imagecopy($tempImage,$image,$x,0,$x,$y,$chunk,$height); + } + for($x=$y=0;$y<$height;$y+=$chunk) + { + $chunk=rand(1,5); + $x+=rand(-1,1); + if($x>=$width) $x=$width-5; + if($x<0) $x=5; + imagecopy($image,$tempImage,$x,$y,0,$y,$width,$chunk); + } +} + -- cgit v1.2.3