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/Javascripts/JSMin.php | 580 +-- framework/Web/Javascripts/TJavaScript.php | 566 +-- framework/Web/Javascripts/packages.php | 242 +- .../source/prado/activecontrols/activecontrols3.js | 820 ++-- .../prado/activecontrols/activedatepicker.js | 174 +- .../source/prado/activecontrols/ajax3.js | 2288 +++++----- .../source/prado/activecontrols/dragdrop.js | 122 +- .../source/prado/activecontrols/inlineeditor.js | 602 +-- .../source/prado/colorpicker/colorpicker.js | 1560 +++---- .../Javascripts/source/prado/controls/controls.js | 1034 ++--- .../Javascripts/source/prado/controls/htmlarea.js | 298 +- .../Javascripts/source/prado/controls/keyboard.js | 322 +- .../Javascripts/source/prado/controls/tabpanel.js | 120 +- .../source/prado/datepicker/datepicker.js | 1578 +++---- .../Web/Javascripts/source/prado/logger/logger.js | 1506 +++--- framework/Web/Javascripts/source/prado/prado.js | 188 +- .../Javascripts/source/prado/ratings/ratings.js | 412 +- .../source/prado/scriptaculous-adapter.js | 2792 ++++++------ .../source/prado/validator/validation3.js | 3886 ++++++++-------- framework/Web/Services/TFeedService.php | 374 +- framework/Web/Services/TJsonService.php | 426 +- framework/Web/Services/TPageService.php | 1786 ++++---- framework/Web/TAssetManager.php | 714 +-- framework/Web/THttpResponse.php | 1438 +++--- framework/Web/THttpSession.php | 1460 +++--- framework/Web/THttpUtility.php | 124 +- framework/Web/TUrlManager.php | 280 +- framework/Web/TUrlMapping.php | 1768 ++++---- 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 +- 151 files changed, 56328 insertions(+), 56328 deletions(-) (limited to 'framework/Web') diff --git a/framework/Web/Javascripts/JSMin.php b/framework/Web/Javascripts/JSMin.php index 6ed24033..95beff38 100644 --- a/framework/Web/Javascripts/JSMin.php +++ b/framework/Web/Javascripts/JSMin.php @@ -1,290 +1,290 @@ - - * @copyright 2002 Douglas Crockford (jsmin.c) - * @copyright 2008 Ryan Grove (PHP port) - * @license http://opensource.org/licenses/mit-license.php MIT License - * @version 1.1.1 (2008-03-02) - * @link http://code.google.com/p/jsmin-php/ - */ - -class JSMin { - const ORD_LF = 10; - const ORD_SPACE = 32; - - protected $a = ''; - protected $b = ''; - protected $input = ''; - protected $inputIndex = 0; - protected $inputLength = 0; - protected $lookAhead = null; - protected $output = ''; - - // -- Public Static Methods -------------------------------------------------- - - public static function minify($js) { - $jsmin = new JSMin($js); - return $jsmin->min(); - } - - // -- Public Instance Methods ------------------------------------------------ - - public function __construct($input) { - $this->input = str_replace("\r\n", "\n", $input); - $this->inputLength = strlen($this->input); - } - - // -- Protected Instance Methods --------------------------------------------- - - protected function action($d) { - switch($d) { - case 1: - $this->output .= $this->a; - - case 2: - $this->a = $this->b; - - if ($this->a === "'" || $this->a === '"') { - for (;;) { - $this->output .= $this->a; - $this->a = $this->get(); - - if ($this->a === $this->b) { - break; - } - - if (ord($this->a) <= self::ORD_LF) { - throw new JSMinException('Unterminated string literal.'); - } - - if ($this->a === '\\') { - $this->output .= $this->a; - $this->a = $this->get(); - } - } - } - - case 3: - $this->b = $this->next(); - - if ($this->b === '/' && ( - $this->a === '(' || $this->a === ',' || $this->a === '=' || - $this->a === ':' || $this->a === '[' || $this->a === '!' || - $this->a === '&' || $this->a === '|' || $this->a === '?')) { - - $this->output .= $this->a . $this->b; - - for (;;) { - $this->a = $this->get(); - - if ($this->a === '/') { - break; - } elseif ($this->a === '\\') { - $this->output .= $this->a; - $this->a = $this->get(); - } elseif (ord($this->a) <= self::ORD_LF) { - throw new JSMinException('Unterminated regular expression '. - 'literal.'); - } - - $this->output .= $this->a; - } - - $this->b = $this->next(); - } - } - } - - protected function get() { - $c = $this->lookAhead; - $this->lookAhead = null; - - if ($c === null) { - if ($this->inputIndex < $this->inputLength) { - $c = $this->input[$this->inputIndex]; - $this->inputIndex += 1; - } else { - $c = null; - } - } - - if ($c === "\r") { - return "\n"; - } - - if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { - return $c; - } - - return ' '; - } - - protected function isAlphaNum($c) { - return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; - } - - protected function min() { - $this->a = "\n"; - $this->action(3); - - while ($this->a !== null) { - switch ($this->a) { - case ' ': - if ($this->isAlphaNum($this->b)) { - $this->action(1); - } else { - $this->action(2); - } - break; - - case "\n": - switch ($this->b) { - case '{': - case '[': - case '(': - case '+': - case '-': - $this->action(1); - break; - - case ' ': - $this->action(3); - break; - - default: - if ($this->isAlphaNum($this->b)) { - $this->action(1); - } - else { - $this->action(2); - } - } - break; - - default: - switch ($this->b) { - case ' ': - if ($this->isAlphaNum($this->a)) { - $this->action(1); - break; - } - - $this->action(3); - break; - - case "\n": - switch ($this->a) { - case '}': - case ']': - case ')': - case '+': - case '-': - case '"': - case "'": - $this->action(1); - break; - - default: - if ($this->isAlphaNum($this->a)) { - $this->action(1); - } - else { - $this->action(3); - } - } - break; - - default: - $this->action(1); - break; - } - } - } - - return $this->output; - } - - protected function next() { - $c = $this->get(); - - if ($c === '/') { - switch($this->peek()) { - case '/': - for (;;) { - $c = $this->get(); - - if (ord($c) <= self::ORD_LF) { - return $c; - } - } - - case '*': - $this->get(); - - for (;;) { - switch($this->get()) { - case '*': - if ($this->peek() === '/') { - $this->get(); - return ' '; - } - break; - - case null: - throw new JSMinException('Unterminated comment.'); - } - } - - default: - return $c; - } - } - - return $c; - } - - protected function peek() { - $this->lookAhead = $this->get(); - return $this->lookAhead; - } -} - -// -- Exceptions --------------------------------------------------------------- -class JSMinException extends Exception {} + + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 1.1.1 (2008-03-02) + * @link http://code.google.com/p/jsmin-php/ + */ + +class JSMin { + const ORD_LF = 10; + const ORD_SPACE = 32; + + protected $a = ''; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + + // -- Public Static Methods -------------------------------------------------- + + public static function minify($js) { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + // -- Public Instance Methods ------------------------------------------------ + + public function __construct($input) { + $this->input = str_replace("\r\n", "\n", $input); + $this->inputLength = strlen($this->input); + } + + // -- Protected Instance Methods --------------------------------------------- + + protected function action($d) { + switch($d) { + case 1: + $this->output .= $this->a; + + case 2: + $this->a = $this->b; + + if ($this->a === "'" || $this->a === '"') { + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === $this->b) { + break; + } + + if (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated string literal.'); + } + + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } + } + } + + case 3: + $this->b = $this->next(); + + if ($this->b === '/' && ( + $this->a === '(' || $this->a === ',' || $this->a === '=' || + $this->a === ':' || $this->a === '[' || $this->a === '!' || + $this->a === '&' || $this->a === '|' || $this->a === '?')) { + + $this->output .= $this->a . $this->b; + + for (;;) { + $this->a = $this->get(); + + if ($this->a === '/') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression '. + 'literal.'); + } + + $this->output .= $this->a; + } + + $this->b = $this->next(); + } + } + } + + protected function get() { + $c = $this->lookAhead; + $this->lookAhead = null; + + if ($c === null) { + if ($this->inputIndex < $this->inputLength) { + $c = $this->input[$this->inputIndex]; + $this->inputIndex += 1; + } else { + $c = null; + } + } + + if ($c === "\r") { + return "\n"; + } + + if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { + return $c; + } + + return ' '; + } + + protected function isAlphaNum($c) { + return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; + } + + protected function min() { + $this->a = "\n"; + $this->action(3); + + while ($this->a !== null) { + switch ($this->a) { + case ' ': + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } else { + $this->action(2); + } + break; + + case "\n": + switch ($this->b) { + case '{': + case '[': + case '(': + case '+': + case '-': + $this->action(1); + break; + + case ' ': + $this->action(3); + break; + + default: + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } + else { + $this->action(2); + } + } + break; + + default: + switch ($this->b) { + case ' ': + if ($this->isAlphaNum($this->a)) { + $this->action(1); + break; + } + + $this->action(3); + break; + + case "\n": + switch ($this->a) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case "'": + $this->action(1); + break; + + default: + if ($this->isAlphaNum($this->a)) { + $this->action(1); + } + else { + $this->action(3); + } + } + break; + + default: + $this->action(1); + break; + } + } + } + + return $this->output; + } + + protected function next() { + $c = $this->get(); + + if ($c === '/') { + switch($this->peek()) { + case '/': + for (;;) { + $c = $this->get(); + + if (ord($c) <= self::ORD_LF) { + return $c; + } + } + + case '*': + $this->get(); + + for (;;) { + switch($this->get()) { + case '*': + if ($this->peek() === '/') { + $this->get(); + return ' '; + } + break; + + case null: + throw new JSMinException('Unterminated comment.'); + } + } + + default: + return $c; + } + } + + return $c; + } + + protected function peek() { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } +} + +// -- Exceptions --------------------------------------------------------------- +class JSMinException extends Exception {} diff --git a/framework/Web/Javascripts/TJavaScript.php b/framework/Web/Javascripts/TJavaScript.php index 9e5be75b..3d062e5c 100644 --- a/framework/Web/Javascripts/TJavaScript.php +++ b/framework/Web/Javascripts/TJavaScript.php @@ -1,283 +1,283 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.Javascripts - */ - -/** - * TJavaScript class. - * - * TJavaScript is a utility class containing commonly-used javascript-related - * functions. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.Javascripts - * @since 3.0 - */ -class TJavaScript -{ - /** - * Renders a list of javascript files - * @param array URLs to the javascript files - * @return string rendering result - */ - public static function renderScriptFiles($files) - { - $str=''; - foreach($files as $file) - $str.= self::renderScriptFile($file); - return $str; - } - - /** - * Renders a javascript file - * @param string URL to the javascript file - * @return string rendering result - */ - public static function renderScriptFile($file) - { - return '\n"; - } - - /** - * Renders a list of javascript blocks - * @param array javascript blocks - * @return string rendering result - */ - public static function renderScriptBlocks($scripts) - { - if(count($scripts)) - return "\n"; - else - return ''; - } - - /** - * Renders javascript block - * @param string javascript block - * @return string rendering result - */ - public static function renderScriptBlock($script) - { - return "\n"; - } - - /** - * Quotes a javascript string. - * After processing, the string is safely enclosed within a pair of - * quotation marks and can serve as a javascript string. - * @param string string to be quoted - * @return string the quoted string - */ - public static function quoteString($js) - { - return self::jsonEncode($js,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); - } - - /** - * @return Marks a string as a javascript function. Once marke, the string is considered as a - * raw javascript function that is not supposed to be encoded by {@link encode} - */ - public static function quoteJsLiteral($js) - { - if($js instanceof TJavaScriptLiteral) - return $js; - else - return new TJavaScriptLiteral($js); - } - - /** - * Deprecated, use {@link quoteJsLiteral} instead - */ - public static function quoteFunction($js) - { - return self::quoteJsLiteral($js); - } - - /** - * @return boolean true if the parameter is marked as a javascript function, i.e. if it's considered as a - * raw javascript function that is not supposed to be encoded by {@link encode} - */ - public static function isJsLiteral($js) - { - return ($js instanceof TJavaScriptLiteral); - } - - /** - * Deprecated, use {@link isJsLiteral} instead - */ - public static function isFunction($js) - { - return self::isJsLiteral($js); - } - - /** - * Encodes a PHP variable into javascript representation. - * - * Example: - * - * $options['onLoading'] = "doit"; - * $options['onComplete'] = "more"; - * echo TJavaScript::encode($options); - * //expects the following javascript code - * // {'onLoading':'doit','onComplete':'more'} - * - * - * For higher complexity data structures use {@link jsonEncode} and {@link jsonDecode} - * to serialize and unserialize. - * - * @param mixed PHP variable to be encoded - * @param boolean whether the output is a map or a list. - * @since 3.1.5 - * @param boolean wether to encode empty strings too. Default to false for BC. - * @return string the encoded string - */ - public static function encode($value,$toMap=true,$encodeEmptyStrings=false) - { - if(is_string($value)) - return self::quoteString($value); - else if(is_bool($value)) - return $value?'true':'false'; - else if(is_array($value)) - { - $results=''; - if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) - { - foreach($value as $k=>$v) - { - if($v!=='' || $encodeEmptyStrings) - { - if($results!=='') - $results.=','; - $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings); - } - } - return '{'.$results.'}'; - } - else - { - foreach($value as $v) - { - if($v!=='' || $encodeEmptyStrings) - { - if($results!=='') - $results.=','; - $results.=self::encode($v,$toMap, $encodeEmptyStrings); - } - } - return '['.$results.']'; - } - } - else if(is_integer($value)) - return "$value"; - else if(is_float($value)) - { - switch($value) - { - case -INF: - return 'Number.NEGATIVE_INFINITY'; - break; - case INF: - return 'Number.POSITIVE_INFINITY'; - break; - default: - $locale=localeConv(); - if($locale['decimal_point']=='.') - return "$value"; - else - return str_replace($locale['decimal_point'], '.', "$value"); - break; - } - } - else if(is_object($value)) - if ($value instanceof TJavaScriptLiteral) - return $value->toJavaScriptLiteral(); - else - return self::encode(get_object_vars($value),$toMap); - else if($value===null) - return 'null'; - else - return ''; - } - - /** - * Encodes a PHP variable into javascript string. - * This method invokes json_encode to perform the encoding. - * @param mixed variable to be encoded - * @return string encoded string - */ - public static function jsonEncode($value, $options = 0) - { - if (is_string($value) && - ($g=Prado::getApplication()->getGlobalization(false))!==null && - strtoupper($enc=$g->getCharset())!='UTF-8') - $value=iconv($enc, 'UTF-8', $value); - $s = json_encode($value,$options); - self::checkJsonError(); - return $s; - } - - /** - * Decodes a javascript string into PHP variable. - * This method invokes json_decode to perform the decoding. - * @param string string to be decoded - * @param bool whether to convert returned objects to associative arrays - * @param int recursion depth - * @return mixed decoded variable - */ - public static function jsonDecode($value, $assoc = false, $depth = 512) - { - $s= json_decode($value, $assoc, $depth); - self::checkJsonError(); - return $s; - } - - private static function checkJsonError() - { - switch ($err = json_last_error()) - { - case JSON_ERROR_NONE: - return; - break; - case JSON_ERROR_DEPTH: - $msg = 'Maximum stack depth exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Underflow or the modes mismatch'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Unexpected control character found'; - break; - case JSON_ERROR_SYNTAX: - $msg = 'Syntax error, malformed JSON'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - default: - $msg = 'Unknown error'; - break; - } - throw new Exception("JSON error ($err): $msg"); - } - - /** - * Minimize the size of a javascript script. - * This method is based on Douglas Crockford's JSMin. - * @param string code that you want to minimzie - * @return minimized version of the code - */ - public static function JSMin($code) - { - Prado::using('System.Web.Javascripts.JSMin'); - return JSMin::minify($code); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.Javascripts + */ + +/** + * TJavaScript class. + * + * TJavaScript is a utility class containing commonly-used javascript-related + * functions. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.Javascripts + * @since 3.0 + */ +class TJavaScript +{ + /** + * Renders a list of javascript files + * @param array URLs to the javascript files + * @return string rendering result + */ + public static function renderScriptFiles($files) + { + $str=''; + foreach($files as $file) + $str.= self::renderScriptFile($file); + return $str; + } + + /** + * Renders a javascript file + * @param string URL to the javascript file + * @return string rendering result + */ + public static function renderScriptFile($file) + { + return '\n"; + } + + /** + * Renders a list of javascript blocks + * @param array javascript blocks + * @return string rendering result + */ + public static function renderScriptBlocks($scripts) + { + if(count($scripts)) + return "\n"; + else + return ''; + } + + /** + * Renders javascript block + * @param string javascript block + * @return string rendering result + */ + public static function renderScriptBlock($script) + { + return "\n"; + } + + /** + * Quotes a javascript string. + * After processing, the string is safely enclosed within a pair of + * quotation marks and can serve as a javascript string. + * @param string string to be quoted + * @return string the quoted string + */ + public static function quoteString($js) + { + return self::jsonEncode($js,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); + } + + /** + * @return Marks a string as a javascript function. Once marke, the string is considered as a + * raw javascript function that is not supposed to be encoded by {@link encode} + */ + public static function quoteJsLiteral($js) + { + if($js instanceof TJavaScriptLiteral) + return $js; + else + return new TJavaScriptLiteral($js); + } + + /** + * Deprecated, use {@link quoteJsLiteral} instead + */ + public static function quoteFunction($js) + { + return self::quoteJsLiteral($js); + } + + /** + * @return boolean true if the parameter is marked as a javascript function, i.e. if it's considered as a + * raw javascript function that is not supposed to be encoded by {@link encode} + */ + public static function isJsLiteral($js) + { + return ($js instanceof TJavaScriptLiteral); + } + + /** + * Deprecated, use {@link isJsLiteral} instead + */ + public static function isFunction($js) + { + return self::isJsLiteral($js); + } + + /** + * Encodes a PHP variable into javascript representation. + * + * Example: + * + * $options['onLoading'] = "doit"; + * $options['onComplete'] = "more"; + * echo TJavaScript::encode($options); + * //expects the following javascript code + * // {'onLoading':'doit','onComplete':'more'} + * + * + * For higher complexity data structures use {@link jsonEncode} and {@link jsonDecode} + * to serialize and unserialize. + * + * @param mixed PHP variable to be encoded + * @param boolean whether the output is a map or a list. + * @since 3.1.5 + * @param boolean wether to encode empty strings too. Default to false for BC. + * @return string the encoded string + */ + public static function encode($value,$toMap=true,$encodeEmptyStrings=false) + { + if(is_string($value)) + return self::quoteString($value); + else if(is_bool($value)) + return $value?'true':'false'; + else if(is_array($value)) + { + $results=''; + if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) + { + foreach($value as $k=>$v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings); + } + } + return '{'.$results.'}'; + } + else + { + foreach($value as $v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.=self::encode($v,$toMap, $encodeEmptyStrings); + } + } + return '['.$results.']'; + } + } + else if(is_integer($value)) + return "$value"; + else if(is_float($value)) + { + switch($value) + { + case -INF: + return 'Number.NEGATIVE_INFINITY'; + break; + case INF: + return 'Number.POSITIVE_INFINITY'; + break; + default: + $locale=localeConv(); + if($locale['decimal_point']=='.') + return "$value"; + else + return str_replace($locale['decimal_point'], '.', "$value"); + break; + } + } + else if(is_object($value)) + if ($value instanceof TJavaScriptLiteral) + return $value->toJavaScriptLiteral(); + else + return self::encode(get_object_vars($value),$toMap); + else if($value===null) + return 'null'; + else + return ''; + } + + /** + * Encodes a PHP variable into javascript string. + * This method invokes json_encode to perform the encoding. + * @param mixed variable to be encoded + * @return string encoded string + */ + public static function jsonEncode($value, $options = 0) + { + if (is_string($value) && + ($g=Prado::getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + $value=iconv($enc, 'UTF-8', $value); + $s = json_encode($value,$options); + self::checkJsonError(); + return $s; + } + + /** + * Decodes a javascript string into PHP variable. + * This method invokes json_decode to perform the decoding. + * @param string string to be decoded + * @param bool whether to convert returned objects to associative arrays + * @param int recursion depth + * @return mixed decoded variable + */ + public static function jsonDecode($value, $assoc = false, $depth = 512) + { + $s= json_decode($value, $assoc, $depth); + self::checkJsonError(); + return $s; + } + + private static function checkJsonError() + { + switch ($err = json_last_error()) + { + case JSON_ERROR_NONE: + return; + break; + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + break; + } + throw new Exception("JSON error ($err): $msg"); + } + + /** + * Minimize the size of a javascript script. + * This method is based on Douglas Crockford's JSMin. + * @param string code that you want to minimzie + * @return minimized version of the code + */ + public static function JSMin($code) + { + Prado::using('System.Web.Javascripts.JSMin'); + return JSMin::minify($code); + } +} + diff --git a/framework/Web/Javascripts/packages.php b/framework/Web/Javascripts/packages.php index 316716d6..4e4d3994 100644 --- a/framework/Web/Javascripts/packages.php +++ b/framework/Web/Javascripts/packages.php @@ -1,121 +1,121 @@ - array( - PROTOTYPE_DIR.'/prototype.js', - SCRIPTACULOUS_DIR.'/builder.js', - ), - 'prado' => array( - 'prado/prado.js', - 'prado/scriptaculous-adapter.js', - 'prado/controls/controls.js', - SCRIPTACULOUS_DIR.'/effects.js' - ), - - 'effects' => array( - SCRIPTACULOUS_DIR.'/effects.js' - ), - - 'logger' => array( - 'prado/logger/logger.js', - ), - - 'validator' => array( - 'prado/validator/validation3.js' - ), - - 'datepicker' => array( - 'prado/datepicker/datepicker.js' - ), - - 'colorpicker' => array( - 'prado/colorpicker/colorpicker.js' - ), - - 'ajax' => array( - SCRIPTACULOUS_DIR.'/controls.js', - 'prado/activecontrols/json2.js', - 'prado/activecontrols/ajax3.js', - 'prado/activecontrols/activecontrols3.js', - ), - - 'dragdrop'=>array( - SCRIPTACULOUS_DIR.'/dragdrop.js', - 'prado/activecontrols/dragdrop.js' - ), - - 'dragdropextra'=>array( - 'prado/activecontrols/dragdropextra.js', - ), - - 'slider'=>array( - 'prado/controls/slider.js' - ), - - 'keyboard'=>array( - 'prado/controls/keyboard.js' - ), - - 'tabpanel'=>array( - 'prado/controls/tabpanel.js' - ), - - 'activedatepicker' => array( - 'prado/activecontrols/activedatepicker.js' - ), - - 'activefileupload' => array( - 'prado/activefileupload/activefileupload.js' - ), - - 'accordion'=>array( - 'prado/controls/accordion.js' - ), - - 'htmlarea'=>array( - 'prado/controls/htmlarea.js' - ), - - 'ratings' => array( - 'prado/ratings/ratings.js', - ), - - 'inlineeditor' => array( - 'prado/activecontrols/inlineeditor.js' - ), - -); - - -//package names and their dependencies -$dependencies = array( - 'prototype' => array('prototype'), - 'prado' => array('prototype', 'prado'), - 'effects' => array('prototype', 'prado', 'effects'), - 'validator' => array('prototype', 'prado', 'validator'), - 'logger' => array('prototype', 'prado', 'logger'), - 'datepicker' => array('prototype', 'prado', 'datepicker'), - 'colorpicker' => array('prototype', 'prado', 'colorpicker'), - 'ajax' => array('prototype', 'prado', 'effects', 'ajax'), - 'dragdrop' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop'), - 'slider' => array('prototype', 'prado', 'slider'), - 'keyboard' => array('prototype', 'prado', 'keyboard'), - 'tabpanel' => array('prototype', 'prado', 'tabpanel'), - 'activedatepicker' => array('prototype', 'prado', 'datepicker', 'ajax', 'activedatepicker'), - 'activefileupload' => array('prototype', 'prado', 'effects', 'ajax', 'activefileupload'), - 'dragdropextra' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop','dragdropextra'), - 'accordion' => array('prototype', 'prado', 'effects', 'accordion'), - 'htmlarea' => array('prototype', 'prado', 'htmlarea'), - 'ratings' => array('prototype', 'prado', 'effects', 'ajax', 'ratings'), - 'inlineeditor' => array('prototype', 'prado', 'effects', 'ajax', 'inlineeditor'), -); - -return array($packages, $dependencies); - + array( + PROTOTYPE_DIR.'/prototype.js', + SCRIPTACULOUS_DIR.'/builder.js', + ), + 'prado' => array( + 'prado/prado.js', + 'prado/scriptaculous-adapter.js', + 'prado/controls/controls.js', + SCRIPTACULOUS_DIR.'/effects.js' + ), + + 'effects' => array( + SCRIPTACULOUS_DIR.'/effects.js' + ), + + 'logger' => array( + 'prado/logger/logger.js', + ), + + 'validator' => array( + 'prado/validator/validation3.js' + ), + + 'datepicker' => array( + 'prado/datepicker/datepicker.js' + ), + + 'colorpicker' => array( + 'prado/colorpicker/colorpicker.js' + ), + + 'ajax' => array( + SCRIPTACULOUS_DIR.'/controls.js', + 'prado/activecontrols/json2.js', + 'prado/activecontrols/ajax3.js', + 'prado/activecontrols/activecontrols3.js', + ), + + 'dragdrop'=>array( + SCRIPTACULOUS_DIR.'/dragdrop.js', + 'prado/activecontrols/dragdrop.js' + ), + + 'dragdropextra'=>array( + 'prado/activecontrols/dragdropextra.js', + ), + + 'slider'=>array( + 'prado/controls/slider.js' + ), + + 'keyboard'=>array( + 'prado/controls/keyboard.js' + ), + + 'tabpanel'=>array( + 'prado/controls/tabpanel.js' + ), + + 'activedatepicker' => array( + 'prado/activecontrols/activedatepicker.js' + ), + + 'activefileupload' => array( + 'prado/activefileupload/activefileupload.js' + ), + + 'accordion'=>array( + 'prado/controls/accordion.js' + ), + + 'htmlarea'=>array( + 'prado/controls/htmlarea.js' + ), + + 'ratings' => array( + 'prado/ratings/ratings.js', + ), + + 'inlineeditor' => array( + 'prado/activecontrols/inlineeditor.js' + ), + +); + + +//package names and their dependencies +$dependencies = array( + 'prototype' => array('prototype'), + 'prado' => array('prototype', 'prado'), + 'effects' => array('prototype', 'prado', 'effects'), + 'validator' => array('prototype', 'prado', 'validator'), + 'logger' => array('prototype', 'prado', 'logger'), + 'datepicker' => array('prototype', 'prado', 'datepicker'), + 'colorpicker' => array('prototype', 'prado', 'colorpicker'), + 'ajax' => array('prototype', 'prado', 'effects', 'ajax'), + 'dragdrop' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop'), + 'slider' => array('prototype', 'prado', 'slider'), + 'keyboard' => array('prototype', 'prado', 'keyboard'), + 'tabpanel' => array('prototype', 'prado', 'tabpanel'), + 'activedatepicker' => array('prototype', 'prado', 'datepicker', 'ajax', 'activedatepicker'), + 'activefileupload' => array('prototype', 'prado', 'effects', 'ajax', 'activefileupload'), + 'dragdropextra' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop','dragdropextra'), + 'accordion' => array('prototype', 'prado', 'effects', 'accordion'), + 'htmlarea' => array('prototype', 'prado', 'htmlarea'), + 'ratings' => array('prototype', 'prado', 'effects', 'ajax', 'ratings'), + 'inlineeditor' => array('prototype', 'prado', 'effects', 'ajax', 'inlineeditor'), +); + +return array($packages, $dependencies); + diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js index 7dbdbe2a..3e6fe5b7 100644 --- a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js @@ -1,410 +1,410 @@ -/** - * Generic postback control. - */ -Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl, -{ - onPostBack : function(event, options) - { - var request = new Prado.CallbackRequest(options.EventTarget, options); - request.dispatch(); - Event.stop(event); - } -}); - -/** - * TActiveButton control. - */ -Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl); -/** - * TActiveLinkButton control. - */ -Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl); - -Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton, -{ - onPostBack : function(event, options) - { - this.addXYInput(event,options); - var request = new Prado.CallbackRequest(options.EventTarget, options); - request.dispatch(); - Event.stop(event); - this.removeXYInput(event,options); - } -}); -/** - * Active check box. - */ -Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl, -{ - onPostBack : function(event, options) - { - var request = new Prado.CallbackRequest(options.EventTarget, options); - if(request.dispatch()==false) - Event.stop(event); - } -}); - -/** - * TActiveRadioButton control. - */ -Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox); - - -Prado.WebUI.TActiveCheckBoxList = Base.extend( -{ - constructor : function(options) - { - Prado.Registry.set(options.ListID, this); - for(var i = 0; i 0) - { - this.hasResults = true; - this.updateChoices(result); - } - else - { - this.active = false; - this.hasResults = false; - this.hide(); - } - } - } -}); - -/** - * Time Triggered Callback class. - */ -Prado.WebUI.TTimeTriggeredCallback = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - this.options = Object.extend({ Interval : 1 }, options || {}); - Prado.WebUI.TTimeTriggeredCallback.registerTimer(this); - }, - - startTimer : function() - { - if(typeof(this.timer) == 'undefined' || this.timer == null) - this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); - }, - - stopTimer : function() - { - if(typeof(this.timer) != 'undefined') - { - this.clearInterval(this.timer); - this.timer = null; - } - }, - - resetTimer : function() - { - if(typeof(this.timer) != 'undefined') - { - this.clearInterval(this.timer); - this.timer = null; - this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); - } - }, - - onTimerEvent : function() - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - }, - - setTimerInterval : function(value) - { - if (this.options.Interval != value){ - this.options.Interval = value; - this.resetTimer(); - } - }, - - onDone: function() - { - this.stopTimer(); - } -}); - -Object.extend(Prado.WebUI.TTimeTriggeredCallback, -{ - - //class methods - - timers : {}, - - registerTimer : function(timer) - { - Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer; - }, - - start : function(id) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer(); - }, - - stop : function(id) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer(); - }, - - setTimerInterval : function (id,value) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].setTimerInterval(value); - } -}); - -Prado.WebUI.ActiveListControl = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - if(this.element) - { - this.options = options; - this.observe(this.element, "change", this.doCallback.bind(this)); - } - }, - - doCallback : function(event) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - Event.stop(event); - } -}); - -Prado.WebUI.TActiveDropDownList = Class.create(Prado.WebUI.ActiveListControl); -Prado.WebUI.TActiveListBox = Class.create(Prado.WebUI.ActiveListControl); - -/** - * Observe event of a particular control to trigger a callback request. - */ -Prado.WebUI.TEventTriggeredCallback = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - this.options = options || {} ; - var element = $(options['ControlID']); - if(element) - this.observe(element, this.getEventName(element), this.doCallback.bind(this)); - }, - - getEventName : function(element) - { - var name = this.options.EventName; - if(typeof(name) == "undefined" && element.type) - { - switch (element.type.toLowerCase()) - { - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': - return 'change'; - } - } - return typeof(name) == "undefined" || name == "undefined" ? 'click' : name; - }, - - doCallback : function(event) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - if(this.options.StopEvent == true) - Event.stop(event); - } -}); - -/** - * Observe changes to a property of a particular control to trigger a callback. - */ -Prado.WebUI.TValueTriggeredCallback = Class.create(Prado.WebUI.Control, -{ - count : 1, - - observing : true, - - onInit : function(options) - { - this.options = options || {} ; - this.options.PropertyName = this.options.PropertyName || 'value'; - var element = $(options['ControlID']); - this.value = element ? element[this.options.PropertyName] : undefined; - Prado.WebUI.TValueTriggeredCallback.register(this); - this.startObserving(); - }, - - stopObserving : function() - { - this.clearTimeout(this.timer); - this.observing = false; - }, - - startObserving : function() - { - this.timer = this.setTimeout(this.checkChanges.bind(this), this.options.Interval*1000); - }, - - checkChanges : function() - { - var element = $(this.options.ControlID); - if(element) - { - var value = element[this.options.PropertyName]; - if(this.value != value) - { - this.doCallback(this.value, value); - this.value = value; - this.count=1; - } - else - this.count = this.count + this.options.Decay; - if(this.observing) - this.time = this.setTimeout(this.checkChanges.bind(this), - parseInt(this.options.Interval*1000*this.count)); - } - }, - - doCallback : function(oldValue, newValue) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - var param = {'OldValue' : oldValue, 'NewValue' : newValue}; - request.setCallbackParameter(param); - request.dispatch(); - }, - - onDone : function() - { - if (this.observing) - this.stopObserving(); - } -}); - -Object.extend(Prado.WebUI.TValueTriggeredCallback, -{ - //class methods - - timers : {}, - - register : function(timer) - { - Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer; - }, - - stop : function(id) - { - Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving(); - } -}); - -Prado.WebUI.TActiveTableCell = Class.create(Prado.WebUI.CallbackControl); -Prado.WebUI.TActiveTableRow = Class.create(Prado.WebUI.CallbackControl); +/** + * Generic postback control. + */ +Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl, +{ + onPostBack : function(event, options) + { + var request = new Prado.CallbackRequest(options.EventTarget, options); + request.dispatch(); + Event.stop(event); + } +}); + +/** + * TActiveButton control. + */ +Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl); +/** + * TActiveLinkButton control. + */ +Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl); + +Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton, +{ + onPostBack : function(event, options) + { + this.addXYInput(event,options); + var request = new Prado.CallbackRequest(options.EventTarget, options); + request.dispatch(); + Event.stop(event); + this.removeXYInput(event,options); + } +}); +/** + * Active check box. + */ +Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl, +{ + onPostBack : function(event, options) + { + var request = new Prado.CallbackRequest(options.EventTarget, options); + if(request.dispatch()==false) + Event.stop(event); + } +}); + +/** + * TActiveRadioButton control. + */ +Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox); + + +Prado.WebUI.TActiveCheckBoxList = Base.extend( +{ + constructor : function(options) + { + Prado.Registry.set(options.ListID, this); + for(var i = 0; i 0) + { + this.hasResults = true; + this.updateChoices(result); + } + else + { + this.active = false; + this.hasResults = false; + this.hide(); + } + } + } +}); + +/** + * Time Triggered Callback class. + */ +Prado.WebUI.TTimeTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = Object.extend({ Interval : 1 }, options || {}); + Prado.WebUI.TTimeTriggeredCallback.registerTimer(this); + }, + + startTimer : function() + { + if(typeof(this.timer) == 'undefined' || this.timer == null) + this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); + }, + + stopTimer : function() + { + if(typeof(this.timer) != 'undefined') + { + this.clearInterval(this.timer); + this.timer = null; + } + }, + + resetTimer : function() + { + if(typeof(this.timer) != 'undefined') + { + this.clearInterval(this.timer); + this.timer = null; + this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); + } + }, + + onTimerEvent : function() + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + }, + + setTimerInterval : function(value) + { + if (this.options.Interval != value){ + this.options.Interval = value; + this.resetTimer(); + } + }, + + onDone: function() + { + this.stopTimer(); + } +}); + +Object.extend(Prado.WebUI.TTimeTriggeredCallback, +{ + + //class methods + + timers : {}, + + registerTimer : function(timer) + { + Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer; + }, + + start : function(id) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer(); + }, + + stop : function(id) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer(); + }, + + setTimerInterval : function (id,value) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].setTimerInterval(value); + } +}); + +Prado.WebUI.ActiveListControl = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + if(this.element) + { + this.options = options; + this.observe(this.element, "change", this.doCallback.bind(this)); + } + }, + + doCallback : function(event) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + Event.stop(event); + } +}); + +Prado.WebUI.TActiveDropDownList = Class.create(Prado.WebUI.ActiveListControl); +Prado.WebUI.TActiveListBox = Class.create(Prado.WebUI.ActiveListControl); + +/** + * Observe event of a particular control to trigger a callback request. + */ +Prado.WebUI.TEventTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = options || {} ; + var element = $(options['ControlID']); + if(element) + this.observe(element, this.getEventName(element), this.doCallback.bind(this)); + }, + + getEventName : function(element) + { + var name = this.options.EventName; + if(typeof(name) == "undefined" && element.type) + { + switch (element.type.toLowerCase()) + { + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + return 'change'; + } + } + return typeof(name) == "undefined" || name == "undefined" ? 'click' : name; + }, + + doCallback : function(event) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + if(this.options.StopEvent == true) + Event.stop(event); + } +}); + +/** + * Observe changes to a property of a particular control to trigger a callback. + */ +Prado.WebUI.TValueTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + count : 1, + + observing : true, + + onInit : function(options) + { + this.options = options || {} ; + this.options.PropertyName = this.options.PropertyName || 'value'; + var element = $(options['ControlID']); + this.value = element ? element[this.options.PropertyName] : undefined; + Prado.WebUI.TValueTriggeredCallback.register(this); + this.startObserving(); + }, + + stopObserving : function() + { + this.clearTimeout(this.timer); + this.observing = false; + }, + + startObserving : function() + { + this.timer = this.setTimeout(this.checkChanges.bind(this), this.options.Interval*1000); + }, + + checkChanges : function() + { + var element = $(this.options.ControlID); + if(element) + { + var value = element[this.options.PropertyName]; + if(this.value != value) + { + this.doCallback(this.value, value); + this.value = value; + this.count=1; + } + else + this.count = this.count + this.options.Decay; + if(this.observing) + this.time = this.setTimeout(this.checkChanges.bind(this), + parseInt(this.options.Interval*1000*this.count)); + } + }, + + doCallback : function(oldValue, newValue) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + var param = {'OldValue' : oldValue, 'NewValue' : newValue}; + request.setCallbackParameter(param); + request.dispatch(); + }, + + onDone : function() + { + if (this.observing) + this.stopObserving(); + } +}); + +Object.extend(Prado.WebUI.TValueTriggeredCallback, +{ + //class methods + + timers : {}, + + register : function(timer) + { + Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer; + }, + + stop : function(id) + { + Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving(); + } +}); + +Prado.WebUI.TActiveTableCell = Class.create(Prado.WebUI.CallbackControl); +Prado.WebUI.TActiveTableRow = Class.create(Prado.WebUI.CallbackControl); diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js index 1b0b1492..f7f63026 100755 --- a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js @@ -1,87 +1,87 @@ -/** - * TActiveDatePicker control - */ -Prado.WebUI.TActiveDatePicker = Class.create(Prado.WebUI.TDatePicker, -{ - onInit : function(options) - { - this.options = options || []; - this.control = $(options.ID); - this.dateSlot = new Array(42); - this.weekSlot = new Array(6); - this.minimalDaysInFirstWeek = 4; - this.selectedDate = this.newDate(); - this.positionMode = 'Bottom'; - - - //which element to trigger to show the calendar - if(this.options.Trigger) - { - this.trigger = $(this.options.Trigger) ; - var triggerEvent = this.options.TriggerEvent || "click"; - } - else - { - this.trigger = this.control; - var triggerEvent = this.options.TriggerEvent || "focus"; - } - - // Popup position - if(this.options.PositionMode == 'Top') - { - this.positionMode = this.options.PositionMode; - } - - Object.extend(this,options); - - if (this.options.ShowCalendar) - this.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); - - // Listen to change event - if(this.options.InputMode == "TextBox") - { - this.observe(this.control, "change", this.onDateChanged.bindEvent(this)); - } - else - { - var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); - var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); - var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); - if (day) this.observe (day, "change", this.onDateChanged.bindEvent(this)); - if (month) this.observe (month, "change", this.onDateChanged.bindEvent(this)); - if (year) this.observe (year, "change", this.onDateChanged.bindEvent(this)); - - } - - }, - - // Respond to change event on the textbox or dropdown list - // This method raises OnDateChanged event on client side if it has been defined, - // and raise the callback request - onDateChanged : function () - { - var date; - if (this.options.InputMode == "TextBox") - { - date=this.control.value; - } - else - { - var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); - if (day) day=day.selectedIndex+1; - var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); - if (month) month=month.selectedIndex; - var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); - if (year) year=year.value; - date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); - } - if (typeof(this.options.OnDateChanged) == "function") this.options.OnDateChanged(this, date); - - if(this.options['AutoPostBack']==true) - { - // Make callback request - var request = new Prado.CallbackRequest(this.options.EventTarget,this.options); - request.dispatch(); - } - } -}); +/** + * TActiveDatePicker control + */ +Prado.WebUI.TActiveDatePicker = Class.create(Prado.WebUI.TDatePicker, +{ + onInit : function(options) + { + this.options = options || []; + this.control = $(options.ID); + this.dateSlot = new Array(42); + this.weekSlot = new Array(6); + this.minimalDaysInFirstWeek = 4; + this.selectedDate = this.newDate(); + this.positionMode = 'Bottom'; + + + //which element to trigger to show the calendar + if(this.options.Trigger) + { + this.trigger = $(this.options.Trigger) ; + var triggerEvent = this.options.TriggerEvent || "click"; + } + else + { + this.trigger = this.control; + var triggerEvent = this.options.TriggerEvent || "focus"; + } + + // Popup position + if(this.options.PositionMode == 'Top') + { + this.positionMode = this.options.PositionMode; + } + + Object.extend(this,options); + + if (this.options.ShowCalendar) + this.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); + + // Listen to change event + if(this.options.InputMode == "TextBox") + { + this.observe(this.control, "change", this.onDateChanged.bindEvent(this)); + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if (day) this.observe (day, "change", this.onDateChanged.bindEvent(this)); + if (month) this.observe (month, "change", this.onDateChanged.bindEvent(this)); + if (year) this.observe (year, "change", this.onDateChanged.bindEvent(this)); + + } + + }, + + // Respond to change event on the textbox or dropdown list + // This method raises OnDateChanged event on client side if it has been defined, + // and raise the callback request + onDateChanged : function () + { + var date; + if (this.options.InputMode == "TextBox") + { + date=this.control.value; + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + if (day) day=day.selectedIndex+1; + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + if (month) month=month.selectedIndex; + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if (year) year=year.value; + date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); + } + if (typeof(this.options.OnDateChanged) == "function") this.options.OnDateChanged(this, date); + + if(this.options['AutoPostBack']==true) + { + // Make callback request + var request = new Prado.CallbackRequest(this.options.EventTarget,this.options); + request.dispatch(); + } + } +}); diff --git a/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js b/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js index 995e8e97..55ef64cb 100644 --- a/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js @@ -1,1144 +1,1144 @@ - -Prado.AjaxRequest = Class.create(); -Prado.AjaxRequest.prototype = Object.clone(Ajax.Request.prototype); - -/** - * Override Prototype's response implementation. - */ -Object.extend(Prado.AjaxRequest.prototype, -{ - /*initialize: function(request) - { - this.CallbackRequest = request; - this.transport = Ajax.getTransport(); - this.setOptions(request.options); - this.request(request.url); - },*/ - - /** - * Customize the response, dispatch onXXX response code events, and - * tries to execute response actions (javascript statements). - */ - respondToReadyState : function(readyState) - { - var event = Ajax.Request.Events[readyState]; - var transport = this.transport, json = this.getBodyDataPart(Prado.CallbackRequest.DATA_HEADER); - - if (event == 'Complete') - { - var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER); - if (redirectUrl) - document.location.href = redirectUrl; - - if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i)) - { - try - { - json = eval('(' + transport.responseText + ')'); - } - catch (e) - { - if(typeof(json) == "string") - json = Prado.CallbackRequest.decode(result); - } - } - - try - { - Prado.CallbackRequest.updatePageState(this,transport); - Prado.CallbackRequest.checkHiddenFields(this,transport); - var obj = this; - Prado.CallbackRequest.loadAssets(this,transport, function() - - { - try - { - Ajax.Responders.dispatch('on' + transport.status, obj, transport, json); - Prado.CallbackRequest.dispatchActions(transport,obj.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER)); - - ( - obj.options['on' + obj.transport.status] - || - obj.options['on' + (obj.success() ? 'Success' : 'Failure')] - || - Prototype.emptyFunction - ) (obj, json); - } - catch (e) - { - obj.dispatchException(e); - } - } - ); - } - catch (e) - { - this.dispatchException(e); - } - } - - try { - (this.options['on' + event] || Prototype.emptyFunction)(this, json); - Ajax.Responders.dispatch('on' + event, this, transport, json); - } catch (e) { - this.dispatchException(e); - } - - /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ - if (event == 'Complete') - this.transport.onreadystatechange = Prototype.emptyFunction; - }, - - /** - * Gets header data assuming JSON encoding. - * @param string header name - * @return object header data as javascript structures. - */ - getHeaderData : function(name) - { - return this.getJsonData(this.getHeader(name)); - }, - - getBodyContentPart : function(name) - { - if(typeof(this.transport.responseText)=="string") - return Prado.Element.extractContent(this.transport.responseText, name); - }, - - getJsonData : function(json) - { - try - { - return eval('(' + json + ')'); - } - catch (e) - { - if(typeof(json) == "string") - return Prado.CallbackRequest.decode(json); - } - }, - - getBodyDataPart : function(name) - { - return this.getJsonData(this.getBodyContentPart(name)); - } -}); - -/** - * Prado Callback client-side request handler. - */ -Prado.CallbackRequest = Class.create(); - -/** - * Static definitions. - */ -Object.extend(Prado.CallbackRequest, -{ - /** - * Callback request target POST field name. - */ - FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET', - /** - * Callback request parameter POST field name. - */ - FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER', - /** - * Callback request page state field name, - */ - FIELD_CALLBACK_PAGESTATE : 'PRADO_PAGESTATE', - - FIELD_POSTBACK_TARGET : 'PRADO_POSTBACK_TARGET', - - FIELD_POSTBACK_PARAMETER : 'PRADO_POSTBACK_PARAMETER', - - /** - * List of form fields that will be collected during callback. - */ - PostDataLoaders : [], - /** - * Response data header name. - */ - DATA_HEADER : 'X-PRADO-DATA', - /** - * Response javascript execution statement header name. - */ - ACTION_HEADER : 'X-PRADO-ACTIONS', - /** - * Response errors/exceptions header name. - */ - ERROR_HEADER : 'X-PRADO-ERROR', - /** - * Page state header name. - */ - PAGESTATE_HEADER : 'X-PRADO-PAGESTATE', - /** - * Script list header name. - */ - SCRIPTLIST_HEADER : 'X-PRADO-SCRIPTLIST', - /** - * Stylesheet list header name. - */ - STYLESHEETLIST_HEADER : 'X-PRADO-STYLESHEETLIST', - /** - * Hidden field list header name. - */ - HIDDENFIELDLIST_HEADER : 'X-PRADO-HIDDENFIELDLIST', - - REDIRECT_HEADER : 'X-PRADO-REDIRECT', - - requestQueue : [], - - //all request objects - requests : {}, - - getRequestById : function(id) - { - var requests = Prado.CallbackRequest.requests; - if(typeof(requests[id]) != "undefined") - return requests[id]; - }, - - dispatch : function(id) - { - var requests = Prado.CallbackRequest.requests; - if(typeof(requests[id]) != "undefined") - requests[id].dispatch(); - }, - - /** - * Add ids of inputs element to post in the request. - */ - addPostLoaders : function(ids) - { - var self = Prado.CallbackRequest; - self.PostDataLoaders = self.PostDataLoaders.concat(ids); - var list = []; - self.PostDataLoaders.each(function(id) - { - if(list.indexOf(id) < 0) - list.push(id); - }); - self.PostDataLoaders = list; - }, - - /** - * Dispatch callback response actions. - */ - dispatchActions : function(transport,actions) - { - var self = Prado.CallbackRequest; - if(actions && actions.length > 0) - actions.each(self.__run.bind(self,transport)); - }, - - /** - * Prase and evaluate a Callback clien-side action - */ - __run : function(transport, command) - { - var self = Prado.CallbackRequest; - self.transport = transport; - for(var method in command) - { - try - { - method.toFunction().apply(self,command[method]); - } - catch(e) - { - if(typeof(Logger) != "undefined") - self.Exception.onException(null,e); - else - debugger; - } - } - }, - - /** - * Respond to Prado Callback request exceptions. - */ - Exception : - { - /** - * Server returns 500 exception. Just log it. - */ - "on500" : function(request, transport, data) - { - var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER); - if (e) - Logger.error("Callback Server Error "+e.code, this.formatException(e)); - else - Logger.error("Callback Server Error Unknown",''); - }, - - /** - * Callback OnComplete event,logs reponse and data to console. - */ - 'on200' : function(request, transport, data) - { - if(transport.status < 500) - { - var msg = 'HTTP '+transport.status+" with response : \n"; - if(transport.responseText.trim().length >0) - { - var f = RegExp('()([\\s\\S\\w\\W]*)()',"m"); - msg += transport.responseText.replace(f,'') + "\n"; - } - if(typeof(data)!="undefined" && data != null) - msg += "Data : \n"+inspect(data)+"\n"; - data = request.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER); - if(data && data.length > 0) - { - msg += "Actions : \n"; - data.each(function(action) - { - msg += inspect(action)+"\n"; - }); - } - Logger.info(msg); - } - }, - - /** - * Uncaught exceptions during callback response. - */ - onException : function(request,e) - { - var msg = ""; - $H(e).each(function(item) - { - msg += item.key+": "+item.value+"\n"; - }) - Logger.error('Uncaught Callback Client Exception:', msg); - }, - - /** - * Formats the exception message for display in console. - */ - formatException : function(e) - { - var msg = e.type + " with message \""+e.message+"\""; - msg += " in "+e.file+"("+e.line+")\n"; - msg += "Stack trace:\n"; - var trace = e.trace; - for(var i = 0; i"+trace[i]["function"]+"()"+"\n"; - } - msg += e.version+" "+e.time+"\n"; - return msg; - } - }, - - /** - * @return string JSON encoded data. - */ - encode : function(data) - { - return Prado.JSON.stringify(data); - }, - - /** - * @return mixed javascript data decoded from string using JSON decoding. - */ - decode : function(data) - { - if(typeof(data) == "string" && data.trim().length > 0) - return Prado.JSON.parse(data); - else - return null; - }, - - /** - * Dispatch a normal request, no timeouts or aborting of requests. - */ - dispatchNormalRequest : function(callback) - { - callback.options.postBody = callback._getPostData(), - callback.request(callback.url); - return true; - }, - - /** - * Abort the current priority request in progress. - */ - tryNextRequest : function() - { - var self = Prado.CallbackRequest; - //Logger.debug('trying next request'); - if(typeof(self.currentRequest) == 'undefined' || self.currentRequest==null) - { - if(self.requestQueue.length > 0) - return self.dispatchQueue(); - //else - //Logger.warn('empty queque'); - } - //else - //Logger.warn('current request ' + self.currentRequest.id); - }, - - /* - * Checks which scripts are used by the response and ensures they're loaded - */ - loadScripts : function(request, transport, callback) - { - var self = Prado.CallbackRequest; - var data = request.getBodyContentPart(self.SCRIPTLIST_HEADER); - if (!this.ScriptsToLoad) this.ScriptsToLoad = new Array(); - this.ScriptLoadFinishedCallback = callback; - if (typeof(data) == "string" && data.length > 0) - { - json = Prado.CallbackRequest.decode(data); - if(typeof(json) != "object") - Logger.warn("Invalid script list:"+data); - else - for(var key in json) - if (/^\d+$/.test(key)) - { - var url = json[key]; - if (!Prado.ScriptManager.isAssetLoaded(url)) - this.ScriptsToLoad.push(url); - } - } - this.loadNextScript(); - }, - - loadNextScript: function() - { - var done = (!this.ScriptsToLoad || (this.ScriptsToLoad.length==0)); - if (!done) - { - var url = this.ScriptsToLoad.shift(); var obj = this; - if ( - Prado.ScriptManager.ensureAssetIsLoaded(url, - function() { - obj.loadNextScript(); - } - ) - ) - this.loadNextScript(); - } - else - { - if (this.ScriptLoadFinishedCallback) - { - var cb = this.ScriptLoadFinishedCallback; - this.ScriptLoadFinishedCallback = null; - cb(); - } - } - }, - - loadStyleSheetsAsync : function(request, transport) - { - var self = Prado.CallbackRequest; - var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); - if (typeof(data) == "string" && data.length > 0) - { - json = Prado.CallbackRequest.decode(data); - if(typeof(json) != "object") - Logger.warn("Invalid stylesheet list:"+data); - else - for(var key in json) - if (/^\d+$/.test(key)) - Prado.StyleSheetManager.ensureAssetIsLoaded(json[key],null); - } - }, - - loadStyleSheets : function(request, transport, callback) - { - var self = Prado.CallbackRequest; - var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); - if (!this.StyleSheetsToLoad) this.StyleSheetsToLoad = new Array(); - this.StyleSheetLoadFinishedCallback = callback; - if (typeof(data) == "string" && data.length > 0) - { - json = Prado.CallbackRequest.decode(data); - if(typeof(json) != "object") - Logger.warn("Invalid stylesheet list:"+data); - else - for(var key in json) - if (/^\d+$/.test(key)) - { - var url = json[key]; - if (!Prado.StyleSheetManager.isAssetLoaded(url)) - this.StyleSheetsToLoad.push(url); - } - } - this.loadNextStyleSheet(); - }, - - loadNextStyleSheet: function() - { - var done = (!this.StyleSheetsToLoad || (this.StyleSheetsToLoad.length==0)); - if (!done) - { - var url = this.StyleSheetsToLoad.shift(); var obj = this; - if ( - Prado.StyleSheetManager.ensureAssetIsLoaded(url, - function() { - obj.loadNextStyleSheet(); - } - ) - ) - this.loadNextStyleSheet(); - } - else - { - if (this.StyleSheetLoadFinishedCallback) - { - var cb = this.StyleSheetLoadFinishedCallback; - this.StyleSheetLoadFinishedCallback = null; - cb(); - } - } - }, - - /* - * Checks which assets are used by the response and ensures they're loaded - */ - loadAssets : function(request, transport, callback) - { - /* - - ! This is the callback-based loader for stylesheets, which loads them one-by-one, and - ! waits for all of them to be loaded before loading scripts and processing the rest of - ! the callback. - ! - ! That however is not neccessary, as stylesheets can be loaded asynchronously too. - ! - ! I leave this code here for the case that this turns out to be a compatibility issue - ! (for ex. I can imagine some scripts trying to access stylesheet properties and such) - ! so if need can be reactivated. If you do so, comment out the async stylesheet loader below! - - var obj = this; - this.loadStyleSheets(request,transport, function() { - obj.loadScripts(request,transport,callback); - }); - - */ - - this.loadStyleSheetsAsync(request,transport); - - this.loadScripts(request,transport,callback); - }, - - checkHiddenField: function(name, value) - { - var id = name.replace(':','_'); - if (!document.getElementById(id)) - { - var field = document.createElement('input'); - field.setAttribute('type','hidden'); - field.id = id; - field.name = name; - field.value = value; - document.body.appendChild(field); - } - }, - - checkHiddenFields : function(request, transport) - { - var self = Prado.CallbackRequest; - var data = request.getBodyContentPart(self.HIDDENFIELDLIST_HEADER); - if (typeof(data) == "string" && data.length > 0) - { - json = Prado.CallbackRequest.decode(data); - if(typeof(json) != "object") - Logger.warn("Invalid hidden field list:"+data); - else - for(var key in json) - this.checkHiddenField(key,json[key]); - } - }, - - /** - * Updates the page state. It will update only if EnablePageStateUpdate and - * HasPriority options are both true. - */ - updatePageState : function(request, transport) - { - var self = Prado.CallbackRequest; - var pagestate = $(self.FIELD_CALLBACK_PAGESTATE); - var enabled = request.ActiveControl.EnablePageStateUpdate && request.ActiveControl.HasPriority; - var aborted = typeof(self.currentRequest) == 'undefined' || self.currentRequest == null; - if(enabled && !aborted && pagestate) - { - var data = request.getBodyContentPart(self.PAGESTATE_HEADER); - if(typeof(data) == "string" && data.length > 0) - pagestate.value = data; - else - { - if(typeof(Logger) != "undefined") - Logger.warn("Missing page state:"+data); - //Logger.warn('## bad state: setting current request to null'); - self.endCurrentRequest(); - //self.tryNextRequest(); - return false; - } - } - self.endCurrentRequest(); - //Logger.warn('## state updated: setting current request to null'); - //self.tryNextRequest(); - return true; - }, - - enqueue : function(callback) - { - var self = Prado.CallbackRequest; - self.requestQueue.push(callback); - //Logger.warn("equeued "+callback.id+", current queque length="+self.requestQueue.length); - self.tryNextRequest(); - }, - - dispatchQueue : function() - { - var self = Prado.CallbackRequest; - //Logger.warn("dispatching queque, length="+self.requestQueue.length+" request="+self.currentRequest); - var callback = self.requestQueue.shift(); - self.currentRequest = callback; - - //get data - callback.options.postBody = callback._getPostData(), - - //callback.request = new Prado.AjaxRequest(callback); - callback.timeout = setTimeout(function() - { - //Logger.warn("priority timeout"); - self.abortRequest(callback.id); - },callback.ActiveControl.RequestTimeOut); - callback.request(callback.url); - //Logger.debug("dispatched "+self.currentRequest.id + " ...") - }, - - endCurrentRequest : function() - { - var self = Prado.CallbackRequest; - if(typeof(self.currentRequest) != 'undefined' && self.currentRequest != null) - clearTimeout(self.currentRequest.timeout); - self.currentRequest=null; - }, - - abortRequest : function(id) - { - //Logger.warn("abort id="+id); - var self = Prado.CallbackRequest; - if(typeof(self.currentRequest) != 'undefined' - && self.currentRequest != null && self.currentRequest.id == id) - { - var request = self.currentRequest; - if(request.transport.readyState < 4) - request.transport.abort(); - //Logger.warn('## aborted: setting current request to null'); - self.endCurrentRequest(); - } - self.tryNextRequest(); - } -}); - -/** - * Automatically aborts the current request when a priority request has returned. - */ -Ajax.Responders.register({onComplete : function(request) -{ - if(request && request instanceof Prado.AjaxRequest) - { - if(request.ActiveControl.HasPriority) - Prado.CallbackRequest.tryNextRequest(); - } -}}); - -//Add HTTP exception respones when logger is enabled. -Event.OnLoad(function() -{ - if(typeof Logger != "undefined") - Ajax.Responders.register(Prado.CallbackRequest.Exception); -}); - -/** - * Create and prepare a new callback request. - * Call the dispatch() method to start the callback request. - * - * request = new Prado.CallbackRequest(UniqueID, callback); - * request.dispatch(); - * - */ -Prado.CallbackRequest.prototype = Object.extend(Prado.AjaxRequest.prototype, -{ - - /** - * Prepare and inititate a callback request. - */ - initialize : function(id, options) - { - /** - * Callback URL, same url as the current page. - */ - this.url = this.getCallbackUrl(); - - this.transport = Ajax.getTransport(); - this.Enabled = true; - this.id = id; - this.randomId = this.randomString(); - - if(typeof(id)=="string"){ - Prado.CallbackRequest.requests[id+"__"+this.randomId] = this; - } - - this.setOptions(Object.extend( - { - RequestTimeOut : 30000, // 30 second timeout. - EnablePageStateUpdate : true, - HasPriority : true, - CausesValidation : true, - ValidationGroup : null, - PostInputs : true - }, options || {})); - - this.ActiveControl = this.options; - Prado.CallbackRequest.requests[id+"__"+this.randomId].ActiveControl = this.options; - }, - - /** - * Sets the request options - * @return {Array} request options. - */ - setOptions: function(options){ - - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '', - evalJSON: true, - evalJS: true - }; - - Object.extend(this.options, options || { }); - - this.options.method = this.options.method.toLowerCase(); - if(Object.isString(this.options.parameters)){ - this.options.parameters = this.options.parameters.toQueryParams(); - } - }, - - /** - * Gets the url from the forms that contains the PRADO_PAGESTATE - * @return {String} callback url. - */ - getCallbackUrl : function() - { - return $('PRADO_PAGESTATE').form.action; - }, - - /** - * Sets the request parameter - * @param {Object} parameter value - */ - setCallbackParameter : function(value) - { - var requestId = this.id+"__"+this.randomId; - this.ActiveControl['CallbackParameter'] = value; - Prado.CallbackRequest.requests[requestId].ActiveControl['CallbackParameter'] = value; - }, - - /** - * @return {Object} request paramater value. - */ - getCallbackParameter : function() - { - return Prado.CallbackRequest.requests[this.id+"__"+this.randomId].ActiveControl['CallbackParameter']; - }, - - /** - * Sets the callback request timeout. - * @param {integer} timeout in milliseconds - */ - setRequestTimeOut : function(timeout) - { - this.ActiveControl['RequestTimeOut'] = timeout; - }, - - /** - * @return {integer} request timeout in milliseconds - */ - getRequestTimeOut : function() - { - return this.ActiveControl['RequestTimeOut']; - }, - - /** - * Set true to enable validation on callback dispatch. - * @param {boolean} true to validate - */ - setCausesValidation : function(validate) - { - this.ActiveControl['CausesValidation'] = validate; - }, - - /** - * @return {boolean} validate on request dispatch - */ - getCausesValidation : function() - { - return this.ActiveControl['CausesValidation']; - }, - - /** - * Sets the validation group to validate during request dispatch. - * @param {string} validation group name - */ - setValidationGroup : function(group) - { - this.ActiveControl['ValidationGroup'] = group; - }, - - /** - * @return {string} validation group name. - */ - getValidationGroup : function() - { - return this.ActiveControl['ValidationGroup']; - }, - - /** - * Dispatch the callback request. - */ - dispatch : function() - { - //Logger.info("dispatching request"); - //trigger tinyMCE to save data. - if(typeof tinyMCE != "undefined") - tinyMCE.triggerSave(); - - if(this.ActiveControl.CausesValidation && typeof(Prado.Validation) != "undefined") - { - var form = this.ActiveControl.Form || Prado.Validation.getForm(); - if(Prado.Validation.validate(form,this.ActiveControl.ValidationGroup,this) == false) - return false; - } - - if(this.ActiveControl.onPreDispatch) - this.ActiveControl.onPreDispatch(this,null); - - if(!this.Enabled) - return; - - // Opera don't have onLoading/onLoaded state, so, simulate them just - // before sending the request. - if (Prototype.Browser.Opera) - { - if (this.ActiveControl.onLoading) - { - this.ActiveControl.onLoading(this,null); - Ajax.Responders.dispatch('onLoading',this, this.transport,null); - } - if (this.ActiveControl.onLoaded) - { - this.ActiveControl.onLoaded(this,null); - Ajax.Responders.dispatch('onLoaded',this, this.transport,null); - } - } - - var result; - if(this.ActiveControl.HasPriority) - { - return Prado.CallbackRequest.enqueue(this); - //return Prado.CallbackRequest.dispatchPriorityRequest(this); - } - else - return Prado.CallbackRequest.dispatchNormalRequest(this); - }, - - abort : function() - { - return Prado.CallbackRequest.abortRequest(this.id); - }, - - /** - * Collects the form inputs, encode the parameters, and sets the callback - * target id. The resulting string is the request content body. - * @return string request body content containing post data. - */ - _getPostData : function() - { - var data = {}; - var callback = Prado.CallbackRequest; - if(this.ActiveControl.PostInputs != false) - { - callback.PostDataLoaders.each(function(name) - { - var elements=$A(document.getElementsByName(name)); - if(elements.size() == 0) - { - name += '[]'; - elements=$A(document.getElementsByName(name)); - } - elements.each(function(element) - { - //IE will try to get elements with ID == name as well. - if(element.type && element.name == name) - { - var value = $F(element); - if(typeof(value) != "undefined" && value != null) - data[name] = value; - } - }) - }) - } - if(typeof(this.ActiveControl.CallbackParameter) != "undefined") - data[callback.FIELD_CALLBACK_PARAMETER] = callback.encode(this.getCallbackParameter()); - var pageState = $F(callback.FIELD_CALLBACK_PAGESTATE); - if(typeof(pageState) != "undefined") - data[callback.FIELD_CALLBACK_PAGESTATE] = pageState; - data[callback.FIELD_CALLBACK_TARGET] = this.id; - if(this.ActiveControl.EventTarget) - data[callback.FIELD_POSTBACK_TARGET] = this.ActiveControl.EventTarget; - if(this.ActiveControl.EventParameter) - data[callback.FIELD_POSTBACK_PARAMETER] = this.ActiveControl.EventParameter; - return $H(data).toQueryString(); - }, - - /** - * Creates a random string with a length of 8 chars. - * @return string - */ - randomString : function() - { - chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - randomString = ""; - for(x=0;x<8;x++) - randomString += chars.charAt(Math.floor(Math.random() * 62)); - return randomString - } -}); - -/** - * Create a new callback request using default settings. - * @param string callback handler unique ID. - * @param mixed parameter to pass to callback handler on the server side. - * @param function client side onSuccess event handler. - * @param object additional request options. - * @return boolean always false. - */ -Prado.Callback = function(UniqueID, parameter, onSuccess, options) -{ - var callback = - { - 'CallbackParameter' : parameter || '', - 'onSuccess' : onSuccess || Prototype.emptyFunction - }; - - Object.extend(callback, options || {}); - - var request = new Prado.CallbackRequest(UniqueID, callback); - request.dispatch(); - return false; -}; - - - -/** - * Asset manager classes for lazy loading of scripts and stylesheets - * @author Gabor Berczi (gabor.berczi@devworx.hu) - */ - -if (typeof(Prado.AssetManagerClass)=="undefined") { - - Prado.AssetManagerClass = Class.create(); - Prado.AssetManagerClass.prototype = { - - initialize: function() { - this.loadedAssets = new Array(); - this.discoverLoadedAssets(); - }, - - - /** - * Detect which assets are already loaded by page markup. - * This is done by looking up all elements and registering the values of their src attributes. - */ - discoverLoadedAssets: function() { - - // wait until document has finished loading to avoid javascript errors - if (!document.body) return; - - var assets = this.findAssetUrlsInMarkup(); - for(var i=0;i element in page header - var asset = this.createAssetElement(url); - - if (callback) - { - asset.onreadystatechange = this.assetReadyStateChanged.bind(this, url, asset, callback, false); - asset.onload = this.assetReadyStateChanged.bind(this, url, asset, callback, true); - asset.onerror = this.assetLoadFailed.bind(this, url, asset, callback); - asset.assetCallbackFired = false; - } - - var head = document.getElementsByTagName('head')[0]; - head.appendChild(asset); - - // mark this asset as loaded - this.markAssetAsLoaded(url); - - return (callback!=false); - }, - - /** - * Check whether a asset is loaded into the page, and if itsn't, load it now - * @param string url of the asset to check/load - * @return boolean returns true if asset is already loaded, or false, if loading has just started. callback will be called when loading has finished. - */ - ensureAssetIsLoaded: function(url, callback) { - url = this.makeFullUrl(url); - if (this.loadedAssets.indexOf(url)==-1) - { - this.startAssetLoad(url,callback); - return false; - } - else - return true; - } - - } - -}; - - Prado.ScriptManagerClass = Class.extend(Prado.AssetManagerClass, { - - findAssetUrlsInMarkup: function() { - var urls = new Array(); - var scripts = document.getElementsByTagName('script'); - for(var i=0;i0)) - urls.push(href); - } - return urls; - }, - - createAssetElement: function(url) { - var asset = document.createElement('link'); - asset.rel = 'stylesheet'; - asset.media = 'screen'; - asset.setAttribute('type', 'text/css'); - asset.href = url; -// asset.async = false; // HTML5 only - return asset; - } - - }); - - if (typeof(Prado.ScriptManager)=="undefined") Prado.ScriptManager = new Prado.ScriptManagerClass(); - if (typeof(Prado.StyleSheetManager)=="undefined") Prado.StyleSheetManager = new Prado.StyleSheetManagerClass(); - - // make sure we scan for loaded scripts again when the page has been loaded - var discover = function() { - Prado.ScriptManager.discoverLoadedAssets(); - Prado.StyleSheetManager.discoverLoadedAssets(); - } - if (window.attachEvent) window.attachEvent('onload', discover); - else if (window.addEventListener) window.addEventListener('load', discover, false); - + +Prado.AjaxRequest = Class.create(); +Prado.AjaxRequest.prototype = Object.clone(Ajax.Request.prototype); + +/** + * Override Prototype's response implementation. + */ +Object.extend(Prado.AjaxRequest.prototype, +{ + /*initialize: function(request) + { + this.CallbackRequest = request; + this.transport = Ajax.getTransport(); + this.setOptions(request.options); + this.request(request.url); + },*/ + + /** + * Customize the response, dispatch onXXX response code events, and + * tries to execute response actions (javascript statements). + */ + respondToReadyState : function(readyState) + { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.getBodyDataPart(Prado.CallbackRequest.DATA_HEADER); + + if (event == 'Complete') + { + var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER); + if (redirectUrl) + document.location.href = redirectUrl; + + if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i)) + { + try + { + json = eval('(' + transport.responseText + ')'); + } + catch (e) + { + if(typeof(json) == "string") + json = Prado.CallbackRequest.decode(result); + } + } + + try + { + Prado.CallbackRequest.updatePageState(this,transport); + Prado.CallbackRequest.checkHiddenFields(this,transport); + var obj = this; + Prado.CallbackRequest.loadAssets(this,transport, function() + + { + try + { + Ajax.Responders.dispatch('on' + transport.status, obj, transport, json); + Prado.CallbackRequest.dispatchActions(transport,obj.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER)); + + ( + obj.options['on' + obj.transport.status] + || + obj.options['on' + (obj.success() ? 'Success' : 'Failure')] + || + Prototype.emptyFunction + ) (obj, json); + } + catch (e) + { + obj.dispatchException(e); + } + } + ); + } + catch (e) + { + this.dispatchException(e); + } + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(this, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + /** + * Gets header data assuming JSON encoding. + * @param string header name + * @return object header data as javascript structures. + */ + getHeaderData : function(name) + { + return this.getJsonData(this.getHeader(name)); + }, + + getBodyContentPart : function(name) + { + if(typeof(this.transport.responseText)=="string") + return Prado.Element.extractContent(this.transport.responseText, name); + }, + + getJsonData : function(json) + { + try + { + return eval('(' + json + ')'); + } + catch (e) + { + if(typeof(json) == "string") + return Prado.CallbackRequest.decode(json); + } + }, + + getBodyDataPart : function(name) + { + return this.getJsonData(this.getBodyContentPart(name)); + } +}); + +/** + * Prado Callback client-side request handler. + */ +Prado.CallbackRequest = Class.create(); + +/** + * Static definitions. + */ +Object.extend(Prado.CallbackRequest, +{ + /** + * Callback request target POST field name. + */ + FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET', + /** + * Callback request parameter POST field name. + */ + FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER', + /** + * Callback request page state field name, + */ + FIELD_CALLBACK_PAGESTATE : 'PRADO_PAGESTATE', + + FIELD_POSTBACK_TARGET : 'PRADO_POSTBACK_TARGET', + + FIELD_POSTBACK_PARAMETER : 'PRADO_POSTBACK_PARAMETER', + + /** + * List of form fields that will be collected during callback. + */ + PostDataLoaders : [], + /** + * Response data header name. + */ + DATA_HEADER : 'X-PRADO-DATA', + /** + * Response javascript execution statement header name. + */ + ACTION_HEADER : 'X-PRADO-ACTIONS', + /** + * Response errors/exceptions header name. + */ + ERROR_HEADER : 'X-PRADO-ERROR', + /** + * Page state header name. + */ + PAGESTATE_HEADER : 'X-PRADO-PAGESTATE', + /** + * Script list header name. + */ + SCRIPTLIST_HEADER : 'X-PRADO-SCRIPTLIST', + /** + * Stylesheet list header name. + */ + STYLESHEETLIST_HEADER : 'X-PRADO-STYLESHEETLIST', + /** + * Hidden field list header name. + */ + HIDDENFIELDLIST_HEADER : 'X-PRADO-HIDDENFIELDLIST', + + REDIRECT_HEADER : 'X-PRADO-REDIRECT', + + requestQueue : [], + + //all request objects + requests : {}, + + getRequestById : function(id) + { + var requests = Prado.CallbackRequest.requests; + if(typeof(requests[id]) != "undefined") + return requests[id]; + }, + + dispatch : function(id) + { + var requests = Prado.CallbackRequest.requests; + if(typeof(requests[id]) != "undefined") + requests[id].dispatch(); + }, + + /** + * Add ids of inputs element to post in the request. + */ + addPostLoaders : function(ids) + { + var self = Prado.CallbackRequest; + self.PostDataLoaders = self.PostDataLoaders.concat(ids); + var list = []; + self.PostDataLoaders.each(function(id) + { + if(list.indexOf(id) < 0) + list.push(id); + }); + self.PostDataLoaders = list; + }, + + /** + * Dispatch callback response actions. + */ + dispatchActions : function(transport,actions) + { + var self = Prado.CallbackRequest; + if(actions && actions.length > 0) + actions.each(self.__run.bind(self,transport)); + }, + + /** + * Prase and evaluate a Callback clien-side action + */ + __run : function(transport, command) + { + var self = Prado.CallbackRequest; + self.transport = transport; + for(var method in command) + { + try + { + method.toFunction().apply(self,command[method]); + } + catch(e) + { + if(typeof(Logger) != "undefined") + self.Exception.onException(null,e); + else + debugger; + } + } + }, + + /** + * Respond to Prado Callback request exceptions. + */ + Exception : + { + /** + * Server returns 500 exception. Just log it. + */ + "on500" : function(request, transport, data) + { + var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER); + if (e) + Logger.error("Callback Server Error "+e.code, this.formatException(e)); + else + Logger.error("Callback Server Error Unknown",''); + }, + + /** + * Callback OnComplete event,logs reponse and data to console. + */ + 'on200' : function(request, transport, data) + { + if(transport.status < 500) + { + var msg = 'HTTP '+transport.status+" with response : \n"; + if(transport.responseText.trim().length >0) + { + var f = RegExp('()([\\s\\S\\w\\W]*)()',"m"); + msg += transport.responseText.replace(f,'') + "\n"; + } + if(typeof(data)!="undefined" && data != null) + msg += "Data : \n"+inspect(data)+"\n"; + data = request.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER); + if(data && data.length > 0) + { + msg += "Actions : \n"; + data.each(function(action) + { + msg += inspect(action)+"\n"; + }); + } + Logger.info(msg); + } + }, + + /** + * Uncaught exceptions during callback response. + */ + onException : function(request,e) + { + var msg = ""; + $H(e).each(function(item) + { + msg += item.key+": "+item.value+"\n"; + }) + Logger.error('Uncaught Callback Client Exception:', msg); + }, + + /** + * Formats the exception message for display in console. + */ + formatException : function(e) + { + var msg = e.type + " with message \""+e.message+"\""; + msg += " in "+e.file+"("+e.line+")\n"; + msg += "Stack trace:\n"; + var trace = e.trace; + for(var i = 0; i"+trace[i]["function"]+"()"+"\n"; + } + msg += e.version+" "+e.time+"\n"; + return msg; + } + }, + + /** + * @return string JSON encoded data. + */ + encode : function(data) + { + return Prado.JSON.stringify(data); + }, + + /** + * @return mixed javascript data decoded from string using JSON decoding. + */ + decode : function(data) + { + if(typeof(data) == "string" && data.trim().length > 0) + return Prado.JSON.parse(data); + else + return null; + }, + + /** + * Dispatch a normal request, no timeouts or aborting of requests. + */ + dispatchNormalRequest : function(callback) + { + callback.options.postBody = callback._getPostData(), + callback.request(callback.url); + return true; + }, + + /** + * Abort the current priority request in progress. + */ + tryNextRequest : function() + { + var self = Prado.CallbackRequest; + //Logger.debug('trying next request'); + if(typeof(self.currentRequest) == 'undefined' || self.currentRequest==null) + { + if(self.requestQueue.length > 0) + return self.dispatchQueue(); + //else + //Logger.warn('empty queque'); + } + //else + //Logger.warn('current request ' + self.currentRequest.id); + }, + + /* + * Checks which scripts are used by the response and ensures they're loaded + */ + loadScripts : function(request, transport, callback) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.SCRIPTLIST_HEADER); + if (!this.ScriptsToLoad) this.ScriptsToLoad = new Array(); + this.ScriptLoadFinishedCallback = callback; + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid script list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + { + var url = json[key]; + if (!Prado.ScriptManager.isAssetLoaded(url)) + this.ScriptsToLoad.push(url); + } + } + this.loadNextScript(); + }, + + loadNextScript: function() + { + var done = (!this.ScriptsToLoad || (this.ScriptsToLoad.length==0)); + if (!done) + { + var url = this.ScriptsToLoad.shift(); var obj = this; + if ( + Prado.ScriptManager.ensureAssetIsLoaded(url, + function() { + obj.loadNextScript(); + } + ) + ) + this.loadNextScript(); + } + else + { + if (this.ScriptLoadFinishedCallback) + { + var cb = this.ScriptLoadFinishedCallback; + this.ScriptLoadFinishedCallback = null; + cb(); + } + } + }, + + loadStyleSheetsAsync : function(request, transport) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid stylesheet list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + Prado.StyleSheetManager.ensureAssetIsLoaded(json[key],null); + } + }, + + loadStyleSheets : function(request, transport, callback) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); + if (!this.StyleSheetsToLoad) this.StyleSheetsToLoad = new Array(); + this.StyleSheetLoadFinishedCallback = callback; + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid stylesheet list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + { + var url = json[key]; + if (!Prado.StyleSheetManager.isAssetLoaded(url)) + this.StyleSheetsToLoad.push(url); + } + } + this.loadNextStyleSheet(); + }, + + loadNextStyleSheet: function() + { + var done = (!this.StyleSheetsToLoad || (this.StyleSheetsToLoad.length==0)); + if (!done) + { + var url = this.StyleSheetsToLoad.shift(); var obj = this; + if ( + Prado.StyleSheetManager.ensureAssetIsLoaded(url, + function() { + obj.loadNextStyleSheet(); + } + ) + ) + this.loadNextStyleSheet(); + } + else + { + if (this.StyleSheetLoadFinishedCallback) + { + var cb = this.StyleSheetLoadFinishedCallback; + this.StyleSheetLoadFinishedCallback = null; + cb(); + } + } + }, + + /* + * Checks which assets are used by the response and ensures they're loaded + */ + loadAssets : function(request, transport, callback) + { + /* + + ! This is the callback-based loader for stylesheets, which loads them one-by-one, and + ! waits for all of them to be loaded before loading scripts and processing the rest of + ! the callback. + ! + ! That however is not neccessary, as stylesheets can be loaded asynchronously too. + ! + ! I leave this code here for the case that this turns out to be a compatibility issue + ! (for ex. I can imagine some scripts trying to access stylesheet properties and such) + ! so if need can be reactivated. If you do so, comment out the async stylesheet loader below! + + var obj = this; + this.loadStyleSheets(request,transport, function() { + obj.loadScripts(request,transport,callback); + }); + + */ + + this.loadStyleSheetsAsync(request,transport); + + this.loadScripts(request,transport,callback); + }, + + checkHiddenField: function(name, value) + { + var id = name.replace(':','_'); + if (!document.getElementById(id)) + { + var field = document.createElement('input'); + field.setAttribute('type','hidden'); + field.id = id; + field.name = name; + field.value = value; + document.body.appendChild(field); + } + }, + + checkHiddenFields : function(request, transport) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.HIDDENFIELDLIST_HEADER); + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid hidden field list:"+data); + else + for(var key in json) + this.checkHiddenField(key,json[key]); + } + }, + + /** + * Updates the page state. It will update only if EnablePageStateUpdate and + * HasPriority options are both true. + */ + updatePageState : function(request, transport) + { + var self = Prado.CallbackRequest; + var pagestate = $(self.FIELD_CALLBACK_PAGESTATE); + var enabled = request.ActiveControl.EnablePageStateUpdate && request.ActiveControl.HasPriority; + var aborted = typeof(self.currentRequest) == 'undefined' || self.currentRequest == null; + if(enabled && !aborted && pagestate) + { + var data = request.getBodyContentPart(self.PAGESTATE_HEADER); + if(typeof(data) == "string" && data.length > 0) + pagestate.value = data; + else + { + if(typeof(Logger) != "undefined") + Logger.warn("Missing page state:"+data); + //Logger.warn('## bad state: setting current request to null'); + self.endCurrentRequest(); + //self.tryNextRequest(); + return false; + } + } + self.endCurrentRequest(); + //Logger.warn('## state updated: setting current request to null'); + //self.tryNextRequest(); + return true; + }, + + enqueue : function(callback) + { + var self = Prado.CallbackRequest; + self.requestQueue.push(callback); + //Logger.warn("equeued "+callback.id+", current queque length="+self.requestQueue.length); + self.tryNextRequest(); + }, + + dispatchQueue : function() + { + var self = Prado.CallbackRequest; + //Logger.warn("dispatching queque, length="+self.requestQueue.length+" request="+self.currentRequest); + var callback = self.requestQueue.shift(); + self.currentRequest = callback; + + //get data + callback.options.postBody = callback._getPostData(), + + //callback.request = new Prado.AjaxRequest(callback); + callback.timeout = setTimeout(function() + { + //Logger.warn("priority timeout"); + self.abortRequest(callback.id); + },callback.ActiveControl.RequestTimeOut); + callback.request(callback.url); + //Logger.debug("dispatched "+self.currentRequest.id + " ...") + }, + + endCurrentRequest : function() + { + var self = Prado.CallbackRequest; + if(typeof(self.currentRequest) != 'undefined' && self.currentRequest != null) + clearTimeout(self.currentRequest.timeout); + self.currentRequest=null; + }, + + abortRequest : function(id) + { + //Logger.warn("abort id="+id); + var self = Prado.CallbackRequest; + if(typeof(self.currentRequest) != 'undefined' + && self.currentRequest != null && self.currentRequest.id == id) + { + var request = self.currentRequest; + if(request.transport.readyState < 4) + request.transport.abort(); + //Logger.warn('## aborted: setting current request to null'); + self.endCurrentRequest(); + } + self.tryNextRequest(); + } +}); + +/** + * Automatically aborts the current request when a priority request has returned. + */ +Ajax.Responders.register({onComplete : function(request) +{ + if(request && request instanceof Prado.AjaxRequest) + { + if(request.ActiveControl.HasPriority) + Prado.CallbackRequest.tryNextRequest(); + } +}}); + +//Add HTTP exception respones when logger is enabled. +Event.OnLoad(function() +{ + if(typeof Logger != "undefined") + Ajax.Responders.register(Prado.CallbackRequest.Exception); +}); + +/** + * Create and prepare a new callback request. + * Call the dispatch() method to start the callback request. + * + * request = new Prado.CallbackRequest(UniqueID, callback); + * request.dispatch(); + * + */ +Prado.CallbackRequest.prototype = Object.extend(Prado.AjaxRequest.prototype, +{ + + /** + * Prepare and inititate a callback request. + */ + initialize : function(id, options) + { + /** + * Callback URL, same url as the current page. + */ + this.url = this.getCallbackUrl(); + + this.transport = Ajax.getTransport(); + this.Enabled = true; + this.id = id; + this.randomId = this.randomString(); + + if(typeof(id)=="string"){ + Prado.CallbackRequest.requests[id+"__"+this.randomId] = this; + } + + this.setOptions(Object.extend( + { + RequestTimeOut : 30000, // 30 second timeout. + EnablePageStateUpdate : true, + HasPriority : true, + CausesValidation : true, + ValidationGroup : null, + PostInputs : true + }, options || {})); + + this.ActiveControl = this.options; + Prado.CallbackRequest.requests[id+"__"+this.randomId].ActiveControl = this.options; + }, + + /** + * Sets the request options + * @return {Array} request options. + */ + setOptions: function(options){ + + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + if(Object.isString(this.options.parameters)){ + this.options.parameters = this.options.parameters.toQueryParams(); + } + }, + + /** + * Gets the url from the forms that contains the PRADO_PAGESTATE + * @return {String} callback url. + */ + getCallbackUrl : function() + { + return $('PRADO_PAGESTATE').form.action; + }, + + /** + * Sets the request parameter + * @param {Object} parameter value + */ + setCallbackParameter : function(value) + { + var requestId = this.id+"__"+this.randomId; + this.ActiveControl['CallbackParameter'] = value; + Prado.CallbackRequest.requests[requestId].ActiveControl['CallbackParameter'] = value; + }, + + /** + * @return {Object} request paramater value. + */ + getCallbackParameter : function() + { + return Prado.CallbackRequest.requests[this.id+"__"+this.randomId].ActiveControl['CallbackParameter']; + }, + + /** + * Sets the callback request timeout. + * @param {integer} timeout in milliseconds + */ + setRequestTimeOut : function(timeout) + { + this.ActiveControl['RequestTimeOut'] = timeout; + }, + + /** + * @return {integer} request timeout in milliseconds + */ + getRequestTimeOut : function() + { + return this.ActiveControl['RequestTimeOut']; + }, + + /** + * Set true to enable validation on callback dispatch. + * @param {boolean} true to validate + */ + setCausesValidation : function(validate) + { + this.ActiveControl['CausesValidation'] = validate; + }, + + /** + * @return {boolean} validate on request dispatch + */ + getCausesValidation : function() + { + return this.ActiveControl['CausesValidation']; + }, + + /** + * Sets the validation group to validate during request dispatch. + * @param {string} validation group name + */ + setValidationGroup : function(group) + { + this.ActiveControl['ValidationGroup'] = group; + }, + + /** + * @return {string} validation group name. + */ + getValidationGroup : function() + { + return this.ActiveControl['ValidationGroup']; + }, + + /** + * Dispatch the callback request. + */ + dispatch : function() + { + //Logger.info("dispatching request"); + //trigger tinyMCE to save data. + if(typeof tinyMCE != "undefined") + tinyMCE.triggerSave(); + + if(this.ActiveControl.CausesValidation && typeof(Prado.Validation) != "undefined") + { + var form = this.ActiveControl.Form || Prado.Validation.getForm(); + if(Prado.Validation.validate(form,this.ActiveControl.ValidationGroup,this) == false) + return false; + } + + if(this.ActiveControl.onPreDispatch) + this.ActiveControl.onPreDispatch(this,null); + + if(!this.Enabled) + return; + + // Opera don't have onLoading/onLoaded state, so, simulate them just + // before sending the request. + if (Prototype.Browser.Opera) + { + if (this.ActiveControl.onLoading) + { + this.ActiveControl.onLoading(this,null); + Ajax.Responders.dispatch('onLoading',this, this.transport,null); + } + if (this.ActiveControl.onLoaded) + { + this.ActiveControl.onLoaded(this,null); + Ajax.Responders.dispatch('onLoaded',this, this.transport,null); + } + } + + var result; + if(this.ActiveControl.HasPriority) + { + return Prado.CallbackRequest.enqueue(this); + //return Prado.CallbackRequest.dispatchPriorityRequest(this); + } + else + return Prado.CallbackRequest.dispatchNormalRequest(this); + }, + + abort : function() + { + return Prado.CallbackRequest.abortRequest(this.id); + }, + + /** + * Collects the form inputs, encode the parameters, and sets the callback + * target id. The resulting string is the request content body. + * @return string request body content containing post data. + */ + _getPostData : function() + { + var data = {}; + var callback = Prado.CallbackRequest; + if(this.ActiveControl.PostInputs != false) + { + callback.PostDataLoaders.each(function(name) + { + var elements=$A(document.getElementsByName(name)); + if(elements.size() == 0) + { + name += '[]'; + elements=$A(document.getElementsByName(name)); + } + elements.each(function(element) + { + //IE will try to get elements with ID == name as well. + if(element.type && element.name == name) + { + var value = $F(element); + if(typeof(value) != "undefined" && value != null) + data[name] = value; + } + }) + }) + } + if(typeof(this.ActiveControl.CallbackParameter) != "undefined") + data[callback.FIELD_CALLBACK_PARAMETER] = callback.encode(this.getCallbackParameter()); + var pageState = $F(callback.FIELD_CALLBACK_PAGESTATE); + if(typeof(pageState) != "undefined") + data[callback.FIELD_CALLBACK_PAGESTATE] = pageState; + data[callback.FIELD_CALLBACK_TARGET] = this.id; + if(this.ActiveControl.EventTarget) + data[callback.FIELD_POSTBACK_TARGET] = this.ActiveControl.EventTarget; + if(this.ActiveControl.EventParameter) + data[callback.FIELD_POSTBACK_PARAMETER] = this.ActiveControl.EventParameter; + return $H(data).toQueryString(); + }, + + /** + * Creates a random string with a length of 8 chars. + * @return string + */ + randomString : function() + { + chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + randomString = ""; + for(x=0;x<8;x++) + randomString += chars.charAt(Math.floor(Math.random() * 62)); + return randomString + } +}); + +/** + * Create a new callback request using default settings. + * @param string callback handler unique ID. + * @param mixed parameter to pass to callback handler on the server side. + * @param function client side onSuccess event handler. + * @param object additional request options. + * @return boolean always false. + */ +Prado.Callback = function(UniqueID, parameter, onSuccess, options) +{ + var callback = + { + 'CallbackParameter' : parameter || '', + 'onSuccess' : onSuccess || Prototype.emptyFunction + }; + + Object.extend(callback, options || {}); + + var request = new Prado.CallbackRequest(UniqueID, callback); + request.dispatch(); + return false; +}; + + + +/** + * Asset manager classes for lazy loading of scripts and stylesheets + * @author Gabor Berczi (gabor.berczi@devworx.hu) + */ + +if (typeof(Prado.AssetManagerClass)=="undefined") { + + Prado.AssetManagerClass = Class.create(); + Prado.AssetManagerClass.prototype = { + + initialize: function() { + this.loadedAssets = new Array(); + this.discoverLoadedAssets(); + }, + + + /** + * Detect which assets are already loaded by page markup. + * This is done by looking up all elements and registering the values of their src attributes. + */ + discoverLoadedAssets: function() { + + // wait until document has finished loading to avoid javascript errors + if (!document.body) return; + + var assets = this.findAssetUrlsInMarkup(); + for(var i=0;i element in page header + var asset = this.createAssetElement(url); + + if (callback) + { + asset.onreadystatechange = this.assetReadyStateChanged.bind(this, url, asset, callback, false); + asset.onload = this.assetReadyStateChanged.bind(this, url, asset, callback, true); + asset.onerror = this.assetLoadFailed.bind(this, url, asset, callback); + asset.assetCallbackFired = false; + } + + var head = document.getElementsByTagName('head')[0]; + head.appendChild(asset); + + // mark this asset as loaded + this.markAssetAsLoaded(url); + + return (callback!=false); + }, + + /** + * Check whether a asset is loaded into the page, and if itsn't, load it now + * @param string url of the asset to check/load + * @return boolean returns true if asset is already loaded, or false, if loading has just started. callback will be called when loading has finished. + */ + ensureAssetIsLoaded: function(url, callback) { + url = this.makeFullUrl(url); + if (this.loadedAssets.indexOf(url)==-1) + { + this.startAssetLoad(url,callback); + return false; + } + else + return true; + } + + } + +}; + + Prado.ScriptManagerClass = Class.extend(Prado.AssetManagerClass, { + + findAssetUrlsInMarkup: function() { + var urls = new Array(); + var scripts = document.getElementsByTagName('script'); + for(var i=0;i0)) + urls.push(href); + } + return urls; + }, + + createAssetElement: function(url) { + var asset = document.createElement('link'); + asset.rel = 'stylesheet'; + asset.media = 'screen'; + asset.setAttribute('type', 'text/css'); + asset.href = url; +// asset.async = false; // HTML5 only + return asset; + } + + }); + + if (typeof(Prado.ScriptManager)=="undefined") Prado.ScriptManager = new Prado.ScriptManagerClass(); + if (typeof(Prado.StyleSheetManager)=="undefined") Prado.StyleSheetManager = new Prado.StyleSheetManagerClass(); + + // make sure we scan for loaded scripts again when the page has been loaded + var discover = function() { + Prado.ScriptManager.discoverLoadedAssets(); + Prado.StyleSheetManager.discoverLoadedAssets(); + } + if (window.attachEvent) window.attachEvent('onload', discover); + else if (window.addEventListener) window.addEventListener('load', discover, false); + diff --git a/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js b/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js index 59c62cc2..511c2ab9 100755 --- a/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js @@ -1,61 +1,61 @@ -/** - * DropContainer control - */ - -Prado.WebUI.DropContainer = Class.create(Prado.WebUI.CallbackControl, -{ - onInit: function(options) - { - this.options = options; - Object.extend (this.options, - { - onDrop: this.onDrop.bind(this) - }); - - Droppables.add (options.ID, this.options); - }, - - onDrop: function(dragElement, dropElement, event) - { - var elementId=dragElement.id.replace(/clone_/,""); - var req = new Prado.CallbackRequest(this.options.EventTarget, this.options); - var curleft = curtop = 0; - var obj = dropElement; - if (obj.offsetParent) { - curleft = obj.offsetLeft - curtop = obj.offsetTop - while (obj = obj.offsetParent) { - curleft += obj.offsetLeft - curtop += obj.offsetTop - } - } - var scrOfX = 0, scrOfY = 0; - if( typeof( window.pageYOffset ) == 'number' ) { - //Netscape compliant - scrOfY = window.pageYOffset; - scrOfX = window.pageXOffset; - } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) { - //DOM compliant - scrOfY = document.body.scrollTop; - scrOfX = document.body.scrollLeft; - } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) { - //IE6 standards compliant mode - scrOfY = document.documentElement.scrollTop; - scrOfX = document.documentElement.scrollLeft; - } - req.setCallbackParameter({ - DragElementID : elementId, - ScreenX : event.screenX, - ScreenY : event.screenY, - OffsetX : event.offsetX || event.clientX - curleft + scrOfX, - OffsetY : event.offsetY || event.clientY - curtop + scrOfY, - ClientX : event.clientX, - ClientY : event.clientY, - AltKey : event.altKey, - CtrlKey : event.ctrlKey, - ShiftKey : event.shiftKey - }); - req.dispatch(); - - } -}); +/** + * DropContainer control + */ + +Prado.WebUI.DropContainer = Class.create(Prado.WebUI.CallbackControl, +{ + onInit: function(options) + { + this.options = options; + Object.extend (this.options, + { + onDrop: this.onDrop.bind(this) + }); + + Droppables.add (options.ID, this.options); + }, + + onDrop: function(dragElement, dropElement, event) + { + var elementId=dragElement.id.replace(/clone_/,""); + var req = new Prado.CallbackRequest(this.options.EventTarget, this.options); + var curleft = curtop = 0; + var obj = dropElement; + if (obj.offsetParent) { + curleft = obj.offsetLeft + curtop = obj.offsetTop + while (obj = obj.offsetParent) { + curleft += obj.offsetLeft + curtop += obj.offsetTop + } + } + var scrOfX = 0, scrOfY = 0; + if( typeof( window.pageYOffset ) == 'number' ) { + //Netscape compliant + scrOfY = window.pageYOffset; + scrOfX = window.pageXOffset; + } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) { + //DOM compliant + scrOfY = document.body.scrollTop; + scrOfX = document.body.scrollLeft; + } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) { + //IE6 standards compliant mode + scrOfY = document.documentElement.scrollTop; + scrOfX = document.documentElement.scrollLeft; + } + req.setCallbackParameter({ + DragElementID : elementId, + ScreenX : event.screenX, + ScreenY : event.screenY, + OffsetX : event.offsetX || event.clientX - curleft + scrOfX, + OffsetY : event.offsetY || event.clientY - curtop + scrOfY, + ClientX : event.clientX, + ClientY : event.clientY, + AltKey : event.altKey, + CtrlKey : event.ctrlKey, + ShiftKey : event.shiftKey + }); + req.dispatch(); + + } +}); diff --git a/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js b/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js index a53ac8fd..0260c219 100644 --- a/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js @@ -1,302 +1,302 @@ -Prado.WebUI.TInPlaceTextBox = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - - this.isSaving = false; - this.isEditing = false; - this.editField = null; - this.readOnly = options.ReadOnly; - - this.options = Object.extend( - { - LoadTextFromSource : false, - TextMode : 'SingleLine' - - }, options || {}); - this.element = $(this.options.ID); - Prado.WebUI.TInPlaceTextBox.register(this); - this.createEditorInput(); - this.initializeListeners(); - }, - - /** - * Initialize the listeners. - */ - initializeListeners : function() - { - this.onclickListener = this.enterEditMode.bindAsEventListener(this); - this.observe(this.element, 'click', this.onclickListener); - if (this.options.ExternalControl) - this.observe($(this.options.ExternalControl), 'click', this.onclickListener); - }, - - /** - * Changes the panel to an editable input. - * @param {Event} evt event source - */ - enterEditMode : function(evt) - { - if (this.isSaving || this.isEditing || this.readOnly) return; - this.isEditing = true; - this.onEnterEditMode(); - this.createEditorInput(); - this.showTextBox(); - this.editField.disabled = false; - if(this.options.LoadTextOnEdit) - this.loadExternalText(); - Prado.Element.focus(this.editField); - if (evt) - Event.stop(evt); - return false; - }, - - exitEditMode : function(evt) - { - this.isEditing = false; - this.isSaving = false; - this.editField.disabled = false; - this.element.innerHTML = this.editField.value; - this.showLabel(); - }, - - showTextBox : function() - { - Element.hide(this.element); - Element.show(this.editField); - }, - - showLabel : function() - { - Element.show(this.element); - Element.hide(this.editField); - }, - - /** - * Create the edit input field. - */ - createEditorInput : function() - { - if(this.editField == null) - this.createTextBox(); - - this.editField.value = this.getText(); - }, - - loadExternalText : function() - { - this.editField.disabled = true; - this.onLoadingText(); - var options = new Array('__InlineEditor_loadExternalText__', this.getText()); - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.setCausesValidation(false); - request.setCallbackParameter(options); - request.ActiveControl.onSuccess = this.onloadExternalTextSuccess.bind(this); - request.ActiveControl.onFailure = this.onloadExternalTextFailure.bind(this); - request.dispatch(); - }, - - /** - * Create a new input textbox or textarea - */ - createTextBox : function() - { - var cssClass= this.element.className || ''; - var inputName = this.options.EventTarget; - var options = {'className' : cssClass, name : inputName, id : this.options.TextBoxID}; - if(this.options.TextMode == 'SingleLine') - { - if(this.options.MaxLength > 0) - options['maxlength'] = this.options.MaxLength; - if(this.options.Columns > 0) - options['size'] = this.options.Columns; - this.editField = INPUT(options); - } - else - { - if(this.options.Rows > 0) - options['rows'] = this.options.Rows; - if(this.options.Columns > 0) - options['cols'] = this.options.Columns; - if(this.options.Wrap) - options['wrap'] = 'off'; - this.editField = TEXTAREA(options); - } - - this.editField.style.display="none"; - this.element.parentNode.insertBefore(this.editField,this.element) - - //handle return key within single line textbox - if(this.options.TextMode == 'SingleLine') - { - this.observe(this.editField, "keydown", function(e) - { - if(Event.keyCode(e) == Event.KEY_RETURN) - { - var target = Event.element(e); - if(target) - { - Event.fireEvent(target, "blur"); - Event.stop(e); - } - } - }); - } - - this.observe(this.editField, "blur", this.onTextBoxBlur.bind(this)); - this.observe(this.editField, "keypress", this.onKeyPressed.bind(this)); - }, - - /** - * @return {String} panel inner html text. - */ - getText: function() - { - return this.element.innerHTML; - }, - - /** - * Edit mode entered, calls optional event handlers. - */ - onEnterEditMode : function() - { - if(typeof(this.options.onEnterEditMode) == "function") - this.options.onEnterEditMode(this,null); - }, - - onTextBoxBlur : function(e) - { - var text = this.element.innerHTML; - if(this.options.AutoPostBack && text != this.editField.value) - { - if(this.isEditing) - this.onTextChanged(text); - } - else - { - this.element.innerHTML = this.editField.value; - this.isEditing = false; - if(this.options.AutoHide) - this.showLabel(); - } - }, - - onKeyPressed : function(e) - { - if (Event.keyCode(e) == Event.KEY_ESC) - { - this.editField.value = this.getText(); - this.isEditing = false; - if(this.options.AutoHide) - this.showLabel(); - } - else if (Event.keyCode(e) == Event.KEY_RETURN && this.options.TextMode != 'MultiLine') - Event.stop(e); - }, - - /** - * When the text input value has changed. - * @param {String} original text - */ - onTextChanged : function(text) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.setCallbackParameter(text); - request.ActiveControl.onSuccess = this.onTextChangedSuccess.bind(this); - request.ActiveControl.onFailure = this.onTextChangedFailure.bind(this); - if(request.dispatch()) - { - this.isSaving = true; - this.editField.disabled = true; - } - }, - - /** - * When loading external text. - */ - onLoadingText : function() - { - //Logger.info("on loading text"); - }, - - onloadExternalTextSuccess : function(request, parameter) - { - this.isEditing = true; - this.editField.disabled = false; - this.editField.value = this.getText(); - Prado.Element.focus(this.editField); - if(typeof(this.options.onSuccess)=="function") - this.options.onSuccess(sender,parameter); - }, - - onloadExternalTextFailure : function(request, parameter) - { - this.isSaving = false; - this.isEditing = false; - this.showLabel(); - if(typeof(this.options.onFailure)=="function") - this.options.onFailure(sender,parameter); - }, - - /** - * Text change successfully. - * @param {Object} sender - * @param {Object} parameter - */ - onTextChangedSuccess : function(sender, parameter) - { - this.isSaving = false; - this.isEditing = false; - if(this.options.AutoHide) - this.showLabel(); - this.element.innerHTML = parameter == null ? this.editField.value : parameter; - this.editField.disabled = false; - if(typeof(this.options.onSuccess)=="function") - this.options.onSuccess(sender,parameter); - }, - - onTextChangedFailure : function(sender, parameter) - { - this.editField.disabled = false; - this.isSaving = false; - this.isEditing = false; - if(typeof(this.options.onFailure)=="function") - this.options.onFailure(sender,parameter); - } -}); - - -Object.extend(Prado.WebUI.TInPlaceTextBox, -{ - //class methods - - textboxes : {}, - - register : function(obj) - { - Prado.WebUI.TInPlaceTextBox.textboxes[obj.options.TextBoxID] = obj; - }, - - setDisplayTextBox : function(id,value) - { - var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; - if(textbox) - { - if(value) - textbox.enterEditMode(null); - else - { - textbox.exitEditMode(null); - } - } - }, - - setReadOnly : function(id, value) - { - var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; - if (textbox) - { - textbox.readOnly=value; - } - } +Prado.WebUI.TInPlaceTextBox = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + + this.isSaving = false; + this.isEditing = false; + this.editField = null; + this.readOnly = options.ReadOnly; + + this.options = Object.extend( + { + LoadTextFromSource : false, + TextMode : 'SingleLine' + + }, options || {}); + this.element = $(this.options.ID); + Prado.WebUI.TInPlaceTextBox.register(this); + this.createEditorInput(); + this.initializeListeners(); + }, + + /** + * Initialize the listeners. + */ + initializeListeners : function() + { + this.onclickListener = this.enterEditMode.bindAsEventListener(this); + this.observe(this.element, 'click', this.onclickListener); + if (this.options.ExternalControl) + this.observe($(this.options.ExternalControl), 'click', this.onclickListener); + }, + + /** + * Changes the panel to an editable input. + * @param {Event} evt event source + */ + enterEditMode : function(evt) + { + if (this.isSaving || this.isEditing || this.readOnly) return; + this.isEditing = true; + this.onEnterEditMode(); + this.createEditorInput(); + this.showTextBox(); + this.editField.disabled = false; + if(this.options.LoadTextOnEdit) + this.loadExternalText(); + Prado.Element.focus(this.editField); + if (evt) + Event.stop(evt); + return false; + }, + + exitEditMode : function(evt) + { + this.isEditing = false; + this.isSaving = false; + this.editField.disabled = false; + this.element.innerHTML = this.editField.value; + this.showLabel(); + }, + + showTextBox : function() + { + Element.hide(this.element); + Element.show(this.editField); + }, + + showLabel : function() + { + Element.show(this.element); + Element.hide(this.editField); + }, + + /** + * Create the edit input field. + */ + createEditorInput : function() + { + if(this.editField == null) + this.createTextBox(); + + this.editField.value = this.getText(); + }, + + loadExternalText : function() + { + this.editField.disabled = true; + this.onLoadingText(); + var options = new Array('__InlineEditor_loadExternalText__', this.getText()); + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.setCausesValidation(false); + request.setCallbackParameter(options); + request.ActiveControl.onSuccess = this.onloadExternalTextSuccess.bind(this); + request.ActiveControl.onFailure = this.onloadExternalTextFailure.bind(this); + request.dispatch(); + }, + + /** + * Create a new input textbox or textarea + */ + createTextBox : function() + { + var cssClass= this.element.className || ''; + var inputName = this.options.EventTarget; + var options = {'className' : cssClass, name : inputName, id : this.options.TextBoxID}; + if(this.options.TextMode == 'SingleLine') + { + if(this.options.MaxLength > 0) + options['maxlength'] = this.options.MaxLength; + if(this.options.Columns > 0) + options['size'] = this.options.Columns; + this.editField = INPUT(options); + } + else + { + if(this.options.Rows > 0) + options['rows'] = this.options.Rows; + if(this.options.Columns > 0) + options['cols'] = this.options.Columns; + if(this.options.Wrap) + options['wrap'] = 'off'; + this.editField = TEXTAREA(options); + } + + this.editField.style.display="none"; + this.element.parentNode.insertBefore(this.editField,this.element) + + //handle return key within single line textbox + if(this.options.TextMode == 'SingleLine') + { + this.observe(this.editField, "keydown", function(e) + { + if(Event.keyCode(e) == Event.KEY_RETURN) + { + var target = Event.element(e); + if(target) + { + Event.fireEvent(target, "blur"); + Event.stop(e); + } + } + }); + } + + this.observe(this.editField, "blur", this.onTextBoxBlur.bind(this)); + this.observe(this.editField, "keypress", this.onKeyPressed.bind(this)); + }, + + /** + * @return {String} panel inner html text. + */ + getText: function() + { + return this.element.innerHTML; + }, + + /** + * Edit mode entered, calls optional event handlers. + */ + onEnterEditMode : function() + { + if(typeof(this.options.onEnterEditMode) == "function") + this.options.onEnterEditMode(this,null); + }, + + onTextBoxBlur : function(e) + { + var text = this.element.innerHTML; + if(this.options.AutoPostBack && text != this.editField.value) + { + if(this.isEditing) + this.onTextChanged(text); + } + else + { + this.element.innerHTML = this.editField.value; + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + } + }, + + onKeyPressed : function(e) + { + if (Event.keyCode(e) == Event.KEY_ESC) + { + this.editField.value = this.getText(); + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + } + else if (Event.keyCode(e) == Event.KEY_RETURN && this.options.TextMode != 'MultiLine') + Event.stop(e); + }, + + /** + * When the text input value has changed. + * @param {String} original text + */ + onTextChanged : function(text) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.setCallbackParameter(text); + request.ActiveControl.onSuccess = this.onTextChangedSuccess.bind(this); + request.ActiveControl.onFailure = this.onTextChangedFailure.bind(this); + if(request.dispatch()) + { + this.isSaving = true; + this.editField.disabled = true; + } + }, + + /** + * When loading external text. + */ + onLoadingText : function() + { + //Logger.info("on loading text"); + }, + + onloadExternalTextSuccess : function(request, parameter) + { + this.isEditing = true; + this.editField.disabled = false; + this.editField.value = this.getText(); + Prado.Element.focus(this.editField); + if(typeof(this.options.onSuccess)=="function") + this.options.onSuccess(sender,parameter); + }, + + onloadExternalTextFailure : function(request, parameter) + { + this.isSaving = false; + this.isEditing = false; + this.showLabel(); + if(typeof(this.options.onFailure)=="function") + this.options.onFailure(sender,parameter); + }, + + /** + * Text change successfully. + * @param {Object} sender + * @param {Object} parameter + */ + onTextChangedSuccess : function(sender, parameter) + { + this.isSaving = false; + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + this.element.innerHTML = parameter == null ? this.editField.value : parameter; + this.editField.disabled = false; + if(typeof(this.options.onSuccess)=="function") + this.options.onSuccess(sender,parameter); + }, + + onTextChangedFailure : function(sender, parameter) + { + this.editField.disabled = false; + this.isSaving = false; + this.isEditing = false; + if(typeof(this.options.onFailure)=="function") + this.options.onFailure(sender,parameter); + } +}); + + +Object.extend(Prado.WebUI.TInPlaceTextBox, +{ + //class methods + + textboxes : {}, + + register : function(obj) + { + Prado.WebUI.TInPlaceTextBox.textboxes[obj.options.TextBoxID] = obj; + }, + + setDisplayTextBox : function(id,value) + { + var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; + if(textbox) + { + if(value) + textbox.enterEditMode(null); + else + { + textbox.exitEditMode(null); + } + } + }, + + setReadOnly : function(id, value) + { + var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; + if (textbox) + { + textbox.readOnly=value; + } + } }); \ No newline at end of file diff --git a/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js b/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js index 164295cd..142745cf 100644 --- a/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js +++ b/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js @@ -1,780 +1,780 @@ -//-------------------- ricoColor.js -if(typeof(Rico) == "undefined") Rico = {}; - -Rico.Color = Class.create(); - -Rico.Color.prototype = { - - initialize: function(red, green, blue) { - this.rgb = { r: red, g : green, b : blue }; - }, - - setRed: function(r) { - this.rgb.r = r; - }, - - setGreen: function(g) { - this.rgb.g = g; - }, - - setBlue: function(b) { - this.rgb.b = b; - }, - - setHue: function(h) { - - // get an HSB model, and set the new hue... - var hsb = this.asHSB(); - hsb.h = h; - - // convert back to RGB... - this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); - }, - - setSaturation: function(s) { - // get an HSB model, and set the new hue... - var hsb = this.asHSB(); - hsb.s = s; - - // convert back to RGB and set values... - this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); - }, - - setBrightness: function(b) { - // get an HSB model, and set the new hue... - var hsb = this.asHSB(); - hsb.b = b; - - // convert back to RGB and set values... - this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); - }, - - darken: function(percent) { - var hsb = this.asHSB(); - this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); - }, - - brighten: function(percent) { - var hsb = this.asHSB(); - this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); - }, - - blend: function(other) { - this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); - this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); - this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); - }, - - isBright: function() { - var hsb = this.asHSB(); - return this.asHSB().b > 0.5; - }, - - isDark: function() { - return ! this.isBright(); - }, - - asRGB: function() { - return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; - }, - - asHex: function() { - return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); - }, - - asHSB: function() { - return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); - }, - - toString: function() { - return this.asHex(); - } - -}; - -Rico.Color.createFromHex = function(hexCode) { - - if ( hexCode.indexOf('#') == 0 ) - hexCode = hexCode.substring(1); - - var red = "ff", green = "ff", blue="ff"; - if(hexCode.length > 4) - { - red = hexCode.substring(0,2); - green = hexCode.substring(2,4); - blue = hexCode.substring(4,6); - } - else if(hexCode.length > 0 & hexCode.length < 4) - { - var r = hexCode.substring(0,1); - var g = hexCode.substring(1,2); - var b = hexCode.substring(2); - red = r+r; - green = g+g; - blue = b+b; - } - return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); -}; - -/** - * Factory method for creating a color from the background of - * an HTML element. - */ -Rico.Color.createColorFromBackground = function(elem) { - - var actualColor = Element.getStyle($(elem), "background-color"); - if ( actualColor == "transparent" && elem.parent ) - return Rico.Color.createColorFromBackground(elem.parent); - - if ( actualColor == null ) - return new Rico.Color(255,255,255); - - if ( actualColor.indexOf("rgb(") == 0 ) { - var colors = actualColor.substring(4, actualColor.length - 1 ); - var colorArray = colors.split(","); - return new Rico.Color( parseInt( colorArray[0] ), - parseInt( colorArray[1] ), - parseInt( colorArray[2] ) ); - - } - else if ( actualColor.indexOf("#") == 0 ) { - return Rico.Color.createFromHex(actualColor); - } - else - return new Rico.Color(255,255,255); -}; - -Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { - - var red = 0; - var green = 0; - var blue = 0; - - if (saturation == 0) { - red = parseInt(brightness * 255.0 + 0.5); - green = red; - blue = red; - } - else { - var h = (hue - Math.floor(hue)) * 6.0; - var f = h - Math.floor(h); - var p = brightness * (1.0 - saturation); - var q = brightness * (1.0 - saturation * f); - var t = brightness * (1.0 - (saturation * (1.0 - f))); - - switch (parseInt(h)) { - case 0: - red = (brightness * 255.0 + 0.5); - green = (t * 255.0 + 0.5); - blue = (p * 255.0 + 0.5); - break; - case 1: - red = (q * 255.0 + 0.5); - green = (brightness * 255.0 + 0.5); - blue = (p * 255.0 + 0.5); - break; - case 2: - red = (p * 255.0 + 0.5); - green = (brightness * 255.0 + 0.5); - blue = (t * 255.0 + 0.5); - break; - case 3: - red = (p * 255.0 + 0.5); - green = (q * 255.0 + 0.5); - blue = (brightness * 255.0 + 0.5); - break; - case 4: - red = (t * 255.0 + 0.5); - green = (p * 255.0 + 0.5); - blue = (brightness * 255.0 + 0.5); - break; - case 5: - red = (brightness * 255.0 + 0.5); - green = (p * 255.0 + 0.5); - blue = (q * 255.0 + 0.5); - break; - } - } - - return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; -}; - -Rico.Color.RGBtoHSB = function(r, g, b) { - - var hue; - var saturation; - var brightness; - - var cmax = (r > g) ? r : g; - if (b > cmax) - cmax = b; - - var cmin = (r < g) ? r : g; - if (b < cmin) - cmin = b; - - brightness = cmax / 255.0; - if (cmax != 0) - saturation = (cmax - cmin)/cmax; - else - saturation = 0; - - if (saturation == 0) - hue = 0; - else { - var redc = (cmax - r)/(cmax - cmin); - var greenc = (cmax - g)/(cmax - cmin); - var bluec = (cmax - b)/(cmax - cmin); - - if (r == cmax) - hue = bluec - greenc; - else if (g == cmax) - hue = 2.0 + redc - bluec; - else - hue = 4.0 + greenc - redc; - - hue = hue / 6.0; - if (hue < 0) - hue = hue + 1.0; - } - - return { h : hue, s : saturation, b : brightness }; -}; - - -Prado.WebUI.TColorPicker = Class.create(); - -Object.extend(Prado.WebUI.TColorPicker, -{ - palettes: - { - Small : [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"], - ["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"], - ["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"], - ["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"], - ["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"], - ["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"], - ["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]], - - Tiny : [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/], - ["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/], - ["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]] - }, - - UIImages : - { - 'button.gif' : 'button.gif', -// 'target_black.gif' : 'target_black.gif', -// 'target_white.gif' : 'target_white.gif', - 'background.png' : 'background.png' -// 'slider.gif' : 'slider.gif', -// 'hue.gif' : 'hue.gif' - } -}); - -Object.extend(Prado.WebUI.TColorPicker.prototype, -{ - initialize : function(options) - { - var basics = - { - Palette : 'Small', - ClassName : 'TColorPicker', - Mode : 'Basic', - OKButtonText : 'OK', - CancelButtonText : 'Cancel', - ShowColorPicker : true - } - - this.element = null; - this.showing = false; - - options = Object.extend(basics, options); - this.options = options; - this.input = $(options['ID']); - this.button = $(options['ID']+'_button'); - this._buttonOnClick = this.buttonOnClick.bind(this); - if(options['ShowColorPicker']) - Event.observe(this.button, "click", this._buttonOnClick); - Event.observe(this.input, "change", this.updatePicker.bind(this)); - - Prado.Registry.set(options.ID, this); - }, - - updatePicker : function(e) - { - var color = Rico.Color.createFromHex(this.input.value); - this.button.style.backgroundColor = color.toString(); - }, - - buttonOnClick : function(event) - { - var mode = this.options['Mode']; - if(this.element == null) - { - var constructor = mode == "Basic" ? "getBasicPickerContainer": "getFullPickerContainer" - this.element = this[constructor](this.options['ID'], this.options['Palette']) - this.input.parentNode.appendChild(this.element); - this.element.style.display = "none"; - - if(Prado.Browser().ie) - { - this.iePopUp = document.createElement('iframe'); - this.iePopUp.src = Prado.WebUI.TColorPicker.UIImages['button.gif']; - this.iePopUp.style.position = "absolute" - this.iePopUp.scrolling="no" - this.iePopUp.frameBorder="0" - this.input.parentNode.appendChild(this.iePopUp); - } - if(mode == "Full") - this.initializeFullPicker(); - } - this.show(mode); - }, - - show : function(type) - { - if(!this.showing) - { - var pos = this.input.positionedOffset(); - pos[1] += this.input.offsetHeight; - - this.element.style.top = (pos[1]-1) + "px"; - this.element.style.left = pos[0] + "px"; - this.element.style.display = "block"; - - this.ieHack(type); - - //observe for clicks on the document body - this._documentClickEvent = this.hideOnClick.bindEvent(this, type); - this._documentKeyDownEvent = this.keyPressed.bindEvent(this, type); - Event.observe(document.body, "click", this._documentClickEvent); - Event.observe(document,"keydown", this._documentKeyDownEvent); - this.showing = true; - - if(type == "Full") - { - this.observeMouseMovement(); - var color = Rico.Color.createFromHex(this.input.value); - this.inputs.oldColor.style.backgroundColor = color.asHex(); - this.setColor(color,true); - } - } - }, - - hide : function(event) - { - if(this.showing) - { - if(this.iePopUp) - this.iePopUp.style.display = "none"; - - this.element.style.display = "none"; - this.showing = false; - Event.stopObserving(document.body, "click", this._documentClickEvent); - Event.stopObserving(document,"keydown", this._documentKeyDownEvent); - - if(this._observingMouseMove) - { - Event.stopObserving(document.body, "mousemove", this._onMouseMove); - this._observingMouseMove = false; - } - } - }, - - keyPressed : function(event,type) - { - if(Event.keyCode(event) == Event.KEY_ESC) - this.hide(event,type); - }, - - hideOnClick : function(ev) - { - if(!this.showing) return; - var el = Event.element(ev); - var within = false; - do - { within = within || String(el.className).indexOf('FullColorPicker') > -1 - within = within || el == this.button; - within = within || el == this.input; - if(within) break; - el = el.parentNode; - } - while(el); - if(!within) this.hide(ev); - }, - - ieHack : function() - { - // IE hack - if(this.iePopUp) - { - this.iePopUp.style.display = "block"; - this.iePopUp.style.top = (this.element.offsetTop) + "px"; - this.iePopUp.style.left = (this.element.offsetLeft)+ "px"; - this.iePopUp.style.width = Math.abs(this.element.offsetWidth)+ "px"; - this.iePopUp.style.height = (this.element.offsetHeight + 1)+ "px"; - } - }, - - getBasicPickerContainer : function(pickerID, palette) - { - var table = TABLE({className:'basic_colors palette_'+palette},TBODY()); - var colors = Prado.WebUI.TColorPicker.palettes[palette]; - var pickerOnClick = this.cellOnClick.bind(this); - colors.each(function(color) - { - var row = document.createElement("tr"); - color.each(function(c) - { - var td = document.createElement("td"); - var img = IMG({src:Prado.WebUI.TColorPicker.UIImages['button.gif'],width:16,height:16}); - img.style.backgroundColor = "#"+c; - Event.observe(img,"click", pickerOnClick); - Event.observe(img,"mouseover", function(e) - { - Element.addClassName(Event.element(e), "pickerhover"); - }); - Event.observe(img,"mouseout", function(e) - { - Element.removeClassName(Event.element(e), "pickerhover"); - }); - td.appendChild(img); - row.appendChild(td); - }); - table.childNodes[0].appendChild(row); - }); - return DIV({className:this.options['ClassName']+" BasicColorPicker", - id:pickerID+"_picker"}, table); - }, - - cellOnClick : function(e) - { - var el = Event.element(e); - if(el.tagName.toLowerCase() != "img") - return; - var color = Rico.Color.createColorFromBackground(el); - this.updateColor(color); - }, - - updateColor : function(color) - { - this.input.value = color.toString().toUpperCase(); - this.button.style.backgroundColor = color.toString(); - if(typeof(this.onChange) == "function") - this.onChange(color); - if(this.options.OnColorSelected) - this.options.OnColorSelected(this,color); - }, - - getFullPickerContainer : function(pickerID) - { - //create the 3 buttons - this.buttons = - { - //Less : INPUT({value:'Less Colors', className:'button', type:'button'}), - OK : INPUT({value:this.options.OKButtonText, className:'button', type:'button'}), - Cancel : INPUT({value:this.options.CancelButtonText, className:'button', type:'button'}) - }; - - //create the 6 inputs - var inputs = {}; - ['H','S','V','R','G','B'].each(function(type) - { - inputs[type] = INPUT({type:'text',size:'3',maxlength:'3'}); - }); - - //create the HEX input - inputs['HEX'] = INPUT({className:'hex',type:'text',size:'6',maxlength:'6'}); - this.inputs = inputs; - - var images = Prado.WebUI.TColorPicker.UIImages; - - this.inputs['currentColor'] = SPAN({className:'currentColor'}); - this.inputs['oldColor'] = SPAN({className:'oldColor'}); - - var inputsTable = - TABLE({className:'inputs'}, TBODY(null, - TR(null, - TD({className:'currentcolor',colSpan:2}, - this.inputs['currentColor'], this.inputs['oldColor'])), - - TR(null, - TD(null,'H:'), - TD(null,this.inputs['H'], '??')), - - TR(null, - TD(null,'S:'), - TD(null,this.inputs['S'], '%')), - - TR(null, - TD(null,'V:'), - TD(null,this.inputs['V'], '%')), - - TR(null, - TD({className:'gap'},'R:'), - TD({className:'gap'},this.inputs['R'])), - - TR(null, - TD(null,'G:'), - TD(null, this.inputs['G'])), - - TR(null, - TD(null,'B:'), - TD(null, this.inputs['B'])), - - TR(null, - TD({className:'gap'},'#'), - TD({className:'gap'},this.inputs['HEX'])) - )); - - var UIimages = - { - selector : SPAN({className:'selector'}), - background : SPAN({className:'colorpanel'}), - slider : SPAN({className:'slider'}), - hue : SPAN({className:'strip'}) - } - - //png alpha channels for IE - if(Prado.Browser().ie) - { - var filter = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"; - UIimages['background'] = SPAN({className:'colorpanel',style:filter+"(src='"+images['background.png']+"' sizingMethod=scale);"}) - } - - this.inputs = Object.extend(this.inputs, UIimages); - - var pickerTable = - TABLE(null,TBODY(null, - TR({className:'selection'}, - TD({className:'colors'},UIimages['selector'],UIimages['background']), - TD({className:'hue'},UIimages['slider'],UIimages['hue']), - TD({className:'inputs'}, inputsTable) - ), - TR({className:'options'}, - TD({colSpan:3}, - this.buttons['OK'], - this.buttons['Cancel']) - ) - )); - - return DIV({className:this.options['ClassName']+" FullColorPicker", - id:pickerID+"_picker"},pickerTable); - }, - - initializeFullPicker : function() - { - var color = Rico.Color.createFromHex(this.input.value); - this.inputs.oldColor.style.backgroundColor = color.asHex(); - this.setColor(color,true); - - var i = 0; - for(var type in this.inputs) - { - Event.observe(this.inputs[type], "change", - this.onInputChanged.bindEvent(this,type)); - i++; - - if(i > 6) break; - } - - this.isMouseDownOnColor = false; - this.isMouseDownOnHue = false; - - this._onColorMouseDown = this.onColorMouseDown.bind(this); - this._onHueMouseDown = this.onHueMouseDown.bind(this); - this._onMouseUp = this.onMouseUp.bind(this); - this._onMouseMove = this.onMouseMove.bind(this); - - Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown); - Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown); - Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown); - Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown); - - Event.observe(document.body, "mouseup", this._onMouseUp); - - this.observeMouseMovement(); - - Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode'])); - Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this)); - }, - - observeMouseMovement : function() - { - if(!this._observingMouseMove) - { - Event.observe(document.body, "mousemove", this._onMouseMove); - this._observingMouseMove = true; - } - }, - - onColorMouseDown : function(ev) - { - this.isMouseDownOnColor = true; - this.onMouseMove(ev); - Event.stop(ev); - }, - - onHueMouseDown : function(ev) - { - this.isMouseDownOnHue = true; - this.onMouseMove(ev); - Event.stop(ev); - }, - - onMouseUp : function(ev) - { - this.isMouseDownOnColor = false; - this.isMouseDownOnHue = false; - Event.stop(ev); - }, - - onMouseMove : function(ev) - { - if(this.isMouseDownOnColor) - this.changeSV(ev); - if(this.isMouseDownOnHue) - this.changeH(ev); - Event.stop(ev); - }, - - changeSV : function(ev) - { - var px = Event.pointerX(ev); - var py = Event.pointerY(ev); - var pos = this.inputs.background.cumulativeOffset(); - - var x = this.truncate(px - pos[0],0,255); - var y = this.truncate(py - pos[1],0,255); - - - var s = x/255; - var b = (255-y)/255; - - var current_s = parseInt(this.inputs.S.value); - var current_b = parseInt(this.inputs.V.value); - - if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return; - - var h = this.truncate(this.inputs.H.value,0,360)/360; - - var color = new Rico.Color(); - color.rgb = Rico.Color.HSBtoRGB(h,s,b); - - - this.inputs.selector.style.left = x+"px"; - this.inputs.selector.style.top = y+"px"; - - this.inputs.currentColor.style.backgroundColor = color.asHex(); - - return this.setColor(color); - }, - - changeH : function(ev) - { - var py = Event.pointerY(ev); - var pos = this.inputs.background.cumulativeOffset(); - var y = this.truncate(py - pos[1],0,255); - - var h = (255-y)/255; - var current_h = this.truncate(this.inputs.H.value,0,360); - current_h = current_h == 0 ? 360 : current_h; - if(current_h == parseInt(h*360)) return; - - var s = parseInt(this.inputs.S.value)/100; - var b = parseInt(this.inputs.V.value)/100; - var color = new Rico.Color(); - color.rgb = Rico.Color.HSBtoRGB(h,s,b); - - var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); - hue.setSaturation(1); hue.setBrightness(1); - - this.inputs.background.style.backgroundColor = hue.asHex(); - this.inputs.currentColor.style.backgroundColor = color.asHex(); - - this.inputs.slider.style.top = this.truncate(y,0,255)+"px"; - return this.setColor(color); - - }, - - onOKClicked : function(ev) - { - var r = this.truncate(this.inputs.R.value,0,255);///255; - var g = this.truncate(this.inputs.G.value,0,255);///255; - var b = this.truncate(this.inputs.B.value,0,255);///255; - var color = new Rico.Color(r,g,b); - this.updateColor(color); - this.inputs.oldColor.style.backgroundColor = color.asHex(); - this.hide(ev); - }, - - onInputChanged : function(ev, type) - { - if(this.isMouseDownOnColor || isMouseDownOnHue) - return; - - - switch(type) - { - case "H": case "S": case "V": - var h = this.truncate(this.inputs.H.value,0,360)/360; - var s = this.truncate(this.inputs.S.value,0,100)/100; - var b = this.truncate(this.inputs.V.value,0,100)/100; - var color = new Rico.Color(); - color.rgb = Rico.Color.HSBtoRGB(h,s,b); - return this.setColor(color,true); - case "R": case "G": case "B": - var r = this.truncate(this.inputs.R.value,0,255);///255; - var g = this.truncate(this.inputs.G.value,0,255);///255; - var b = this.truncate(this.inputs.B.value,0,255);///255; - var color = new Rico.Color(r,g,b); - return this.setColor(color,true); - case "HEX": - var color = Rico.Color.createFromHex(this.inputs.HEX.value); - return this.setColor(color,true); - } - }, - - setColor : function(color, update) - { - var hsb = color.asHSB(); - - this.inputs.H.value = parseInt(hsb.h*360); - this.inputs.S.value = parseInt(hsb.s*100); - this.inputs.V.value = parseInt(hsb.b*100); - this.inputs.R.value = color.rgb.r; - this.inputs.G.value = color.rgb.g; - this.inputs.B.value = color.rgb.b; - this.inputs.HEX.value = color.asHex().substring(1).toUpperCase(); - - var images = Prado.WebUI.TColorPicker.UIImages; - - var changeCss = color.isBright() ? 'removeClassName' : 'addClassName'; - Element[changeCss](this.inputs.selector, 'target_white'); - - if(update) - this.updateSelectors(color); - }, - - updateSelectors : function(color) - { - var hsb = color.asHSB(); - var pos = [hsb.s*255, hsb.b*255, hsb.h*255]; - - this.inputs.selector.style.left = this.truncate(pos[0],0,255)+"px"; - this.inputs.selector.style.top = this.truncate(255-pos[1],0,255)+"px"; - this.inputs.slider.style.top = this.truncate(255-pos[2],0,255)+"px"; - - var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); - hue.setSaturation(1); hue.setBrightness(1); - this.inputs.background.style.backgroundColor = hue.asHex(); - this.inputs.currentColor.style.backgroundColor = color.asHex(); - }, - - truncate : function(value, min, max) - { - value = parseInt(value); - return value < min ? min : value > max ? max : value; - } -}); +//-------------------- ricoColor.js +if(typeof(Rico) == "undefined") Rico = {}; + +Rico.Color = Class.create(); + +Rico.Color.prototype = { + + initialize: function(red, green, blue) { + this.rgb = { r: red, g : green, b : blue }; + }, + + setRed: function(r) { + this.rgb.r = r; + }, + + setGreen: function(g) { + this.rgb.g = g; + }, + + setBlue: function(b) { + this.rgb.b = b; + }, + + setHue: function(h) { + + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.h = h; + + // convert back to RGB... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setSaturation: function(s) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.s = s; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setBrightness: function(b) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.b = b; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); + }, + + darken: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); + }, + + brighten: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); + }, + + blend: function(other) { + this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); + this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); + this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); + }, + + isBright: function() { + var hsb = this.asHSB(); + return this.asHSB().b > 0.5; + }, + + isDark: function() { + return ! this.isBright(); + }, + + asRGB: function() { + return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; + }, + + asHex: function() { + return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); + }, + + asHSB: function() { + return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); + }, + + toString: function() { + return this.asHex(); + } + +}; + +Rico.Color.createFromHex = function(hexCode) { + + if ( hexCode.indexOf('#') == 0 ) + hexCode = hexCode.substring(1); + + var red = "ff", green = "ff", blue="ff"; + if(hexCode.length > 4) + { + red = hexCode.substring(0,2); + green = hexCode.substring(2,4); + blue = hexCode.substring(4,6); + } + else if(hexCode.length > 0 & hexCode.length < 4) + { + var r = hexCode.substring(0,1); + var g = hexCode.substring(1,2); + var b = hexCode.substring(2); + red = r+r; + green = g+g; + blue = b+b; + } + return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); +}; + +/** + * Factory method for creating a color from the background of + * an HTML element. + */ +Rico.Color.createColorFromBackground = function(elem) { + + var actualColor = Element.getStyle($(elem), "background-color"); + if ( actualColor == "transparent" && elem.parent ) + return Rico.Color.createColorFromBackground(elem.parent); + + if ( actualColor == null ) + return new Rico.Color(255,255,255); + + if ( actualColor.indexOf("rgb(") == 0 ) { + var colors = actualColor.substring(4, actualColor.length - 1 ); + var colorArray = colors.split(","); + return new Rico.Color( parseInt( colorArray[0] ), + parseInt( colorArray[1] ), + parseInt( colorArray[2] ) ); + + } + else if ( actualColor.indexOf("#") == 0 ) { + return Rico.Color.createFromHex(actualColor); + } + else + return new Rico.Color(255,255,255); +}; + +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { + + var red = 0; + var green = 0; + var blue = 0; + + if (saturation == 0) { + red = parseInt(brightness * 255.0 + 0.5); + green = red; + blue = red; + } + else { + var h = (hue - Math.floor(hue)) * 6.0; + var f = h - Math.floor(h); + var p = brightness * (1.0 - saturation); + var q = brightness * (1.0 - saturation * f); + var t = brightness * (1.0 - (saturation * (1.0 - f))); + + switch (parseInt(h)) { + case 0: + red = (brightness * 255.0 + 0.5); + green = (t * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 1: + red = (q * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 2: + red = (p * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (t * 255.0 + 0.5); + break; + case 3: + red = (p * 255.0 + 0.5); + green = (q * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 4: + red = (t * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 5: + red = (brightness * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (q * 255.0 + 0.5); + break; + } + } + + return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; +}; + +Rico.Color.RGBtoHSB = function(r, g, b) { + + var hue; + var saturation; + var brightness; + + var cmax = (r > g) ? r : g; + if (b > cmax) + cmax = b; + + var cmin = (r < g) ? r : g; + if (b < cmin) + cmin = b; + + brightness = cmax / 255.0; + if (cmax != 0) + saturation = (cmax - cmin)/cmax; + else + saturation = 0; + + if (saturation == 0) + hue = 0; + else { + var redc = (cmax - r)/(cmax - cmin); + var greenc = (cmax - g)/(cmax - cmin); + var bluec = (cmax - b)/(cmax - cmin); + + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + + return { h : hue, s : saturation, b : brightness }; +}; + + +Prado.WebUI.TColorPicker = Class.create(); + +Object.extend(Prado.WebUI.TColorPicker, +{ + palettes: + { + Small : [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"], + ["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"], + ["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"], + ["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"], + ["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"], + ["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"], + ["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]], + + Tiny : [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/], + ["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/], + ["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]] + }, + + UIImages : + { + 'button.gif' : 'button.gif', +// 'target_black.gif' : 'target_black.gif', +// 'target_white.gif' : 'target_white.gif', + 'background.png' : 'background.png' +// 'slider.gif' : 'slider.gif', +// 'hue.gif' : 'hue.gif' + } +}); + +Object.extend(Prado.WebUI.TColorPicker.prototype, +{ + initialize : function(options) + { + var basics = + { + Palette : 'Small', + ClassName : 'TColorPicker', + Mode : 'Basic', + OKButtonText : 'OK', + CancelButtonText : 'Cancel', + ShowColorPicker : true + } + + this.element = null; + this.showing = false; + + options = Object.extend(basics, options); + this.options = options; + this.input = $(options['ID']); + this.button = $(options['ID']+'_button'); + this._buttonOnClick = this.buttonOnClick.bind(this); + if(options['ShowColorPicker']) + Event.observe(this.button, "click", this._buttonOnClick); + Event.observe(this.input, "change", this.updatePicker.bind(this)); + + Prado.Registry.set(options.ID, this); + }, + + updatePicker : function(e) + { + var color = Rico.Color.createFromHex(this.input.value); + this.button.style.backgroundColor = color.toString(); + }, + + buttonOnClick : function(event) + { + var mode = this.options['Mode']; + if(this.element == null) + { + var constructor = mode == "Basic" ? "getBasicPickerContainer": "getFullPickerContainer" + this.element = this[constructor](this.options['ID'], this.options['Palette']) + this.input.parentNode.appendChild(this.element); + this.element.style.display = "none"; + + if(Prado.Browser().ie) + { + this.iePopUp = document.createElement('iframe'); + this.iePopUp.src = Prado.WebUI.TColorPicker.UIImages['button.gif']; + this.iePopUp.style.position = "absolute" + this.iePopUp.scrolling="no" + this.iePopUp.frameBorder="0" + this.input.parentNode.appendChild(this.iePopUp); + } + if(mode == "Full") + this.initializeFullPicker(); + } + this.show(mode); + }, + + show : function(type) + { + if(!this.showing) + { + var pos = this.input.positionedOffset(); + pos[1] += this.input.offsetHeight; + + this.element.style.top = (pos[1]-1) + "px"; + this.element.style.left = pos[0] + "px"; + this.element.style.display = "block"; + + this.ieHack(type); + + //observe for clicks on the document body + this._documentClickEvent = this.hideOnClick.bindEvent(this, type); + this._documentKeyDownEvent = this.keyPressed.bindEvent(this, type); + Event.observe(document.body, "click", this._documentClickEvent); + Event.observe(document,"keydown", this._documentKeyDownEvent); + this.showing = true; + + if(type == "Full") + { + this.observeMouseMovement(); + var color = Rico.Color.createFromHex(this.input.value); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.setColor(color,true); + } + } + }, + + hide : function(event) + { + if(this.showing) + { + if(this.iePopUp) + this.iePopUp.style.display = "none"; + + this.element.style.display = "none"; + this.showing = false; + Event.stopObserving(document.body, "click", this._documentClickEvent); + Event.stopObserving(document,"keydown", this._documentKeyDownEvent); + + if(this._observingMouseMove) + { + Event.stopObserving(document.body, "mousemove", this._onMouseMove); + this._observingMouseMove = false; + } + } + }, + + keyPressed : function(event,type) + { + if(Event.keyCode(event) == Event.KEY_ESC) + this.hide(event,type); + }, + + hideOnClick : function(ev) + { + if(!this.showing) return; + var el = Event.element(ev); + var within = false; + do + { within = within || String(el.className).indexOf('FullColorPicker') > -1 + within = within || el == this.button; + within = within || el == this.input; + if(within) break; + el = el.parentNode; + } + while(el); + if(!within) this.hide(ev); + }, + + ieHack : function() + { + // IE hack + if(this.iePopUp) + { + this.iePopUp.style.display = "block"; + this.iePopUp.style.top = (this.element.offsetTop) + "px"; + this.iePopUp.style.left = (this.element.offsetLeft)+ "px"; + this.iePopUp.style.width = Math.abs(this.element.offsetWidth)+ "px"; + this.iePopUp.style.height = (this.element.offsetHeight + 1)+ "px"; + } + }, + + getBasicPickerContainer : function(pickerID, palette) + { + var table = TABLE({className:'basic_colors palette_'+palette},TBODY()); + var colors = Prado.WebUI.TColorPicker.palettes[palette]; + var pickerOnClick = this.cellOnClick.bind(this); + colors.each(function(color) + { + var row = document.createElement("tr"); + color.each(function(c) + { + var td = document.createElement("td"); + var img = IMG({src:Prado.WebUI.TColorPicker.UIImages['button.gif'],width:16,height:16}); + img.style.backgroundColor = "#"+c; + Event.observe(img,"click", pickerOnClick); + Event.observe(img,"mouseover", function(e) + { + Element.addClassName(Event.element(e), "pickerhover"); + }); + Event.observe(img,"mouseout", function(e) + { + Element.removeClassName(Event.element(e), "pickerhover"); + }); + td.appendChild(img); + row.appendChild(td); + }); + table.childNodes[0].appendChild(row); + }); + return DIV({className:this.options['ClassName']+" BasicColorPicker", + id:pickerID+"_picker"}, table); + }, + + cellOnClick : function(e) + { + var el = Event.element(e); + if(el.tagName.toLowerCase() != "img") + return; + var color = Rico.Color.createColorFromBackground(el); + this.updateColor(color); + }, + + updateColor : function(color) + { + this.input.value = color.toString().toUpperCase(); + this.button.style.backgroundColor = color.toString(); + if(typeof(this.onChange) == "function") + this.onChange(color); + if(this.options.OnColorSelected) + this.options.OnColorSelected(this,color); + }, + + getFullPickerContainer : function(pickerID) + { + //create the 3 buttons + this.buttons = + { + //Less : INPUT({value:'Less Colors', className:'button', type:'button'}), + OK : INPUT({value:this.options.OKButtonText, className:'button', type:'button'}), + Cancel : INPUT({value:this.options.CancelButtonText, className:'button', type:'button'}) + }; + + //create the 6 inputs + var inputs = {}; + ['H','S','V','R','G','B'].each(function(type) + { + inputs[type] = INPUT({type:'text',size:'3',maxlength:'3'}); + }); + + //create the HEX input + inputs['HEX'] = INPUT({className:'hex',type:'text',size:'6',maxlength:'6'}); + this.inputs = inputs; + + var images = Prado.WebUI.TColorPicker.UIImages; + + this.inputs['currentColor'] = SPAN({className:'currentColor'}); + this.inputs['oldColor'] = SPAN({className:'oldColor'}); + + var inputsTable = + TABLE({className:'inputs'}, TBODY(null, + TR(null, + TD({className:'currentcolor',colSpan:2}, + this.inputs['currentColor'], this.inputs['oldColor'])), + + TR(null, + TD(null,'H:'), + TD(null,this.inputs['H'], '??')), + + TR(null, + TD(null,'S:'), + TD(null,this.inputs['S'], '%')), + + TR(null, + TD(null,'V:'), + TD(null,this.inputs['V'], '%')), + + TR(null, + TD({className:'gap'},'R:'), + TD({className:'gap'},this.inputs['R'])), + + TR(null, + TD(null,'G:'), + TD(null, this.inputs['G'])), + + TR(null, + TD(null,'B:'), + TD(null, this.inputs['B'])), + + TR(null, + TD({className:'gap'},'#'), + TD({className:'gap'},this.inputs['HEX'])) + )); + + var UIimages = + { + selector : SPAN({className:'selector'}), + background : SPAN({className:'colorpanel'}), + slider : SPAN({className:'slider'}), + hue : SPAN({className:'strip'}) + } + + //png alpha channels for IE + if(Prado.Browser().ie) + { + var filter = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"; + UIimages['background'] = SPAN({className:'colorpanel',style:filter+"(src='"+images['background.png']+"' sizingMethod=scale);"}) + } + + this.inputs = Object.extend(this.inputs, UIimages); + + var pickerTable = + TABLE(null,TBODY(null, + TR({className:'selection'}, + TD({className:'colors'},UIimages['selector'],UIimages['background']), + TD({className:'hue'},UIimages['slider'],UIimages['hue']), + TD({className:'inputs'}, inputsTable) + ), + TR({className:'options'}, + TD({colSpan:3}, + this.buttons['OK'], + this.buttons['Cancel']) + ) + )); + + return DIV({className:this.options['ClassName']+" FullColorPicker", + id:pickerID+"_picker"},pickerTable); + }, + + initializeFullPicker : function() + { + var color = Rico.Color.createFromHex(this.input.value); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.setColor(color,true); + + var i = 0; + for(var type in this.inputs) + { + Event.observe(this.inputs[type], "change", + this.onInputChanged.bindEvent(this,type)); + i++; + + if(i > 6) break; + } + + this.isMouseDownOnColor = false; + this.isMouseDownOnHue = false; + + this._onColorMouseDown = this.onColorMouseDown.bind(this); + this._onHueMouseDown = this.onHueMouseDown.bind(this); + this._onMouseUp = this.onMouseUp.bind(this); + this._onMouseMove = this.onMouseMove.bind(this); + + Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown); + Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown); + Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown); + Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown); + + Event.observe(document.body, "mouseup", this._onMouseUp); + + this.observeMouseMovement(); + + Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode'])); + Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this)); + }, + + observeMouseMovement : function() + { + if(!this._observingMouseMove) + { + Event.observe(document.body, "mousemove", this._onMouseMove); + this._observingMouseMove = true; + } + }, + + onColorMouseDown : function(ev) + { + this.isMouseDownOnColor = true; + this.onMouseMove(ev); + Event.stop(ev); + }, + + onHueMouseDown : function(ev) + { + this.isMouseDownOnHue = true; + this.onMouseMove(ev); + Event.stop(ev); + }, + + onMouseUp : function(ev) + { + this.isMouseDownOnColor = false; + this.isMouseDownOnHue = false; + Event.stop(ev); + }, + + onMouseMove : function(ev) + { + if(this.isMouseDownOnColor) + this.changeSV(ev); + if(this.isMouseDownOnHue) + this.changeH(ev); + Event.stop(ev); + }, + + changeSV : function(ev) + { + var px = Event.pointerX(ev); + var py = Event.pointerY(ev); + var pos = this.inputs.background.cumulativeOffset(); + + var x = this.truncate(px - pos[0],0,255); + var y = this.truncate(py - pos[1],0,255); + + + var s = x/255; + var b = (255-y)/255; + + var current_s = parseInt(this.inputs.S.value); + var current_b = parseInt(this.inputs.V.value); + + if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return; + + var h = this.truncate(this.inputs.H.value,0,360)/360; + + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + + + this.inputs.selector.style.left = x+"px"; + this.inputs.selector.style.top = y+"px"; + + this.inputs.currentColor.style.backgroundColor = color.asHex(); + + return this.setColor(color); + }, + + changeH : function(ev) + { + var py = Event.pointerY(ev); + var pos = this.inputs.background.cumulativeOffset(); + var y = this.truncate(py - pos[1],0,255); + + var h = (255-y)/255; + var current_h = this.truncate(this.inputs.H.value,0,360); + current_h = current_h == 0 ? 360 : current_h; + if(current_h == parseInt(h*360)) return; + + var s = parseInt(this.inputs.S.value)/100; + var b = parseInt(this.inputs.V.value)/100; + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + + var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); + hue.setSaturation(1); hue.setBrightness(1); + + this.inputs.background.style.backgroundColor = hue.asHex(); + this.inputs.currentColor.style.backgroundColor = color.asHex(); + + this.inputs.slider.style.top = this.truncate(y,0,255)+"px"; + return this.setColor(color); + + }, + + onOKClicked : function(ev) + { + var r = this.truncate(this.inputs.R.value,0,255);///255; + var g = this.truncate(this.inputs.G.value,0,255);///255; + var b = this.truncate(this.inputs.B.value,0,255);///255; + var color = new Rico.Color(r,g,b); + this.updateColor(color); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.hide(ev); + }, + + onInputChanged : function(ev, type) + { + if(this.isMouseDownOnColor || isMouseDownOnHue) + return; + + + switch(type) + { + case "H": case "S": case "V": + var h = this.truncate(this.inputs.H.value,0,360)/360; + var s = this.truncate(this.inputs.S.value,0,100)/100; + var b = this.truncate(this.inputs.V.value,0,100)/100; + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + return this.setColor(color,true); + case "R": case "G": case "B": + var r = this.truncate(this.inputs.R.value,0,255);///255; + var g = this.truncate(this.inputs.G.value,0,255);///255; + var b = this.truncate(this.inputs.B.value,0,255);///255; + var color = new Rico.Color(r,g,b); + return this.setColor(color,true); + case "HEX": + var color = Rico.Color.createFromHex(this.inputs.HEX.value); + return this.setColor(color,true); + } + }, + + setColor : function(color, update) + { + var hsb = color.asHSB(); + + this.inputs.H.value = parseInt(hsb.h*360); + this.inputs.S.value = parseInt(hsb.s*100); + this.inputs.V.value = parseInt(hsb.b*100); + this.inputs.R.value = color.rgb.r; + this.inputs.G.value = color.rgb.g; + this.inputs.B.value = color.rgb.b; + this.inputs.HEX.value = color.asHex().substring(1).toUpperCase(); + + var images = Prado.WebUI.TColorPicker.UIImages; + + var changeCss = color.isBright() ? 'removeClassName' : 'addClassName'; + Element[changeCss](this.inputs.selector, 'target_white'); + + if(update) + this.updateSelectors(color); + }, + + updateSelectors : function(color) + { + var hsb = color.asHSB(); + var pos = [hsb.s*255, hsb.b*255, hsb.h*255]; + + this.inputs.selector.style.left = this.truncate(pos[0],0,255)+"px"; + this.inputs.selector.style.top = this.truncate(255-pos[1],0,255)+"px"; + this.inputs.slider.style.top = this.truncate(255-pos[2],0,255)+"px"; + + var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); + hue.setSaturation(1); hue.setBrightness(1); + this.inputs.background.style.backgroundColor = hue.asHex(); + this.inputs.currentColor.style.backgroundColor = color.asHex(); + }, + + truncate : function(value, min, max) + { + value = parseInt(value); + return value < min ? min : value > max ? max : value; + } +}); diff --git a/framework/Web/Javascripts/source/prado/controls/controls.js b/framework/Web/Javascripts/source/prado/controls/controls.js index bbc54e9e..fd8a4c91 100644 --- a/framework/Web/Javascripts/source/prado/controls/controls.js +++ b/framework/Web/Javascripts/source/prado/controls/controls.js @@ -1,517 +1,517 @@ -Prado.WebUI = Class.create(); - -Prado.WebUI.Control = Class.create({ - - initialize : function(options) - { - this.registered = false; - this.ID = options.ID; - this.element = $(this.ID); - this.observers = new Array(); - this.intervals = new Array(); - var e; - if (e = Prado.Registry.get(this.ID)) - this.replace(e, options); - else - this.register(options); - - if (this === Prado.Registry.get(this.ID)) - { - this.registered = true; - if(this.onInit) - this.onInit(options); - } - }, - - /** - * Registers the control wrapper in the Prado client side control registry - * @param array control wrapper options - */ - register : function(options) - { - return Prado.Registry.set(options.ID, this); - }, - - /** - * De-registers the control wrapper in the Prado client side control registry - */ - deregister : function() - { - // extra check so we don't ever deregister another wrapper - if (Prado.Registry.get(this.ID)===this) - return Prado.Registry.unset(this.ID); - else - debugger; // invoke debugger - this should never happen - }, - - /** - * Replaces and control wrapper for an already existing control in the Prado client side control registry - * @param object reference to the old wrapper - * @param array control wrapper options - */ - replace : function(oldwrapper, options) - { - // if there's some advanced state management in the wrapper going on, then - // this method could be used either to copy the current state of the control - // from the old wrapper to this new one (which then could live on, while the old - // one could get destroyed), or to copy the new, changed options to the old wrapper, - // (which could then left intact to keep working, while this new wrapper could be - // disposed of by exiting its initialization without installing any handlers or - // leaving any references to it) - // - - // for now this method is simply deinitializing and deregistering the old wrapper, - // and then registering the new wrapper for the control id - - if (oldwrapper.deinitialize) - oldwrapper.deinitialize(); - - return this.register(options); - }, - - /** - * Registers an event observer which will be automatically disposed of when the wrapper - * is deregistered - * @param element DOM element reference or id to attach the event handler to - * @param string event name to observe - * @param handler event handler function - */ - observe: function(element, eventName, handler) - { - var e = { _element: element, _eventName: eventName, _handler: handler }; - this.observers.push(e); - return Event.observe(e._element,e._eventName,e._handler); - }, - - /** - * Checks whether an event observer is installed and returns its index - * @param element DOM element reference or id the event handler was attached to - * @param string event name observed - * @param handler event handler function - * @result int false if the event handler is not installed, or 1-based index when installed - */ - findObserver: function(element, eventName, handler) - { - var e = { _element: element, _eventName: eventName, _handler: handler }; - var idx = -1; - for(var i=0;i0) - window.clearInterval(this.intervals.pop()); - - // automatically deregister all installed observers - while (this.observers.length>0) - { - var e = this.observers.pop(); - Event.stopObserving(e._element,e._eventName,e._handler); - } - } - else - debugger; // shouldn't happen - - this.deregister(); - - this.registered = false; - } - -}); - -Prado.WebUI.PostBackControl = Class.create(Prado.WebUI.Control, { - - onInit : function(options) - { - this._elementOnClick = null; - - if (!this.element) - debugger; // element not found - else - { - //capture the element's onclick function - if(typeof(this.element.onclick)=="function") - { - this._elementOnClick = this.element.onclick.bind(this.element); - this.element.onclick = null; - } - this.observe(this.element, "click", this.elementClicked.bindEvent(this,options)); - } - }, - - elementClicked : function(event, options) - { - var src = Event.element(event); - var doPostBack = true; - var onclicked = null; - - if(this._elementOnClick) - { - var onclicked = this._elementOnClick(event); - if(typeof(onclicked) == "boolean") - doPostBack = onclicked; - } - if(doPostBack && !Prado.Element.isDisabled(src)) - this.onPostBack(event,options); - if(typeof(onclicked) == "boolean" && !onclicked) - Event.stop(event); - }, - - onPostBack : function(event, options) - { - Prado.PostBack(event,options); - } - -}); - -Prado.WebUI.TButton = Class.create(Prado.WebUI.PostBackControl); -Prado.WebUI.TLinkButton = Class.create(Prado.WebUI.PostBackControl); -Prado.WebUI.TCheckBox = Class.create(Prado.WebUI.PostBackControl); -Prado.WebUI.TBulletedList = Class.create(Prado.WebUI.PostBackControl); -Prado.WebUI.TImageMap = Class.create(Prado.WebUI.PostBackControl); - -/** - * TImageButton client-side behaviour. With validation, Firefox needs - * to capture the x,y point of the clicked image in hidden form fields. - */ -Prado.WebUI.TImageButton = Class.create(Prado.WebUI.PostBackControl, -{ - /** - * Override parent onPostBack function, tried to add hidden forms - * inputs to capture x,y clicked point. - */ - onPostBack : function(event, options) - { - this.addXYInput(event,options); - Prado.PostBack(event, options); - this.removeXYInput(event,options); - }, - - /** - * Add hidden inputs to capture the x,y point clicked on the image. - * @param event DOM click event. - * @param array image button options. - */ - addXYInput : function(event,options) - { - var imagePos = this.element.cumulativeOffset(); - var clickedPos = [event.clientX, event.clientY]; - var x = clickedPos[0]-imagePos[0]+1; - var y = clickedPos[1]-imagePos[1]+1; - x = x < 0 ? 0 : x; - y = y < 0 ? 0 : y; - var id = options['EventTarget']; - var x_input = $(id+"_x"); - var y_input = $(id+"_y"); - if(x_input) - { - x_input.value = x; - } - else - { - x_input = INPUT({type:'hidden',name:id+'_x','id':id+'_x',value:x}); - this.element.parentNode.appendChild(x_input); - } - if(y_input) - { - y_input.value = y; - } - else - { - y_input = INPUT({type:'hidden',name:id+'_y','id':id+'_y',value:y}); - this.element.parentNode.appendChild(y_input); - } - }, - - /** - * Remove hidden inputs for x,y-click capturing - * @param event DOM click event. - * @param array image button options. - */ - removeXYInput : function(event,options) - { - var id = options['EventTarget']; - this.element.parentNode.removeChild($(id+"_x")); - this.element.parentNode.removeChild($(id+"_y")); - } -}); - - -/** - * Radio button, only initialize if not already checked. - */ -Prado.WebUI.TRadioButton = Class.create(Prado.WebUI.PostBackControl, -{ - initialize : function($super, options) - { - this.element = $(options['ID']); - if(this.element) - { - if(!this.element.checked) - $super(options); - } - } -}); - - -Prado.WebUI.TTextBox = Class.create(Prado.WebUI.PostBackControl, -{ - onInit : function(options) - { - this.options=options; - if(this.options['TextMode'] != 'MultiLine') - this.observe(this.element, "keydown", this.handleReturnKey.bind(this)); - if(this.options['AutoPostBack']==true) - this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); - }, - - handleReturnKey : function(e) - { - if(Event.keyCode(e) == Event.KEY_RETURN) - { - var target = Event.element(e); - if(target) - { - if(this.options['AutoPostBack']==true) - { - Event.fireEvent(target, "change"); - Event.stop(e); - } - else - { - if(this.options['CausesValidation'] && typeof(Prado.Validation) != "undefined") - { - if(!Prado.Validation.validate(this.options['FormID'], this.options['ValidationGroup'], $(this.options['ID']))) - return Event.stop(e); - } - } - } - } - } -}); - -Prado.WebUI.TListControl = Class.create(Prado.WebUI.PostBackControl, -{ - onInit : function(options) - { - this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); - } -}); - -Prado.WebUI.TListBox = Class.create(Prado.WebUI.TListControl); -Prado.WebUI.TDropDownList = Class.create(Prado.WebUI.TListControl); - -Prado.WebUI.DefaultButton = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - this.options = options; - this.observe(options['Panel'], 'keydown', this.triggerEvent.bindEvent(this)); - }, - - triggerEvent : function(ev, target) - { - var enterPressed = Event.keyCode(ev) == Event.KEY_RETURN; - var isTextArea = Event.element(ev).tagName.toLowerCase() == "textarea"; - var isValidButton = Event.element(ev).tagName.toLowerCase() == "input" && Event.element(ev).type.toLowerCase() == "submit"; - - if(enterPressed && !isTextArea && !isValidButton) - { - var defaultButton = $(this.options['Target']); - if(defaultButton) - { - this.triggered = true; - Event.fireEvent(defaultButton, this.options['Event']); - Event.stop(ev); - } - } - } -}); - -Prado.WebUI.TTextHighlighter = Class.create(); -Prado.WebUI.TTextHighlighter.prototype = -{ - initialize:function(id) - { - if(!window.clipboardData) return; - var options = - { - href : 'javascript:;/'+'/copy code to clipboard', - onclick : 'Prado.WebUI.TTextHighlighter.copy(this)', - onmouseover : 'Prado.WebUI.TTextHighlighter.hover(this)', - onmouseout : 'Prado.WebUI.TTextHighlighter.out(this)' - } - var div = DIV({className:'copycode'}, A(options, 'Copy Code')); - document.write(DIV(null,div).innerHTML); - } -}; - -Object.extend(Prado.WebUI.TTextHighlighter, -{ - copy : function(obj) - { - var parent = obj.parentNode.parentNode.parentNode; - var text = ''; - for(var i = 0; i < parent.childNodes.length; i++) - { - var node = parent.childNodes[i]; - if(node.innerText) - text += node.innerText == 'Copy Code' ? '' : node.innerText; - else - text += node.nodeValue; - } - if(text.length > 0) - window.clipboardData.setData("Text", text); - }, - - hover : function(obj) - { - obj.parentNode.className = "copycode copycode_hover"; - }, - - out : function(obj) - { - obj.parentNode.className = "copycode"; - } -}); - - -Prado.WebUI.TCheckBoxList = Base.extend( -{ - constructor : function(options) - { - Prado.Registry.set(options.ListID, this); - for(var i = 0; i0) + window.clearInterval(this.intervals.pop()); + + // automatically deregister all installed observers + while (this.observers.length>0) + { + var e = this.observers.pop(); + Event.stopObserving(e._element,e._eventName,e._handler); + } + } + else + debugger; // shouldn't happen + + this.deregister(); + + this.registered = false; + } + +}); + +Prado.WebUI.PostBackControl = Class.create(Prado.WebUI.Control, { + + onInit : function(options) + { + this._elementOnClick = null; + + if (!this.element) + debugger; // element not found + else + { + //capture the element's onclick function + if(typeof(this.element.onclick)=="function") + { + this._elementOnClick = this.element.onclick.bind(this.element); + this.element.onclick = null; + } + this.observe(this.element, "click", this.elementClicked.bindEvent(this,options)); + } + }, + + elementClicked : function(event, options) + { + var src = Event.element(event); + var doPostBack = true; + var onclicked = null; + + if(this._elementOnClick) + { + var onclicked = this._elementOnClick(event); + if(typeof(onclicked) == "boolean") + doPostBack = onclicked; + } + if(doPostBack && !Prado.Element.isDisabled(src)) + this.onPostBack(event,options); + if(typeof(onclicked) == "boolean" && !onclicked) + Event.stop(event); + }, + + onPostBack : function(event, options) + { + Prado.PostBack(event,options); + } + +}); + +Prado.WebUI.TButton = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TLinkButton = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TCheckBox = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TBulletedList = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TImageMap = Class.create(Prado.WebUI.PostBackControl); + +/** + * TImageButton client-side behaviour. With validation, Firefox needs + * to capture the x,y point of the clicked image in hidden form fields. + */ +Prado.WebUI.TImageButton = Class.create(Prado.WebUI.PostBackControl, +{ + /** + * Override parent onPostBack function, tried to add hidden forms + * inputs to capture x,y clicked point. + */ + onPostBack : function(event, options) + { + this.addXYInput(event,options); + Prado.PostBack(event, options); + this.removeXYInput(event,options); + }, + + /** + * Add hidden inputs to capture the x,y point clicked on the image. + * @param event DOM click event. + * @param array image button options. + */ + addXYInput : function(event,options) + { + var imagePos = this.element.cumulativeOffset(); + var clickedPos = [event.clientX, event.clientY]; + var x = clickedPos[0]-imagePos[0]+1; + var y = clickedPos[1]-imagePos[1]+1; + x = x < 0 ? 0 : x; + y = y < 0 ? 0 : y; + var id = options['EventTarget']; + var x_input = $(id+"_x"); + var y_input = $(id+"_y"); + if(x_input) + { + x_input.value = x; + } + else + { + x_input = INPUT({type:'hidden',name:id+'_x','id':id+'_x',value:x}); + this.element.parentNode.appendChild(x_input); + } + if(y_input) + { + y_input.value = y; + } + else + { + y_input = INPUT({type:'hidden',name:id+'_y','id':id+'_y',value:y}); + this.element.parentNode.appendChild(y_input); + } + }, + + /** + * Remove hidden inputs for x,y-click capturing + * @param event DOM click event. + * @param array image button options. + */ + removeXYInput : function(event,options) + { + var id = options['EventTarget']; + this.element.parentNode.removeChild($(id+"_x")); + this.element.parentNode.removeChild($(id+"_y")); + } +}); + + +/** + * Radio button, only initialize if not already checked. + */ +Prado.WebUI.TRadioButton = Class.create(Prado.WebUI.PostBackControl, +{ + initialize : function($super, options) + { + this.element = $(options['ID']); + if(this.element) + { + if(!this.element.checked) + $super(options); + } + } +}); + + +Prado.WebUI.TTextBox = Class.create(Prado.WebUI.PostBackControl, +{ + onInit : function(options) + { + this.options=options; + if(this.options['TextMode'] != 'MultiLine') + this.observe(this.element, "keydown", this.handleReturnKey.bind(this)); + if(this.options['AutoPostBack']==true) + this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); + }, + + handleReturnKey : function(e) + { + if(Event.keyCode(e) == Event.KEY_RETURN) + { + var target = Event.element(e); + if(target) + { + if(this.options['AutoPostBack']==true) + { + Event.fireEvent(target, "change"); + Event.stop(e); + } + else + { + if(this.options['CausesValidation'] && typeof(Prado.Validation) != "undefined") + { + if(!Prado.Validation.validate(this.options['FormID'], this.options['ValidationGroup'], $(this.options['ID']))) + return Event.stop(e); + } + } + } + } + } +}); + +Prado.WebUI.TListControl = Class.create(Prado.WebUI.PostBackControl, +{ + onInit : function(options) + { + this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); + } +}); + +Prado.WebUI.TListBox = Class.create(Prado.WebUI.TListControl); +Prado.WebUI.TDropDownList = Class.create(Prado.WebUI.TListControl); + +Prado.WebUI.DefaultButton = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = options; + this.observe(options['Panel'], 'keydown', this.triggerEvent.bindEvent(this)); + }, + + triggerEvent : function(ev, target) + { + var enterPressed = Event.keyCode(ev) == Event.KEY_RETURN; + var isTextArea = Event.element(ev).tagName.toLowerCase() == "textarea"; + var isValidButton = Event.element(ev).tagName.toLowerCase() == "input" && Event.element(ev).type.toLowerCase() == "submit"; + + if(enterPressed && !isTextArea && !isValidButton) + { + var defaultButton = $(this.options['Target']); + if(defaultButton) + { + this.triggered = true; + Event.fireEvent(defaultButton, this.options['Event']); + Event.stop(ev); + } + } + } +}); + +Prado.WebUI.TTextHighlighter = Class.create(); +Prado.WebUI.TTextHighlighter.prototype = +{ + initialize:function(id) + { + if(!window.clipboardData) return; + var options = + { + href : 'javascript:;/'+'/copy code to clipboard', + onclick : 'Prado.WebUI.TTextHighlighter.copy(this)', + onmouseover : 'Prado.WebUI.TTextHighlighter.hover(this)', + onmouseout : 'Prado.WebUI.TTextHighlighter.out(this)' + } + var div = DIV({className:'copycode'}, A(options, 'Copy Code')); + document.write(DIV(null,div).innerHTML); + } +}; + +Object.extend(Prado.WebUI.TTextHighlighter, +{ + copy : function(obj) + { + var parent = obj.parentNode.parentNode.parentNode; + var text = ''; + for(var i = 0; i < parent.childNodes.length; i++) + { + var node = parent.childNodes[i]; + if(node.innerText) + text += node.innerText == 'Copy Code' ? '' : node.innerText; + else + text += node.nodeValue; + } + if(text.length > 0) + window.clipboardData.setData("Text", text); + }, + + hover : function(obj) + { + obj.parentNode.className = "copycode copycode_hover"; + }, + + out : function(obj) + { + obj.parentNode.className = "copycode"; + } +}); + + +Prado.WebUI.TCheckBoxList = Base.extend( +{ + constructor : function(options) + { + Prado.Registry.set(options.ListID, this); + for(var i = 0; i - * -*/ - - -Prado.WebUI.THtmlArea = Class.create(Prado.WebUI.Control, -{ - initialize: function($super, options) - { - options.ID = options.EditorOptions.elements; - $super(options); - }, - - onInit : function(options) - { - this.options = options; - - var obj = this; - this.ajaxresponder = { - onComplete : function(request) - { - if(request && (request instanceof Prado.AjaxRequest)) - obj.checkInstance(); - } - }; - this.registerAjaxHook(); - - this.registerInstance(); - }, - - registerInstance: function() - { - if (typeof tinyMCE_GZ == 'undefined') - { - if (typeof tinyMCE == 'undefined') - { - if (typeof Prado.CallbackRequest != 'undefined') - if (typeof Prado.CallbackRequest.transport != 'undefined') - { - // we're in a callback - // try it again in some time, as tinyMCE is most likely still loading - this.setTimeout(this.registerInstance.bind(this), 50); - return; - } - throw "TinyMCE libraries must be loaded first"; - } - Prado.WebUI.THtmlArea.tinyMCELoadState = 255; - this.initInstance(); - } - else - if (Prado.WebUI.THtmlArea.tinyMCELoadState==255) - this.initInstance(); - else - { - Prado.WebUI.THtmlArea.pendingRegistrations.push(this.options.ID); - if (Prado.WebUI.THtmlArea.tinyMCELoadState==0) - { - Prado.WebUI.THtmlArea.tinyMCELoadState = 1; - tinyMCE_GZ.init( - this.options.CompressionOptions, - this.compressedScriptsLoaded.bind(this) - ); - } - } - }, - - compressedScriptsLoaded: function() - { - Prado.WebUI.THtmlArea.tinyMCELoadState = 255; - tinymce.dom.Event._pageInit(); - var wrapper; - while(Prado.WebUI.THtmlArea.pendingRegistrations.length>0) - if (wrapper = Prado.Registry.get(Prado.WebUI.THtmlArea.pendingRegistrations.pop())) - wrapper.initInstance(); - }, - - initInstance: function() - { - tinyMCE.init(this.options.EditorOptions); - }, - - checkInstance: function() - { - if (!document.getElementById(this.ID)) - this.deinitialize(); - }, - - removePreviousInstance: function() - { - for(var i=0;i has been removed from DOM tree without deinitialzing the tinyMCE editor first) - } - - // doublecheck editor instance here and remove manually from tinyMCE-registry if neccessary - this.removePreviousInstance(); - - this.deRegisterAjaxHook(); - } -}); - -Object.extend(Prado.WebUI.THtmlArea, -{ - pendingRegistrations : [], - tinyMCELoadState : 0 -}); - - + +/* + * + * HtmlArea (tinyMCE) wrapper + * + * @author Gabor Berczi + * +*/ + + +Prado.WebUI.THtmlArea = Class.create(Prado.WebUI.Control, +{ + initialize: function($super, options) + { + options.ID = options.EditorOptions.elements; + $super(options); + }, + + onInit : function(options) + { + this.options = options; + + var obj = this; + this.ajaxresponder = { + onComplete : function(request) + { + if(request && (request instanceof Prado.AjaxRequest)) + obj.checkInstance(); + } + }; + this.registerAjaxHook(); + + this.registerInstance(); + }, + + registerInstance: function() + { + if (typeof tinyMCE_GZ == 'undefined') + { + if (typeof tinyMCE == 'undefined') + { + if (typeof Prado.CallbackRequest != 'undefined') + if (typeof Prado.CallbackRequest.transport != 'undefined') + { + // we're in a callback + // try it again in some time, as tinyMCE is most likely still loading + this.setTimeout(this.registerInstance.bind(this), 50); + return; + } + throw "TinyMCE libraries must be loaded first"; + } + Prado.WebUI.THtmlArea.tinyMCELoadState = 255; + this.initInstance(); + } + else + if (Prado.WebUI.THtmlArea.tinyMCELoadState==255) + this.initInstance(); + else + { + Prado.WebUI.THtmlArea.pendingRegistrations.push(this.options.ID); + if (Prado.WebUI.THtmlArea.tinyMCELoadState==0) + { + Prado.WebUI.THtmlArea.tinyMCELoadState = 1; + tinyMCE_GZ.init( + this.options.CompressionOptions, + this.compressedScriptsLoaded.bind(this) + ); + } + } + }, + + compressedScriptsLoaded: function() + { + Prado.WebUI.THtmlArea.tinyMCELoadState = 255; + tinymce.dom.Event._pageInit(); + var wrapper; + while(Prado.WebUI.THtmlArea.pendingRegistrations.length>0) + if (wrapper = Prado.Registry.get(Prado.WebUI.THtmlArea.pendingRegistrations.pop())) + wrapper.initInstance(); + }, + + initInstance: function() + { + tinyMCE.init(this.options.EditorOptions); + }, + + checkInstance: function() + { + if (!document.getElementById(this.ID)) + this.deinitialize(); + }, + + removePreviousInstance: function() + { + for(var i=0;i has been removed from DOM tree without deinitialzing the tinyMCE editor first) + } + + // doublecheck editor instance here and remove manually from tinyMCE-registry if neccessary + this.removePreviousInstance(); + + this.deRegisterAjaxHook(); + } +}); + +Object.extend(Prado.WebUI.THtmlArea, +{ + pendingRegistrations : [], + tinyMCELoadState : 0 +}); + + diff --git a/framework/Web/Javascripts/source/prado/controls/keyboard.js b/framework/Web/Javascripts/source/prado/controls/keyboard.js index 5b8a6b15..25541074 100644 --- a/framework/Web/Javascripts/source/prado/controls/keyboard.js +++ b/framework/Web/Javascripts/source/prado/controls/keyboard.js @@ -1,161 +1,161 @@ -Prado.WebUI.TKeyboard = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - this.cssClass = options['CssClass']; - this.forControl = document.getElementById(options['ForControl']); - this.autoHide = options['AutoHide']; - - this.flagShift = false; - this.flagCaps = false; - this.flagHover = false; - this.flagFocus = false; - - this.keys = new Array - ( - new Array('` ~ D', '1 ! D', '2 @ D', '3 # D', '4 $ D', '5 % D', '6 ^ D', '7 & D', '8 * D', '9 ( D', '0 ) D', '- _ D', '= + D', 'Bksp Bksp Bksp'), - new Array('Del Del Del', 'q Q L', 'w W L', 'e E L', 'r R L', 't T L', 'y Y L', 'u U L', 'i I L', 'o O L', 'p P L', '[ { D', '] } D', '\\ | \\'), - new Array('Caps Caps Caps', 'a A L', 's S L', 'd D L', 'f F L', 'g G L', 'h H L', 'j J L', 'k K L', 'l L L', '; : D', '\' " D', 'Exit Exit Exit'), - new Array('Shift Shift Shift', 'z Z L', 'x X L', 'c C L', 'v V L', 'b B L', 'n N L', 'm M L', ', < D', '. > D', '/ ? D', 'Shift Shift Shift') - ); - - if (this.isObject(this.forControl)) - { - this.forControl.keyboard = this; - this.forControl.onfocus = function() {this.keyboard.show(); }; - this.forControl.onblur = function() {if (this.keyboard.flagHover == false) this.keyboard.hide();}; - this.forControl.onkeydown = function(e) {if (!e) e = window.event; var key = (e.keyCode)?e.keyCode:e.which; if(key == 9) this.keyboard.hide();;}; - this.forControl.onselect = this.saveSelection; - this.forControl.onclick = this.saveSelection; - this.forControl.onkeyup = this.saveSelection; - } - - this.render(); - - this.tagKeyboard.onmouseover = function() {this.keyboard.flagHover = true;}; - this.tagKeyboard.onmouseout = function() {this.keyboard.flagHover = false;}; - - if (!this.autoHide) this.show(); - }, - - isObject : function(a) - { - return (typeof a == 'object' && !!a) || typeof a == 'function'; - }, - - createElement : function(tagName, attributes, parent) - { - var tagElement = document.createElement(tagName); - if (this.isObject(attributes)) for (attribute in attributes) tagElement[attribute] = attributes[attribute]; - if (this.isObject(parent)) parent.appendChild(tagElement); - return tagElement; - }, - - onmouseover : function() - { - this.className += ' Hover'; - }, - - onmouseout : function() - { - this.className = this.className.replace(/( Hover| Active)/ig, ''); - }, - - onmousedown : function() - { - this.className += ' Active'; - }, - - onmouseup : function() - { - this.className = this.className.replace(/( Active)/ig, ''); - this.keyboard.type(this.innerHTML); - }, - - render : function() - { - this.tagKeyboard = this.createElement('div', {className: this.cssClass, onselectstart: function() {return false;}}, this.element); - this.tagKeyboard.keyboard = this; - - for (var line = 0; line < this.keys.length; line++) - { - var tagLine = this.createElement('div', {className: 'Line'}, this.tagKeyboard); - for (var key = 0; key < this.keys[line].length; key++) - { - var split = this.keys[line][key].split(' '); - var tagKey = this.createElement('div', {className: 'Key ' + split[2]}, tagLine); - var tagKey1 = this.createElement('div', {className: 'Key1', innerHTML: split[0], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); - var tagKey2 = this.createElement('div', {className: 'Key2', innerHTML: split[1], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); - } - } - }, - - isShown : function() - { - return (this.tagKeyboard.style.visibility.toLowerCase() == 'visible'); - }, - - show : function() - { - if (this.isShown() == false) this.tagKeyboard.style.visibility = 'visible'; - }, - - hide : function() - { - if (this.isShown() == true && this.autoHide) {this.tagKeyboard.style.visibility = 'hidden'; } - }, - - type : function(key) - { - var input = this.forControl; - var command = key.toLowerCase(); - - if (command == 'exit') {this.hide();} - else if (input != 'undefined' && input != null && command == 'bksp') {this.insert(input, 'bksp');} - else if (input != 'undefined' && input != null && command == 'del') {this.insert(input, 'del');} - else if (command == 'shift') {this.tagKeyboard.className = this.flagShift?'Keyboard Off':'Keyboard Shift';this.flagShift = this.flagShift?false:true;} - else if (command == 'caps') {this.tagKeyboard.className = this.caps?'Keyboard Off':'Keyboard Caps';this.caps = this.caps?false:true;} - else if (input != 'undefined' && input != null) - { - if (this.flagShift == true) {this.flagShift = false; this.tagKeyboard.className = 'Keyboard Off';} - key = key.replace(/>/, '>'); key = key.replace(/</, '<'); key = key.replace(/&/, '&'); - this.insert(input, key); - } - - if (command != 'exit') input.focus(); - }, - - saveSelection : function() - { - if (this.keyboard.forControl.createTextRange) - { - this.keyboard.selection = document.selection.createRange().duplicate(); - return; - } - }, - - insert : function(field, value) - { - if (this.forControl.createTextRange && this.selection) - { - if (value == 'bksp') {this.selection.moveStart("character", -1); this.selection.text = '';} - else if (value == 'del') {this.selection.moveEnd("character", 1); this.selection.text = '';} - else {this.selection.text = value;} - this.selection.select(); - } - else - { - var selectStart = this.forControl.selectionStart; - var selectEnd = this.forControl.selectionEnd; - var start = (this.forControl.value).substring(0, selectStart); - var end = (this.forControl.value).substring(selectEnd, this.forControl.textLength); - - if (value == 'bksp') {start = start.substring(0, start.length - 1); selectStart -= 1; value = '';} - if (value == 'del') {end = end.substring(1, end.length); value = '';} - - this.forControl.value = start + value + end; - this.forControl.selectionStart = selectEnd + value.length; - this.forControl.selectionEnd = selectStart + value.length; - } - } -}); +Prado.WebUI.TKeyboard = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.cssClass = options['CssClass']; + this.forControl = document.getElementById(options['ForControl']); + this.autoHide = options['AutoHide']; + + this.flagShift = false; + this.flagCaps = false; + this.flagHover = false; + this.flagFocus = false; + + this.keys = new Array + ( + new Array('` ~ D', '1 ! D', '2 @ D', '3 # D', '4 $ D', '5 % D', '6 ^ D', '7 & D', '8 * D', '9 ( D', '0 ) D', '- _ D', '= + D', 'Bksp Bksp Bksp'), + new Array('Del Del Del', 'q Q L', 'w W L', 'e E L', 'r R L', 't T L', 'y Y L', 'u U L', 'i I L', 'o O L', 'p P L', '[ { D', '] } D', '\\ | \\'), + new Array('Caps Caps Caps', 'a A L', 's S L', 'd D L', 'f F L', 'g G L', 'h H L', 'j J L', 'k K L', 'l L L', '; : D', '\' " D', 'Exit Exit Exit'), + new Array('Shift Shift Shift', 'z Z L', 'x X L', 'c C L', 'v V L', 'b B L', 'n N L', 'm M L', ', < D', '. > D', '/ ? D', 'Shift Shift Shift') + ); + + if (this.isObject(this.forControl)) + { + this.forControl.keyboard = this; + this.forControl.onfocus = function() {this.keyboard.show(); }; + this.forControl.onblur = function() {if (this.keyboard.flagHover == false) this.keyboard.hide();}; + this.forControl.onkeydown = function(e) {if (!e) e = window.event; var key = (e.keyCode)?e.keyCode:e.which; if(key == 9) this.keyboard.hide();;}; + this.forControl.onselect = this.saveSelection; + this.forControl.onclick = this.saveSelection; + this.forControl.onkeyup = this.saveSelection; + } + + this.render(); + + this.tagKeyboard.onmouseover = function() {this.keyboard.flagHover = true;}; + this.tagKeyboard.onmouseout = function() {this.keyboard.flagHover = false;}; + + if (!this.autoHide) this.show(); + }, + + isObject : function(a) + { + return (typeof a == 'object' && !!a) || typeof a == 'function'; + }, + + createElement : function(tagName, attributes, parent) + { + var tagElement = document.createElement(tagName); + if (this.isObject(attributes)) for (attribute in attributes) tagElement[attribute] = attributes[attribute]; + if (this.isObject(parent)) parent.appendChild(tagElement); + return tagElement; + }, + + onmouseover : function() + { + this.className += ' Hover'; + }, + + onmouseout : function() + { + this.className = this.className.replace(/( Hover| Active)/ig, ''); + }, + + onmousedown : function() + { + this.className += ' Active'; + }, + + onmouseup : function() + { + this.className = this.className.replace(/( Active)/ig, ''); + this.keyboard.type(this.innerHTML); + }, + + render : function() + { + this.tagKeyboard = this.createElement('div', {className: this.cssClass, onselectstart: function() {return false;}}, this.element); + this.tagKeyboard.keyboard = this; + + for (var line = 0; line < this.keys.length; line++) + { + var tagLine = this.createElement('div', {className: 'Line'}, this.tagKeyboard); + for (var key = 0; key < this.keys[line].length; key++) + { + var split = this.keys[line][key].split(' '); + var tagKey = this.createElement('div', {className: 'Key ' + split[2]}, tagLine); + var tagKey1 = this.createElement('div', {className: 'Key1', innerHTML: split[0], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); + var tagKey2 = this.createElement('div', {className: 'Key2', innerHTML: split[1], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); + } + } + }, + + isShown : function() + { + return (this.tagKeyboard.style.visibility.toLowerCase() == 'visible'); + }, + + show : function() + { + if (this.isShown() == false) this.tagKeyboard.style.visibility = 'visible'; + }, + + hide : function() + { + if (this.isShown() == true && this.autoHide) {this.tagKeyboard.style.visibility = 'hidden'; } + }, + + type : function(key) + { + var input = this.forControl; + var command = key.toLowerCase(); + + if (command == 'exit') {this.hide();} + else if (input != 'undefined' && input != null && command == 'bksp') {this.insert(input, 'bksp');} + else if (input != 'undefined' && input != null && command == 'del') {this.insert(input, 'del');} + else if (command == 'shift') {this.tagKeyboard.className = this.flagShift?'Keyboard Off':'Keyboard Shift';this.flagShift = this.flagShift?false:true;} + else if (command == 'caps') {this.tagKeyboard.className = this.caps?'Keyboard Off':'Keyboard Caps';this.caps = this.caps?false:true;} + else if (input != 'undefined' && input != null) + { + if (this.flagShift == true) {this.flagShift = false; this.tagKeyboard.className = 'Keyboard Off';} + key = key.replace(/>/, '>'); key = key.replace(/</, '<'); key = key.replace(/&/, '&'); + this.insert(input, key); + } + + if (command != 'exit') input.focus(); + }, + + saveSelection : function() + { + if (this.keyboard.forControl.createTextRange) + { + this.keyboard.selection = document.selection.createRange().duplicate(); + return; + } + }, + + insert : function(field, value) + { + if (this.forControl.createTextRange && this.selection) + { + if (value == 'bksp') {this.selection.moveStart("character", -1); this.selection.text = '';} + else if (value == 'del') {this.selection.moveEnd("character", 1); this.selection.text = '';} + else {this.selection.text = value;} + this.selection.select(); + } + else + { + var selectStart = this.forControl.selectionStart; + var selectEnd = this.forControl.selectionEnd; + var start = (this.forControl.value).substring(0, selectStart); + var end = (this.forControl.value).substring(selectEnd, this.forControl.textLength); + + if (value == 'bksp') {start = start.substring(0, start.length - 1); selectStart -= 1; value = '';} + if (value == 'del') {end = end.substring(1, end.length); value = '';} + + this.forControl.value = start + value + end; + this.forControl.selectionStart = selectEnd + value.length; + this.forControl.selectionEnd = selectStart + value.length; + } + } +}); diff --git a/framework/Web/Javascripts/source/prado/controls/tabpanel.js b/framework/Web/Javascripts/source/prado/controls/tabpanel.js index 157664e3..bd0a7494 100644 --- a/framework/Web/Javascripts/source/prado/controls/tabpanel.js +++ b/framework/Web/Javascripts/source/prado/controls/tabpanel.js @@ -1,60 +1,60 @@ -Prado.WebUI.TTabPanel = Class.create(Prado.WebUI.Control, -{ - onInit : function(options) - { - this.views = options.Views; - this.viewsvis = options.ViewsVis; - this.hiddenField = $(options.ID+'_1'); - this.activeCssClass = options.ActiveCssClass; - this.normalCssClass = options.NormalCssClass; - var length = options.Views.length; - for(var i = 0; i 40) return true; - - var current = this.selectedDate; - var d = current.valueOf(); - if(kc == Event.KEY_LEFT) - { - if(ev.ctrlKey || ev.shiftKey) // -1 month - { - current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() - 1,current.getFullYear())) ); // no need to catch dec -> jan for the year - d = current.setMonth( current.getMonth() - 1 ); - } - else - d -= 86400000; //-1 day - } - else if (kc == Event.KEY_RIGHT) - { - if(ev.ctrlKey || ev.shiftKey) // +1 month - { - current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() + 1,current.getFullYear())) ); // no need to catch dec -> jan for the year - d = current.setMonth( current.getMonth() + 1 ); - } - else - d += 86400000; //+1 day - } - else if (kc == Event.KEY_UP) - { - if(ev.ctrlKey || ev.shiftKey) //-1 year - { - current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() - 1)) ); // no need to catch dec -> jan for the year - d = current.setFullYear( current.getFullYear() - 1 ); - } - else - d -= 604800000; // -7 days - } - else if (kc == Event.KEY_DOWN) - { - if(ev.ctrlKey || ev.shiftKey) // +1 year - { - current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year - d = current.setFullYear( current.getFullYear() + 1 ); - } - else - d += 7 * 24 * 61 * 60 * 1000; // +7 days - } - this.setSelectedDate(d); - Event.stop(ev); - }, - - selectDate : function(ev) - { - var el = Event.element(ev); - while (el.nodeType != 1) - el = el.parentNode; - - while (el != null && el.tagName && el.tagName.toLowerCase() != "td") - el = el.parentNode; - - // if no td found, return - if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td") - return; - - var d = this.newDate(this.selectedDate); - var n = Number(el.firstChild.data); - if (isNaN(n) || n <= 0 || n == null) - return; - - d.setDate(n); - this.setSelectedDate(d); - this.hide(); - }, - - selectToday : function() - { - if(this.selectedDate.toISODate() == this.newDate().toISODate()) - this.hide(); - - this.setSelectedDate(this.newDate()); - }, - - clearSelection : function() - { - this.setSelectedDate(this.newDate()); - this.hide(); - }, - - monthSelect : function(ev) - { - this.setMonth(Form.Element.getValue(Event.element(ev))); - }, - - yearSelect : function(ev) - { - this.setYear(Form.Element.getValue(Event.element(ev))); - }, - - mouseWheelChange : function (event) - { - var delta = 0; - if (!event) event = document.parentWindow.event; - if (event.wheelDelta) { - delta = event.wheelDelta/120; - if (window.opera) delta = -delta; - } else if (event.detail) { delta = -event.detail/3; } - - var d = this.newDate(this.selectedDate); - var m = d.getMonth() + Math.round(delta); - this.setMonth(m,true); - return false; - }, - - // Respond to change event on the textbox or dropdown list - // This method raises OnDateChanged event on client side if it has been defined - onDateChanged : function () - { - if (this.options.OnDateChanged) - { - var date; - if (this.options.InputMode == "TextBox") - { - date=this.control.value; - } - else - { - var day = Prado.WebUI.TDatePicker.getDayListControl(this.control).selectedIndex+1; - var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control).selectedIndex; - var year = Prado.WebUI.TDatePicker.getYearListControl(this.control).value; - date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); - } - this.options.OnDateChanged(this, date); - } - }, - - fireChangeEvent: function(element, capped) - { - if (capped) - { - var obj = this; - - if (typeof(obj.changeeventtimer)!="undefined") - { - clearTimeout(obj.changeeventtimer); - obj.changeeventtimer = null; - } - obj.changeeventtimer = setTimeout( - function() { obj.changeeventtimer = null; Event.fireEvent(element, "change"); }, - 1500 - ); - } - else - Event.fireEvent(element, "change"); - }, - - onChange : function(ref, date, capevents) - { - if(this.options.InputMode == "TextBox") - { - this.control.value = this.formatDate(); - this.fireChangeEvent(this.control, capevents); - } - else - { - var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); - var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); - var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); - var date = this.selectedDate; - if(day) - { - day.selectedIndex = date.getDate()-1; - } - if(month) - { - month.selectedIndex = date.getMonth(); - } - if(year) - { - var years = year.options; - var currentYear = date.getFullYear(); - for(var i = 0; i < years.length; i++) - years[i].selected = years[i].value.toInteger() == currentYear; - } - this.fireChangeEvent(day || month || year, capevents); - } - }, - - formatDate : function() - { - return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : ''; - }, - - newDate : function(date) - { - if(!date) - date = new Date(); - if(typeof(date) == "string" || typeof(date) == "number") - date = new Date(date); - return new Date(Math.min(Math.max(date.getFullYear(),this.FromYear),this.UpToYear), date.getMonth(), date.getDate(), 0,0,0); - }, - - setSelectedDate : function(date, capevents) - { - if (date == null) - return; - var old=this.selectedDate; - this.selectedDate = this.newDate(date); - var dateChanged=(old - this.selectedDate != 0) || ( this.options.InputMode == "TextBox" && this.control.value != this.formatDate()); - - this.updateHeader(); - this.update(); - if (dateChanged && typeof(this.onChange) == "function") - this.onChange(this, date, capevents); - }, - - getElement : function() - { - return this._calDiv; - }, - - getSelectedDate : function () - { - return this.selectedDate == null ? null : this.newDate(this.selectedDate); - }, - - setYear : function(year) - { - var d = this.newDate(this.selectedDate); - d.setFullYear(year); - this.setSelectedDate(d); - }, - - setMonth : function (month, capevents) - { - var d = this.newDate(this.selectedDate); - d.setDate(Math.min(d.getDate(), this.getDaysPerMonth(month,d.getFullYear()))); - d.setMonth(month); - this.setSelectedDate(d,capevents); - }, - - nextMonth : function () - { - this.setMonth(this.selectedDate.getMonth()+1); - }, - - prevMonth : function () - { - this.setMonth(this.selectedDate.getMonth()-1); - }, - - getDaysPerMonth : function (month, year) - { - month = (Number(month)+12) % 12; - var days = [31,28,31,30,31,30,31,31,30,31,30,31]; - var res = days[month]; - if (month == 1 && ((!(year % 4) && (year % 100)) || !(year % 400))) //feburary, leap years has 29 - res++; - return res; - }, - - getDatePickerOffsetHeight : function() - { - if(this.options.InputMode == "TextBox") - return this.control.offsetHeight; - - var control = Prado.WebUI.TDatePicker.getDayListControl(this.control); - if(control) return control.offsetHeight; - - var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control); - if(control) return control.offsetHeight; - - var control = Prado.WebUI.TDatePicker.getYearListControl(this.control); - if(control) return control.offsetHeight; - return 0; - }, - - show : function() - { - this.create(); - - if(!this.showing) - { - var pos = this.control.positionedOffset(); - - pos[1] += this.getDatePickerOffsetHeight(); - this._calDiv.style.top = (pos[1]-1) + "px"; - this._calDiv.style.display = "block"; - this._calDiv.style.left = pos[0] + "px"; - - this.documentClickEvent = this.hideOnClick.bindEvent(this); - this.documentKeyDownEvent = this.keyPressed.bindEvent(this); - Event.observe(document.body, "click", this.documentClickEvent); - var date = this.getDateFromInput(); - if(date) - { - this.selectedDate = date; - this.setSelectedDate(date); - } - Event.observe(document,"keydown", this.documentKeyDownEvent); - this.showing = true; - - if(this.positionMode=='Top') - { - this._calDiv.style.top = ((pos[1]-1) - this.getDatePickerOffsetHeight() - this._calDiv.offsetHeight) + 'px'; - if(Prado.Browser().ie) - this.iePopup = this._calDiv.style.top; - } - this.ieHack(false); - } - }, - - getDateFromInput : function() - { - if(this.options.InputMode == "TextBox") - return Date.SimpleParse($F(this.control), this.Format); - else - return Prado.WebUI.TDatePicker.getDropDownDate(this.control); - }, - - //hide the calendar when clicked outside any calendar - hideOnClick : function(ev) - { - if(!this.showing) return; - var el = Event.element(ev); - var within = false; - do - { - within = within || (el.className && Element.hasClassName(el, "TDatePicker_"+this.CalendarStyle)); - within = within || el == this.trigger; - within = within || el == this.control; - if(within) break; - el = el.parentNode; - } - while(el); - if(!within) this.hide(); - }, - - - hide : function() - { - if(this.showing) - { - this._calDiv.style.display = "none"; - if(this.iePopUp) - this.iePopUp.style.display = "none"; - this.showing = false; - Event.stopObserving(document.body, "click", this.documentClickEvent); - Event.stopObserving(document,"keydown", this.documentKeyDownEvent); - } - }, - - update : function() - { - // Calculate the number of days in the month for the selected date - var date = this.selectedDate; - var today = (this.newDate()).toISODate(); - - var selected = date.toISODate(); - var d1 = new Date(date.getFullYear(), date.getMonth(), 1); - var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1); - var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000)); - - // Find out the weekDay index for the first of this month - var firstIndex = (d1.getDay() - this.FirstDayOfWeek) % 7 ; - if (firstIndex < 0) - firstIndex += 7; - - var index = 0; - while (index < firstIndex) { - this.dateSlot[index].value = -1; - this.dateSlot[index].data.data = String.fromCharCode(160); - this.dateSlot[index].data.parentNode.className = "empty"; - index++; - } - - for (var i = 1; i <= monthLength; i++, index++) { - var slot = this.dateSlot[index]; - var slotNode = slot.data.parentNode; - slot.value = i; - slot.data.data = i; - slotNode.className = "date"; - //slotNode.style.color = ""; - if (d1.toISODate() == today) { - slotNode.className += " today"; - } - if (d1.toISODate() == selected) { - // slotNode.style.color = "blue"; - slotNode.className += " selected"; - } - d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1); - } - - var lastDateIndex = index; - - while(index < 42) { - this.dateSlot[index].value = -1; - this.dateSlot[index].data.data = String.fromCharCode(160); - this.dateSlot[index].data.parentNode.className = "empty"; - ++index; - } - - }, - - hover : function(ev) - { - if(Event.element(ev).tagName) - { - if(ev.type == "mouseover") - Event.element(ev).addClassName("hover"); - else - Event.element(ev).removeClassName("hover"); - } - }, - - updateHeader : function () { - - var options = this._monthSelect.options; - var m = this.selectedDate.getMonth(); - for(var i=0; i < options.length; ++i) { - options[i].selected = false; - if (options[i].value == m) { - options[i].selected = true; - } - } - - options = this._yearSelect.options; - var year = this.selectedDate.getFullYear(); - for(var i=0; i < options.length; ++i) { - options[i].selected = false; - if (options[i].value == year) { - options[i].selected = true; - } - } - - } -}); - -Object.extend(Prado.WebUI.TDatePicker, -{ - /** - * @return Date the date from drop down list options. - */ - getDropDownDate : function(control) - { - var now=new Date(); - var year=now.getFullYear(); - var month=now.getMonth(); - var day=1; - - var month_list = Prado.WebUI.TDatePicker.getMonthListControl(control); - var day_list = Prado.WebUI.TDatePicker.getDayListControl(control); - var year_list = Prado.WebUI.TDatePicker.getYearListControl(control); - - var day = day_list ? $F(day_list) : 1; - var month = month_list ? $F(month_list) : now.getMonth(); - var year = year_list ? $F(year_list) : now.getFullYear(); - - return new Date(year,month,day, 0, 0, 0); - }, - - getYearListControl : function(control) - { - return $(control.id+"_year"); - }, - - getMonthListControl : function(control) - { - return $(control.id+"_month"); - }, - - getDayListControl : function(control) - { - return $(control.id+"_day"); - } +Prado.WebUI.TDatePicker = Class.create(Prado.WebUI.Control, +{ + MonthNames : [ "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" + ], + AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + + ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + + Format : "yyyy-MM-dd", + + FirstDayOfWeek : 1, // 0 for sunday + + ClassName : "", + + CalendarStyle : "default", + + FromYear : 2005, UpToYear: 2020, + + onInit : function(options) + { + this.options = options || []; + this.control = $(options.ID); + this.dateSlot = new Array(42); + this.weekSlot = new Array(6); + this.minimalDaysInFirstWeek = 4; + this.positionMode = 'Bottom'; + + Prado.Registry.set(options.ID, this); + + //which element to trigger to show the calendar + if(this.options.Trigger) + { + this.trigger = $(this.options.Trigger) ; + var triggerEvent = this.options.TriggerEvent || "click"; + } + else + { + this.trigger = this.control; + var triggerEvent = this.options.TriggerEvent || "focus"; + } + + // Popup position + if(this.options.PositionMode == 'Top') + { + this.positionMode = this.options.PositionMode; + } + + Object.extend(this,options); + // generate default date _after_ extending options + this.selectedDate = this.newDate(); + + Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); + + // Listen to change event if needed + if (typeof(this.options.OnDateChanged) == "function") + { + if(this.options.InputMode == "TextBox") + { + Event.observe(this.control, "change", this.onDateChanged.bindEvent(this)); + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + Event.observe (day, "change", this.onDateChanged.bindEvent(this)); + Event.observe (month, "change", this.onDateChanged.bindEvent(this)); + Event.observe (year, "change", this.onDateChanged.bindEvent(this)); + + } + + + } + + }, + + create : function() + { + if(typeof(this._calDiv) != "undefined") + return; + + var div; + var table; + var tbody; + var tr; + var td; + + // Create the top-level div element + this._calDiv = document.createElement("div"); + this._calDiv.className = "TDatePicker_"+this.CalendarStyle+" "+this.ClassName; + this._calDiv.style.display = "none"; + this._calDiv.style.position = "absolute" + + // header div + div = document.createElement("div"); + div.className = "calendarHeader"; + this._calDiv.appendChild(div); + + table = document.createElement("table"); + table.style.cellSpacing = 0; + div.appendChild(table); + + tbody = document.createElement("tbody"); + table.appendChild(tbody); + + tr = document.createElement("tr"); + tbody.appendChild(tr); + + // Previous Month Button + td = document.createElement("td"); + var previousMonth = document.createElement("input"); + previousMonth.className = "prevMonthButton button"; + previousMonth.type = "button" + previousMonth.value = "<<"; + td.appendChild(previousMonth); + tr.appendChild(td); + + + + // + // Create the month drop down + // + td = document.createElement("td"); + tr.appendChild(td); + this._monthSelect = document.createElement("select"); + this._monthSelect.className = "months"; + for (var i = 0 ; i < this.MonthNames.length ; i++) { + var opt = document.createElement("option"); + opt.innerHTML = this.MonthNames[i]; + opt.value = i; + if (i == this.selectedDate.getMonth()) { + opt.selected = true; + } + this._monthSelect.appendChild(opt); + } + td.appendChild(this._monthSelect); + + + // + // Create the year drop down + // + td = document.createElement("td"); + td.className = "labelContainer"; + tr.appendChild(td); + this._yearSelect = document.createElement("select"); + for(var i=this.FromYear; i <= this.UpToYear; ++i) { + var opt = document.createElement("option"); + opt.innerHTML = i; + opt.value = i; + if (i == this.selectedDate.getFullYear()) { + opt.selected = false; + } + this._yearSelect.appendChild(opt); + } + td.appendChild(this._yearSelect); + + + td = document.createElement("td"); + var nextMonth = document.createElement("input"); + nextMonth.className = "nextMonthButton button"; + nextMonth.type = "button"; + nextMonth.value = ">>"; + td.appendChild(nextMonth); + tr.appendChild(td); + + // Calendar body + div = document.createElement("div"); + div.className = "calendarBody"; + this._calDiv.appendChild(div); + var calendarBody = div; + + // Create the inside of calendar body + + var text; + table = document.createElement("table"); + table.align="center"; + table.className = "grid"; + + div.appendChild(table); + var thead = document.createElement("thead"); + table.appendChild(thead); + tr = document.createElement("tr"); + thead.appendChild(tr); + + for(i=0; i < 7; ++i) { + td = document.createElement("th"); + text = document.createTextNode(this.ShortWeekDayNames[(i+this.FirstDayOfWeek)%7]); + td.appendChild(text); + td.className = "weekDayHead"; + tr.appendChild(td); + } + + // Date grid + tbody = document.createElement("tbody"); + table.appendChild(tbody); + + for(var week=0; week<6; ++week) { + tr = document.createElement("tr"); + tbody.appendChild(tr); + + for(var day=0; day<7; ++day) { + td = document.createElement("td"); + td.className = "calendarDate"; + text = document.createTextNode(String.fromCharCode(160)); + td.appendChild(text); + + tr.appendChild(td); + var tmp = new Object(); + tmp.tag = "DATE"; + tmp.value = -1; + tmp.data = text; + this.dateSlot[(week*7)+day] = tmp; + + Event.observe(td, "mouseover", this.hover.bindEvent(this)); + Event.observe(td, "mouseout", this.hover.bindEvent(this)); + + } + } + + // Calendar Footer + div = document.createElement("div"); + div.className = "calendarFooter"; + this._calDiv.appendChild(div); + + var todayButton = document.createElement("input"); + todayButton.type="button"; + todayButton.className = "todayButton"; + var today = this.newDate(); + var buttonText = today.SimpleFormat(this.Format,this); + todayButton.value = buttonText; + div.appendChild(todayButton); + + if(Prado.Browser().ie) + { + this.iePopUp = document.createElement('iframe'); + this.iePopUp.src = Prado.WebUI.TDatePicker.spacer; + this.iePopUp.style.position = "absolute" + this.iePopUp.scrolling="no" + this.iePopUp.frameBorder="0" + this.control.parentNode.appendChild(this.iePopUp); + } + + this.control.parentNode.appendChild(this._calDiv); + + this.update(); + this.updateHeader(); + + this.ieHack(true); + + // IE55+ extension + previousMonth.hideFocus = true; + nextMonth.hideFocus = true; + todayButton.hideFocus = true; + // end IE55+ extension + + // hook up events + Event.observe(previousMonth, "click", this.prevMonth.bindEvent(this)); + Event.observe(nextMonth, "click", this.nextMonth.bindEvent(this)); + Event.observe(todayButton, "click", this.selectToday.bindEvent(this)); + //Event.observe(clearButton, "click", this.clearSelection.bindEvent(this)); + Event.observe(this._monthSelect, "change", this.monthSelect.bindEvent(this)); + Event.observe(this._yearSelect, "change", this.yearSelect.bindEvent(this)); + + // ie, opera + Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this)); + // ff + Event.observe(this._calDiv, "DOMMouseScroll", this.mouseWheelChange.bindEvent(this)); + + Event.observe(calendarBody, "click", this.selectDate.bindEvent(this)); + + Prado.Element.focus(this.control); + + }, + + ieHack : function(cleanup) + { + // IE hack + if(this.iePopUp) + { + this.iePopUp.style.display = "block"; + this.iePopUp.style.left = (this._calDiv.offsetLeft -1)+ "px"; + this.iePopUp.style.top = (this._calDiv.offsetTop -1 ) + "px"; + this.iePopUp.style.width = Math.abs(this._calDiv.offsetWidth -2)+ "px"; + this.iePopUp.style.height = (this._calDiv.offsetHeight + 1)+ "px"; + if(cleanup) this.iePopUp.style.display = "none"; + } + }, + + keyPressed : function(ev) + { + if(!this.showing) return; + if (!ev) ev = document.parentWindow.event; + var kc = ev.keyCode != null ? ev.keyCode : ev.charCode; + + if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR || kc == Event.KEY_TAB) + { + this.setSelectedDate(this.selectedDate); + Event.stop(ev); + this.hide(); + } + if(kc == Event.KEY_ESC) + { + Event.stop(ev); this.hide(); + } + + var getDaysPerMonth = function (nMonth, nYear) + { + nMonth = (nMonth + 12) % 12; + var days= [31,28,31,30,31,30,31,31,30,31,30,31]; + var res = days[nMonth]; + if (nMonth == 1) //feburary, leap years has 29 + res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0; + return res; + } + + if(kc < 37 || kc > 40) return true; + + var current = this.selectedDate; + var d = current.valueOf(); + if(kc == Event.KEY_LEFT) + { + if(ev.ctrlKey || ev.shiftKey) // -1 month + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() - 1,current.getFullYear())) ); // no need to catch dec -> jan for the year + d = current.setMonth( current.getMonth() - 1 ); + } + else + d -= 86400000; //-1 day + } + else if (kc == Event.KEY_RIGHT) + { + if(ev.ctrlKey || ev.shiftKey) // +1 month + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() + 1,current.getFullYear())) ); // no need to catch dec -> jan for the year + d = current.setMonth( current.getMonth() + 1 ); + } + else + d += 86400000; //+1 day + } + else if (kc == Event.KEY_UP) + { + if(ev.ctrlKey || ev.shiftKey) //-1 year + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() - 1)) ); // no need to catch dec -> jan for the year + d = current.setFullYear( current.getFullYear() - 1 ); + } + else + d -= 604800000; // -7 days + } + else if (kc == Event.KEY_DOWN) + { + if(ev.ctrlKey || ev.shiftKey) // +1 year + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year + d = current.setFullYear( current.getFullYear() + 1 ); + } + else + d += 7 * 24 * 61 * 60 * 1000; // +7 days + } + this.setSelectedDate(d); + Event.stop(ev); + }, + + selectDate : function(ev) + { + var el = Event.element(ev); + while (el.nodeType != 1) + el = el.parentNode; + + while (el != null && el.tagName && el.tagName.toLowerCase() != "td") + el = el.parentNode; + + // if no td found, return + if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td") + return; + + var d = this.newDate(this.selectedDate); + var n = Number(el.firstChild.data); + if (isNaN(n) || n <= 0 || n == null) + return; + + d.setDate(n); + this.setSelectedDate(d); + this.hide(); + }, + + selectToday : function() + { + if(this.selectedDate.toISODate() == this.newDate().toISODate()) + this.hide(); + + this.setSelectedDate(this.newDate()); + }, + + clearSelection : function() + { + this.setSelectedDate(this.newDate()); + this.hide(); + }, + + monthSelect : function(ev) + { + this.setMonth(Form.Element.getValue(Event.element(ev))); + }, + + yearSelect : function(ev) + { + this.setYear(Form.Element.getValue(Event.element(ev))); + }, + + mouseWheelChange : function (event) + { + var delta = 0; + if (!event) event = document.parentWindow.event; + if (event.wheelDelta) { + delta = event.wheelDelta/120; + if (window.opera) delta = -delta; + } else if (event.detail) { delta = -event.detail/3; } + + var d = this.newDate(this.selectedDate); + var m = d.getMonth() + Math.round(delta); + this.setMonth(m,true); + return false; + }, + + // Respond to change event on the textbox or dropdown list + // This method raises OnDateChanged event on client side if it has been defined + onDateChanged : function () + { + if (this.options.OnDateChanged) + { + var date; + if (this.options.InputMode == "TextBox") + { + date=this.control.value; + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control).selectedIndex+1; + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control).selectedIndex; + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control).value; + date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); + } + this.options.OnDateChanged(this, date); + } + }, + + fireChangeEvent: function(element, capped) + { + if (capped) + { + var obj = this; + + if (typeof(obj.changeeventtimer)!="undefined") + { + clearTimeout(obj.changeeventtimer); + obj.changeeventtimer = null; + } + obj.changeeventtimer = setTimeout( + function() { obj.changeeventtimer = null; Event.fireEvent(element, "change"); }, + 1500 + ); + } + else + Event.fireEvent(element, "change"); + }, + + onChange : function(ref, date, capevents) + { + if(this.options.InputMode == "TextBox") + { + this.control.value = this.formatDate(); + this.fireChangeEvent(this.control, capevents); + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + var date = this.selectedDate; + if(day) + { + day.selectedIndex = date.getDate()-1; + } + if(month) + { + month.selectedIndex = date.getMonth(); + } + if(year) + { + var years = year.options; + var currentYear = date.getFullYear(); + for(var i = 0; i < years.length; i++) + years[i].selected = years[i].value.toInteger() == currentYear; + } + this.fireChangeEvent(day || month || year, capevents); + } + }, + + formatDate : function() + { + return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : ''; + }, + + newDate : function(date) + { + if(!date) + date = new Date(); + if(typeof(date) == "string" || typeof(date) == "number") + date = new Date(date); + return new Date(Math.min(Math.max(date.getFullYear(),this.FromYear),this.UpToYear), date.getMonth(), date.getDate(), 0,0,0); + }, + + setSelectedDate : function(date, capevents) + { + if (date == null) + return; + var old=this.selectedDate; + this.selectedDate = this.newDate(date); + var dateChanged=(old - this.selectedDate != 0) || ( this.options.InputMode == "TextBox" && this.control.value != this.formatDate()); + + this.updateHeader(); + this.update(); + if (dateChanged && typeof(this.onChange) == "function") + this.onChange(this, date, capevents); + }, + + getElement : function() + { + return this._calDiv; + }, + + getSelectedDate : function () + { + return this.selectedDate == null ? null : this.newDate(this.selectedDate); + }, + + setYear : function(year) + { + var d = this.newDate(this.selectedDate); + d.setFullYear(year); + this.setSelectedDate(d); + }, + + setMonth : function (month, capevents) + { + var d = this.newDate(this.selectedDate); + d.setDate(Math.min(d.getDate(), this.getDaysPerMonth(month,d.getFullYear()))); + d.setMonth(month); + this.setSelectedDate(d,capevents); + }, + + nextMonth : function () + { + this.setMonth(this.selectedDate.getMonth()+1); + }, + + prevMonth : function () + { + this.setMonth(this.selectedDate.getMonth()-1); + }, + + getDaysPerMonth : function (month, year) + { + month = (Number(month)+12) % 12; + var days = [31,28,31,30,31,30,31,31,30,31,30,31]; + var res = days[month]; + if (month == 1 && ((!(year % 4) && (year % 100)) || !(year % 400))) //feburary, leap years has 29 + res++; + return res; + }, + + getDatePickerOffsetHeight : function() + { + if(this.options.InputMode == "TextBox") + return this.control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getDayListControl(this.control); + if(control) return control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + if(control) return control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if(control) return control.offsetHeight; + return 0; + }, + + show : function() + { + this.create(); + + if(!this.showing) + { + var pos = this.control.positionedOffset(); + + pos[1] += this.getDatePickerOffsetHeight(); + this._calDiv.style.top = (pos[1]-1) + "px"; + this._calDiv.style.display = "block"; + this._calDiv.style.left = pos[0] + "px"; + + this.documentClickEvent = this.hideOnClick.bindEvent(this); + this.documentKeyDownEvent = this.keyPressed.bindEvent(this); + Event.observe(document.body, "click", this.documentClickEvent); + var date = this.getDateFromInput(); + if(date) + { + this.selectedDate = date; + this.setSelectedDate(date); + } + Event.observe(document,"keydown", this.documentKeyDownEvent); + this.showing = true; + + if(this.positionMode=='Top') + { + this._calDiv.style.top = ((pos[1]-1) - this.getDatePickerOffsetHeight() - this._calDiv.offsetHeight) + 'px'; + if(Prado.Browser().ie) + this.iePopup = this._calDiv.style.top; + } + this.ieHack(false); + } + }, + + getDateFromInput : function() + { + if(this.options.InputMode == "TextBox") + return Date.SimpleParse($F(this.control), this.Format); + else + return Prado.WebUI.TDatePicker.getDropDownDate(this.control); + }, + + //hide the calendar when clicked outside any calendar + hideOnClick : function(ev) + { + if(!this.showing) return; + var el = Event.element(ev); + var within = false; + do + { + within = within || (el.className && Element.hasClassName(el, "TDatePicker_"+this.CalendarStyle)); + within = within || el == this.trigger; + within = within || el == this.control; + if(within) break; + el = el.parentNode; + } + while(el); + if(!within) this.hide(); + }, + + + hide : function() + { + if(this.showing) + { + this._calDiv.style.display = "none"; + if(this.iePopUp) + this.iePopUp.style.display = "none"; + this.showing = false; + Event.stopObserving(document.body, "click", this.documentClickEvent); + Event.stopObserving(document,"keydown", this.documentKeyDownEvent); + } + }, + + update : function() + { + // Calculate the number of days in the month for the selected date + var date = this.selectedDate; + var today = (this.newDate()).toISODate(); + + var selected = date.toISODate(); + var d1 = new Date(date.getFullYear(), date.getMonth(), 1); + var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1); + var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000)); + + // Find out the weekDay index for the first of this month + var firstIndex = (d1.getDay() - this.FirstDayOfWeek) % 7 ; + if (firstIndex < 0) + firstIndex += 7; + + var index = 0; + while (index < firstIndex) { + this.dateSlot[index].value = -1; + this.dateSlot[index].data.data = String.fromCharCode(160); + this.dateSlot[index].data.parentNode.className = "empty"; + index++; + } + + for (var i = 1; i <= monthLength; i++, index++) { + var slot = this.dateSlot[index]; + var slotNode = slot.data.parentNode; + slot.value = i; + slot.data.data = i; + slotNode.className = "date"; + //slotNode.style.color = ""; + if (d1.toISODate() == today) { + slotNode.className += " today"; + } + if (d1.toISODate() == selected) { + // slotNode.style.color = "blue"; + slotNode.className += " selected"; + } + d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1); + } + + var lastDateIndex = index; + + while(index < 42) { + this.dateSlot[index].value = -1; + this.dateSlot[index].data.data = String.fromCharCode(160); + this.dateSlot[index].data.parentNode.className = "empty"; + ++index; + } + + }, + + hover : function(ev) + { + if(Event.element(ev).tagName) + { + if(ev.type == "mouseover") + Event.element(ev).addClassName("hover"); + else + Event.element(ev).removeClassName("hover"); + } + }, + + updateHeader : function () { + + var options = this._monthSelect.options; + var m = this.selectedDate.getMonth(); + for(var i=0; i < options.length; ++i) { + options[i].selected = false; + if (options[i].value == m) { + options[i].selected = true; + } + } + + options = this._yearSelect.options; + var year = this.selectedDate.getFullYear(); + for(var i=0; i < options.length; ++i) { + options[i].selected = false; + if (options[i].value == year) { + options[i].selected = true; + } + } + + } +}); + +Object.extend(Prado.WebUI.TDatePicker, +{ + /** + * @return Date the date from drop down list options. + */ + getDropDownDate : function(control) + { + var now=new Date(); + var year=now.getFullYear(); + var month=now.getMonth(); + var day=1; + + var month_list = Prado.WebUI.TDatePicker.getMonthListControl(control); + var day_list = Prado.WebUI.TDatePicker.getDayListControl(control); + var year_list = Prado.WebUI.TDatePicker.getYearListControl(control); + + var day = day_list ? $F(day_list) : 1; + var month = month_list ? $F(month_list) : now.getMonth(); + var year = year_list ? $F(year_list) : now.getFullYear(); + + return new Date(year,month,day, 0, 0, 0); + }, + + getYearListControl : function(control) + { + return $(control.id+"_year"); + }, + + getMonthListControl : function(control) + { + return $(control.id+"_month"); + }, + + getDayListControl : function(control) + { + return $(control.id+"_day"); + } }); \ No newline at end of file diff --git a/framework/Web/Javascripts/source/prado/logger/logger.js b/framework/Web/Javascripts/source/prado/logger/logger.js index a48f401a..55cc1aa3 100644 --- a/framework/Web/Javascripts/source/prado/logger/logger.js +++ b/framework/Web/Javascripts/source/prado/logger/logger.js @@ -1,753 +1,753 @@ -/* - -Created By: Corey Johnson -E-mail: probablyCorey@gmail.com - -Requires: Prototype Javascript library (http://prototype.conio.net/) - -Use it all you want. Just remember to give me some credit :) - -*/ - -// ------------ -// Custom Event -// ------------ - -CustomEvent = Class.create(); -CustomEvent.prototype = { - initialize : function() { - this.listeners = [] - }, - - addListener : function(method) { - this.listeners.push(method) - }, - - removeListener : function(method) { - var foundIndexes = this._findListenerIndexes(method) - - for(var i = 0; i < foundIndexes.length; i++) { - this.listeners.splice(foundIndexes[i], 1) - } - }, - - dispatch : function(handler) { - for(var i = 0; i < this.listeners.length; i++) { - try { - this.listeners[i](handler) - } - catch (e) { - alert("Could not run the listener " + this.listeners[i] + ". " + e) - } - } - }, - - // Private Methods - // --------------- - _findListenerIndexes : function(method) { - var indexes = [] - for(var i = 0; i < this.listeners.length; i++) { - if (this.listeners[i] == method) { - indexes.push(i) - } - } - - return indexes - } -}; - -// ------ -// Cookie -// ------ - -var Cookie = { - set : function(name, value, expirationInDays, path) { - var cookie = escape(name) + "=" + escape(value) - - if (expirationInDays) { - var date = new Date() - date.setDate(date.getDate() + expirationInDays) - cookie += "; expires=" + date.toGMTString() - } - - if (path) { - cookie += ";path=" + path - } - - document.cookie = cookie - - if (value && (expirationInDays == undefined || expirationInDays > 0) && !this.get(name)) { - Logger.error("Cookie (" + name + ") was not set correctly... The value was " + value.toString().length + " charachters long (This may be over the cookie limit)"); - } - }, - - get : function(name) { - var pattern = "(^|;)\\s*" + escape(name) + "=([^;]+)" - - var m = document.cookie.match(pattern) - if (m && m[2]) { - return unescape(m[2]) - } - else return null - }, - - getAll : function() { - var cookies = document.cookie.split(';') - var cookieArray = [] - - for (var i = 0; i < cookies.length; i++) { - try { - var name = unescape(cookies[i].match(/^\s*([^=]+)/m)[1]) - var value = unescape(cookies[i].match(/=(.*$)/m)[1]) - } - catch (e) { - continue - } - - cookieArray.push({name : name, value : value}) - - if (cookieArray[name] != undefined) { - Logger.waring("Trying to retrieve cookie named(" + name + "). There appears to be another property with this name though."); - } - - cookieArray[name] = value - } - - return cookieArray - }, - - clear : function(name) { - this.set(name, "", -1) - }, - - clearAll : function() { - var cookies = this.getAll() - - for(var i = 0; i < cookies.length; i++) { - this.clear(cookies[i].name) - } - - } -}; - -// ------ -// Logger -// ----- - -Logger = { - logEntries : [], - - onupdate : new CustomEvent(), - onclear : new CustomEvent(), - - - // Logger output - log : function(message, tag) { - var logEntry = new LogEntry(message, tag || "info") - this.logEntries.push(logEntry) - this.onupdate.dispatch(logEntry) - }, - - info : function(message) { - this.log(message, 'info') - if(typeof(console) != "undefined") - console.info(message); - }, - - debug : function(message) { - this.log(message, 'debug') - if(typeof(console) != "undefined") - console.debug(message); - }, - - warn : function(message) { - this.log(message, 'warning') - if(typeof(console) != "undefined") - console.warn(message); - }, - - error : function(message, error) { - this.log(message + ": \n" + error, 'error') - if(typeof(console) != "undefined") - console.error(message + ": \n" + error); - - }, - - clear : function () { - this.logEntries = [] - this.onclear.dispatch() - } -}; - -LogEntry = Class.create() -LogEntry.prototype = { - initialize : function(message, tag) { - this.message = message - this.tag = tag - } -}; - -LogConsole = Class.create(); -LogConsole.prototype = { - - // Properties - // ---------- - commandHistory : [], - commandIndex : 0, - - hidden : true, - - // Methods - // ------- - - initialize : function(toggleKey) { - this.outputCount = 0 - this.tagPattern = Cookie.get('tagPattern') || ".*" - - // I hate writing javascript in HTML... but what's a better alternative - this.logElement = document.createElement('div') - document.body.appendChild(this.logElement) - Element.hide(this.logElement) - - this.logElement.style.position = "absolute" - this.logElement.style.left = '0px' - this.logElement.style.width = '100%' - - this.logElement.style.textAlign = "left" - this.logElement.style.fontFamily = "lucida console" - this.logElement.style.fontSize = "100%" - this.logElement.style.backgroundColor = 'darkgray' - this.logElement.style.opacity = 0.9 - this.logElement.style.zIndex = 2000 - - // Add toolbarElement - this.toolbarElement = document.createElement('div') - this.logElement.appendChild(this.toolbarElement) - this.toolbarElement.style.padding = "0 0 0 2px" - - // Add buttons - this.buttonsContainerElement = document.createElement('span') - this.toolbarElement.appendChild(this.buttonsContainerElement) - - this.buttonsContainerElement.innerHTML += '' - this.buttonsContainerElement.innerHTML += '' - if(!Prado.Inspector.disabled) - this.buttonsContainerElement.innerHTML += '' - - - //Add Tag Filter - this.tagFilterContainerElement = document.createElement('span') - this.toolbarElement.appendChild(this.tagFilterContainerElement) - this.tagFilterContainerElement.style.cssFloat = 'left' - this.tagFilterContainerElement.appendChild(document.createTextNode("Log Filter")) - - this.tagFilterElement = document.createElement('input') - this.tagFilterContainerElement.appendChild(this.tagFilterElement) - this.tagFilterElement.style.width = '200px' - this.tagFilterElement.value = this.tagPattern - this.tagFilterElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out - - Event.observe(this.tagFilterElement, 'keyup', this.updateTags.bind(this)) - Event.observe(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this)) - - // Add outputElement - this.outputElement = document.createElement('div') - this.logElement.appendChild(this.outputElement) - this.outputElement.style.overflow = "auto" - this.outputElement.style.clear = "both" - this.outputElement.style.height = "200px" - this.outputElement.style.backgroundColor = 'black' - - this.inputContainerElement = document.createElement('div') - this.inputContainerElement.style.width = "100%" - this.logElement.appendChild(this.inputContainerElement) - - this.inputElement = document.createElement('input') - this.inputContainerElement.appendChild(this.inputElement) - this.inputElement.style.width = '100%' - this.inputElement.style.borderWidth = '0px' // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0 - this.inputElement.style.margin = '0px' - this.inputElement.style.padding = '0px' - this.inputElement.value = 'Type command here' - this.inputElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out - - Event.observe(this.inputElement, 'keyup', this.handleInput.bind(this)) - Event.observe(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this)) - - if(document.all && !window.opera) - { - window.setInterval(this.repositionWindow.bind(this), 500) - } - else - { - this.logElement.style.position="fixed"; - this.logElement.style.bottom="0px"; - } - var self=this; - Event.observe(document, 'keydown', function(e) - { - if((e.altKey==true) && Event.keyCode(e) == toggleKey ) //Alt+J | Ctrl+J - self.toggle(); - }); - - // Listen to the logger.... - Logger.onupdate.addListener(this.logUpdate.bind(this)) - Logger.onclear.addListener(this.clear.bind(this)) - - // Preload log element with the log entries that have been entered - for (var i = 0; i < Logger.logEntries.length; i++) { - this.logUpdate(Logger.logEntries[i]) - } - - // Feed all errors into the logger (For some unknown reason I can only get this to work - // with an inline event declaration) - Event.observe(window, 'error', function(msg, url, lineNumber) {Logger.error("Error in (" + (url || location) + ") on line "+lineNumber+"", msg)}) - - // Allow acess key link - var accessElement = document.createElement('span') - accessElement.innerHTML = '' - document.body.appendChild(accessElement) - - if (Cookie.get('ConsoleVisible') == 'true') { - this.toggle() - } - }, - - toggle : function() { - if (this.logElement.style.display == 'none') { - this.show() - } - else { - this.hide() - } - }, - - show : function() { - Element.show(this.logElement) - this.outputElement.scrollTop = this.outputElement.scrollHeight // Scroll to bottom when toggled - if(document.all && !window.opera) - this.repositionWindow(); - Cookie.set('ConsoleVisible', 'true') - this.inputElement.select() - this.hidden = false; - }, - - hide : function() { - this.hidden = true; - Element.hide(this.logElement) - Cookie.set('ConsoleVisible', 'false') - }, - - output : function(message, style) { - // If we are at the bottom of the window, then keep scrolling with the output - var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight - - this.outputCount++ - style = (style ? style += ';' : '') - style += 'padding:1px;margin:0 0 5px 0' - - if (this.outputCount % 2 == 0) style += ";background-color:#101010" - - message = message || "undefined" - message = message.toString().escapeHTML() - - this.outputElement.innerHTML += "
" + message + "
" - - if (shouldScroll) { - this.outputElement.scrollTop = this.outputElement.scrollHeight - } - }, - - updateTags : function() { - var pattern = this.tagFilterElement.value - - if (this.tagPattern == pattern) return - - try { - new RegExp(pattern) - } - catch (e) { - return - } - - this.tagPattern = pattern - Cookie.set('tagPattern', this.tagPattern) - - this.outputElement.innerHTML = "" - - // Go through each log entry again - this.outputCount = 0; - for (var i = 0; i < Logger.logEntries.length; i++) { - this.logUpdate(Logger.logEntries[i]) - } - }, - - repositionWindow : function() { - var offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop - var pageHeight = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight - this.logElement.style.top = (offset + pageHeight - Element.getHeight(this.logElement)) + "px" - }, - - // Event Handlers - // -------------- - - logUpdate : function(logEntry) { - if (logEntry.tag.search(new RegExp(this.tagPattern, 'igm')) == -1) return - var style = '' - if (logEntry.tag.search(/error/) != -1) style += 'color:red' - else if (logEntry.tag.search(/warning/) != -1) style += 'color:orange' - else if (logEntry.tag.search(/debug/) != -1) style += 'color:green' - else if (logEntry.tag.search(/info/) != -1) style += 'color:white' - else style += 'color:yellow' - - this.output(logEntry.message, style) - }, - - clear : function(e) { - this.outputElement.innerHTML = "" - }, - - handleInput : function(e) { - if (e.keyCode == Event.KEY_RETURN ) { - var command = this.inputElement.value - - switch(command) { - case "clear": - Logger.clear() - break - - default: - var consoleOutput = "" - - try { - consoleOutput = eval(this.inputElement.value) - } - catch (e) { - Logger.error("Problem parsing input <" + command + ">", e) - break - } - - Logger.log(consoleOutput) - break - } - - if (this.inputElement.value != "" && this.inputElement.value != this.commandHistory[0]) { - this.commandHistory.unshift(this.inputElement.value) - } - - this.commandIndex = 0 - this.inputElement.value = "" - } - else if (e.keyCode == Event.KEY_UP && this.commandHistory.length > 0) { - this.inputElement.value = this.commandHistory[this.commandIndex] - - if (this.commandIndex < this.commandHistory.length - 1) { - this.commandIndex += 1 - } - } - else if (e.keyCode == Event.KEY_DOWN && this.commandHistory.length > 0) { - if (this.commandIndex > 0) { - this.commandIndex -= 1 - } - - this.inputElement.value = this.commandHistory[this.commandIndex] - } - else { - this.commandIndex = 0 - } - } -}; - - -// ------------------------- -// Helper Functions And Junk -// ------------------------- -function inspect(o) -{ - var objtype = typeof(o); - if (objtype == "undefined") { - return "undefined"; - } else if (objtype == "number" || objtype == "boolean") { - return o + ""; - } else if (o === null) { - return "null"; - } - - try { - var ostring = (o + ""); - } catch (e) { - return "[" + typeof(o) + "]"; - } - - if (typeof(o) == "function") - { - o = ostring.replace(/^\s+/, ""); - var idx = o.indexOf("{"); - if (idx != -1) { - o = o.substr(0, idx) + "{...}"; - } - return o; - } - - var reprString = function (o) - { - return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' - ).replace(/[\f]/g, "\\f" - ).replace(/[\b]/g, "\\b" - ).replace(/[\n]/g, "\\n" - ).replace(/[\t]/g, "\\t" - ).replace(/[\r]/g, "\\r"); - }; - - if (objtype == "string") { - return reprString(o); - } - // recurse - var me = arguments.callee; - // short-circuit for objects that support "json" serialization - // if they return "self" then just pass-through... - var newObj; - if (typeof(o.__json__) == "function") { - newObj = o.__json__(); - if (o !== newObj) { - return me(newObj); - } - } - if (typeof(o.json) == "function") { - newObj = o.json(); - if (o !== newObj) { - return me(newObj); - } - } - // array - if (objtype != "function" && typeof(o.length) == "number") { - var res = []; - for (var i = 0; i < o.length; i++) { - var val = me(o[i]); - if (typeof(val) != "string") { - val = "undefined"; - } - res.push(val); - } - return "[" + res.join(", ") + "]"; - } - - // generic object code path - res = []; - for (var k in o) { - var useKey; - if (typeof(k) == "number") { - useKey = '"' + k + '"'; - } else if (typeof(k) == "string") { - useKey = reprString(k); - } else { - // skip non-string or number keys - continue; - } - val = me(o[k]); - if (typeof(val) != "string") { - // skip non-serializable values - continue; - } - res.push(useKey + ":" + val); - } - return "{" + res.join(", ") + "}"; -}; - -Array.prototype.contains = function(object) { - for(var i = 0; i < this.length; i++) { - if (object == this[i]) return true - } - - return false -}; - -// Helper Alias for simple logging -var puts = function() {return Logger.log(arguments[0], arguments[1])}; - -/************************************* - - Javascript Object Tree - version 1.0 - last revision:04.11.2004 - steve@slayeroffice.com - http://slayeroffice.com - - (c)2004 S.G. Chipman - - Please notify me of any modifications - you make to this code so that I can - update the version hosted on slayeroffice.com - - -************************************/ -if(typeof Prado == "undefined") - var Prado = {}; -Prado.Inspector = -{ - d : document, - types : new Array(), - objs : new Array(), - hidden : new Array(), - opera : window.opera, - displaying : '', - nameList : new Array(), - - format : function(str) { - if(typeof(str) != "string") return str; - str=str.replace(//g,">"); - return str; - }, - - parseJS : function(obj) { - var name; - if(typeof obj == "string") { name = obj; obj = eval(obj); } - win= typeof obj == 'undefined' ? window : obj; - this.displaying = name ? name : win.toString(); - for(js in win) { - try { - if(win[js] && js.toString().indexOf("Inspector")==-1 && (win[js]+"").indexOf("[native code]")==-1) { - - t=typeof(win[js]); - if(!this.objs[t.toString()]) { - this.types[this.types.length]=t; - this.objs[t]={}; - this.nameList[t] = new Array(); - } - this.nameList[t].push(js); - this.objs[t][js] = this.format(win[js]+""); - } - } catch(err) { } - } - - for(i=0;i-1){ - this.d.getElementById(spanID).innerHTML="[-]"; - } else { - this.d.getElementById(spanID).innerHTML="[+]"; - } - }, - - buildInspectionLevel : function() - { - var display = this.displaying; - var list = display.split("."); - var links = ["[object Window]"]; - var name = ''; - if(display.indexOf("[object ") >= 0) return links.join("."); - for(var i = 0; i < list.length; i++) - { - name += (name.length ? "." : "") + list[i]; - links[i+1] = ""+list[i]+""; - } - return links.join("."); - }, - - buildTree : function() { - mHTML = "
Inspecting "+this.buildInspectionLevel()+"
"; - mHTML +="
    "; - this.types.sort(); - var so_objIndex=0; - for(i=0;i[+]" + this.types[i] + " (" + this.nameList[this.types[i]].length + ")
      "; - this.hidden["ul"+i]=0; - for(e=0;e= 0 && /^[a-zA-Z_]/.test(prop)) - { - if(this.displaying.indexOf("[object ") < 0) - more = " more"; - else if(this.displaying.indexOf("[object Window]") >= 0) - more = " more"; - } - mHTML+="
    • [+]" + prop + "
      • " + value + more + "
      "; - this.hidden["mul"+so_objIndex]=0; - so_objIndex++; - } - mHTML+="
    "; - } - mHTML+="
"; - this.d.getElementById("so_mContainer").innerHTML =mHTML; - }, - - handleKeyEvent : function(e) { - keyCode=document.all?window.event.keyCode:e.keyCode; - if(keyCode==27) { - this.cleanUp(); - } - }, - - cleanUp : function() - { - if(this.d.getElementById("so_mContainer")) - { - this.d.body.removeChild(this.d.getElementById("so_mContainer")); - this.d.body.removeChild(this.d.getElementById("so_mStyle")); - if(typeof Event != "undefined") - Event.stopObserving(this.d, "keydown", this.dKeyDownEvent); - this.types = new Array(); - this.objs = new Array(); - this.hidden = new Array(); - } - }, - - disabled : document.all && !this.opera, - - inspect : function(obj) - { - if(this.disabled)return alert("Sorry, this only works in Mozilla and Firefox currently."); - this.cleanUp(); - mObj=this.d.body.appendChild(this.d.createElement("div")); - mObj.id="so_mContainer"; - sObj=this.d.body.appendChild(this.d.createElement("style")); - sObj.id="so_mStyle"; - sObj.type="text/css"; - sObj.innerHTML = this.style; - this.dKeyDownEvent = this.handleKeyEvent.bind(this); - if(typeof Event != "undefined") - Event.observe(this.d, "keydown", this.dKeyDownEvent); - - this.parseJS(obj); - this.buildTree(); - - cObj=mObj.appendChild(this.d.createElement("div")); - cObj.className="credits"; - cObj.innerHTML = "[esc] to close
Javascript Object Tree V2.0."; - - window.scrollTo(0,0); - }, - - style : "#so_mContainer { position:absolute; top:5px; left:5px; background-color:#E3EBED; text-align:left; font:9pt verdana; width:85%; border:2px solid #000; padding:5px; z-index:1000; color:#000; } " + - "#so_mContainer ul { padding-left:20px; } " + - "#so_mContainer ul li { display:block; list-style-type:none; list-style-image:url(); line-height:2em; -moz-border-radius:.75em; font:10px verdana; padding:0; margin:2px; color:#000; } " + - "#so_mContainer li:hover { background-color:#E3EBED; } " + - "#so_mContainer ul li span { position:relative; width:15px; height:15px; margin-right:4px; } " + - "#so_mContainer pre { background-color:#F9FAFB; border:1px solid #638DA1; height:auto; padding:5px; font:9px verdana; color:#000; } " + - "#so_mContainer .topLevel { margin:0; padding:0; } " + - "#so_mContainer .credits { float:left; width:200px; font:6.5pt verdana; color:#000; padding:2px; margin-left:5px; text-align:left; border-top:1px solid #000; margin-top:15px; width:75%; } " + - "#so_mContainer .credits a { font:9px verdana; font-weight:bold; color:#004465; text-decoration:none; background-color:transparent; }" -}; - -//similar function to var_dump in PHP, brings up the javascript object tree UI. -function var_dump(obj) -{ - Prado.Inspector.inspect(obj); -} - -//similar function to print_r for PHP -var print_r = inspect; - +/* + +Created By: Corey Johnson +E-mail: probablyCorey@gmail.com + +Requires: Prototype Javascript library (http://prototype.conio.net/) + +Use it all you want. Just remember to give me some credit :) + +*/ + +// ------------ +// Custom Event +// ------------ + +CustomEvent = Class.create(); +CustomEvent.prototype = { + initialize : function() { + this.listeners = [] + }, + + addListener : function(method) { + this.listeners.push(method) + }, + + removeListener : function(method) { + var foundIndexes = this._findListenerIndexes(method) + + for(var i = 0; i < foundIndexes.length; i++) { + this.listeners.splice(foundIndexes[i], 1) + } + }, + + dispatch : function(handler) { + for(var i = 0; i < this.listeners.length; i++) { + try { + this.listeners[i](handler) + } + catch (e) { + alert("Could not run the listener " + this.listeners[i] + ". " + e) + } + } + }, + + // Private Methods + // --------------- + _findListenerIndexes : function(method) { + var indexes = [] + for(var i = 0; i < this.listeners.length; i++) { + if (this.listeners[i] == method) { + indexes.push(i) + } + } + + return indexes + } +}; + +// ------ +// Cookie +// ------ + +var Cookie = { + set : function(name, value, expirationInDays, path) { + var cookie = escape(name) + "=" + escape(value) + + if (expirationInDays) { + var date = new Date() + date.setDate(date.getDate() + expirationInDays) + cookie += "; expires=" + date.toGMTString() + } + + if (path) { + cookie += ";path=" + path + } + + document.cookie = cookie + + if (value && (expirationInDays == undefined || expirationInDays > 0) && !this.get(name)) { + Logger.error("Cookie (" + name + ") was not set correctly... The value was " + value.toString().length + " charachters long (This may be over the cookie limit)"); + } + }, + + get : function(name) { + var pattern = "(^|;)\\s*" + escape(name) + "=([^;]+)" + + var m = document.cookie.match(pattern) + if (m && m[2]) { + return unescape(m[2]) + } + else return null + }, + + getAll : function() { + var cookies = document.cookie.split(';') + var cookieArray = [] + + for (var i = 0; i < cookies.length; i++) { + try { + var name = unescape(cookies[i].match(/^\s*([^=]+)/m)[1]) + var value = unescape(cookies[i].match(/=(.*$)/m)[1]) + } + catch (e) { + continue + } + + cookieArray.push({name : name, value : value}) + + if (cookieArray[name] != undefined) { + Logger.waring("Trying to retrieve cookie named(" + name + "). There appears to be another property with this name though."); + } + + cookieArray[name] = value + } + + return cookieArray + }, + + clear : function(name) { + this.set(name, "", -1) + }, + + clearAll : function() { + var cookies = this.getAll() + + for(var i = 0; i < cookies.length; i++) { + this.clear(cookies[i].name) + } + + } +}; + +// ------ +// Logger +// ----- + +Logger = { + logEntries : [], + + onupdate : new CustomEvent(), + onclear : new CustomEvent(), + + + // Logger output + log : function(message, tag) { + var logEntry = new LogEntry(message, tag || "info") + this.logEntries.push(logEntry) + this.onupdate.dispatch(logEntry) + }, + + info : function(message) { + this.log(message, 'info') + if(typeof(console) != "undefined") + console.info(message); + }, + + debug : function(message) { + this.log(message, 'debug') + if(typeof(console) != "undefined") + console.debug(message); + }, + + warn : function(message) { + this.log(message, 'warning') + if(typeof(console) != "undefined") + console.warn(message); + }, + + error : function(message, error) { + this.log(message + ": \n" + error, 'error') + if(typeof(console) != "undefined") + console.error(message + ": \n" + error); + + }, + + clear : function () { + this.logEntries = [] + this.onclear.dispatch() + } +}; + +LogEntry = Class.create() +LogEntry.prototype = { + initialize : function(message, tag) { + this.message = message + this.tag = tag + } +}; + +LogConsole = Class.create(); +LogConsole.prototype = { + + // Properties + // ---------- + commandHistory : [], + commandIndex : 0, + + hidden : true, + + // Methods + // ------- + + initialize : function(toggleKey) { + this.outputCount = 0 + this.tagPattern = Cookie.get('tagPattern') || ".*" + + // I hate writing javascript in HTML... but what's a better alternative + this.logElement = document.createElement('div') + document.body.appendChild(this.logElement) + Element.hide(this.logElement) + + this.logElement.style.position = "absolute" + this.logElement.style.left = '0px' + this.logElement.style.width = '100%' + + this.logElement.style.textAlign = "left" + this.logElement.style.fontFamily = "lucida console" + this.logElement.style.fontSize = "100%" + this.logElement.style.backgroundColor = 'darkgray' + this.logElement.style.opacity = 0.9 + this.logElement.style.zIndex = 2000 + + // Add toolbarElement + this.toolbarElement = document.createElement('div') + this.logElement.appendChild(this.toolbarElement) + this.toolbarElement.style.padding = "0 0 0 2px" + + // Add buttons + this.buttonsContainerElement = document.createElement('span') + this.toolbarElement.appendChild(this.buttonsContainerElement) + + this.buttonsContainerElement.innerHTML += '' + this.buttonsContainerElement.innerHTML += '' + if(!Prado.Inspector.disabled) + this.buttonsContainerElement.innerHTML += '' + + + //Add Tag Filter + this.tagFilterContainerElement = document.createElement('span') + this.toolbarElement.appendChild(this.tagFilterContainerElement) + this.tagFilterContainerElement.style.cssFloat = 'left' + this.tagFilterContainerElement.appendChild(document.createTextNode("Log Filter")) + + this.tagFilterElement = document.createElement('input') + this.tagFilterContainerElement.appendChild(this.tagFilterElement) + this.tagFilterElement.style.width = '200px' + this.tagFilterElement.value = this.tagPattern + this.tagFilterElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out + + Event.observe(this.tagFilterElement, 'keyup', this.updateTags.bind(this)) + Event.observe(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this)) + + // Add outputElement + this.outputElement = document.createElement('div') + this.logElement.appendChild(this.outputElement) + this.outputElement.style.overflow = "auto" + this.outputElement.style.clear = "both" + this.outputElement.style.height = "200px" + this.outputElement.style.backgroundColor = 'black' + + this.inputContainerElement = document.createElement('div') + this.inputContainerElement.style.width = "100%" + this.logElement.appendChild(this.inputContainerElement) + + this.inputElement = document.createElement('input') + this.inputContainerElement.appendChild(this.inputElement) + this.inputElement.style.width = '100%' + this.inputElement.style.borderWidth = '0px' // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0 + this.inputElement.style.margin = '0px' + this.inputElement.style.padding = '0px' + this.inputElement.value = 'Type command here' + this.inputElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out + + Event.observe(this.inputElement, 'keyup', this.handleInput.bind(this)) + Event.observe(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this)) + + if(document.all && !window.opera) + { + window.setInterval(this.repositionWindow.bind(this), 500) + } + else + { + this.logElement.style.position="fixed"; + this.logElement.style.bottom="0px"; + } + var self=this; + Event.observe(document, 'keydown', function(e) + { + if((e.altKey==true) && Event.keyCode(e) == toggleKey ) //Alt+J | Ctrl+J + self.toggle(); + }); + + // Listen to the logger.... + Logger.onupdate.addListener(this.logUpdate.bind(this)) + Logger.onclear.addListener(this.clear.bind(this)) + + // Preload log element with the log entries that have been entered + for (var i = 0; i < Logger.logEntries.length; i++) { + this.logUpdate(Logger.logEntries[i]) + } + + // Feed all errors into the logger (For some unknown reason I can only get this to work + // with an inline event declaration) + Event.observe(window, 'error', function(msg, url, lineNumber) {Logger.error("Error in (" + (url || location) + ") on line "+lineNumber+"", msg)}) + + // Allow acess key link + var accessElement = document.createElement('span') + accessElement.innerHTML = '' + document.body.appendChild(accessElement) + + if (Cookie.get('ConsoleVisible') == 'true') { + this.toggle() + } + }, + + toggle : function() { + if (this.logElement.style.display == 'none') { + this.show() + } + else { + this.hide() + } + }, + + show : function() { + Element.show(this.logElement) + this.outputElement.scrollTop = this.outputElement.scrollHeight // Scroll to bottom when toggled + if(document.all && !window.opera) + this.repositionWindow(); + Cookie.set('ConsoleVisible', 'true') + this.inputElement.select() + this.hidden = false; + }, + + hide : function() { + this.hidden = true; + Element.hide(this.logElement) + Cookie.set('ConsoleVisible', 'false') + }, + + output : function(message, style) { + // If we are at the bottom of the window, then keep scrolling with the output + var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight + + this.outputCount++ + style = (style ? style += ';' : '') + style += 'padding:1px;margin:0 0 5px 0' + + if (this.outputCount % 2 == 0) style += ";background-color:#101010" + + message = message || "undefined" + message = message.toString().escapeHTML() + + this.outputElement.innerHTML += "
" + message + "
" + + if (shouldScroll) { + this.outputElement.scrollTop = this.outputElement.scrollHeight + } + }, + + updateTags : function() { + var pattern = this.tagFilterElement.value + + if (this.tagPattern == pattern) return + + try { + new RegExp(pattern) + } + catch (e) { + return + } + + this.tagPattern = pattern + Cookie.set('tagPattern', this.tagPattern) + + this.outputElement.innerHTML = "" + + // Go through each log entry again + this.outputCount = 0; + for (var i = 0; i < Logger.logEntries.length; i++) { + this.logUpdate(Logger.logEntries[i]) + } + }, + + repositionWindow : function() { + var offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop + var pageHeight = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight + this.logElement.style.top = (offset + pageHeight - Element.getHeight(this.logElement)) + "px" + }, + + // Event Handlers + // -------------- + + logUpdate : function(logEntry) { + if (logEntry.tag.search(new RegExp(this.tagPattern, 'igm')) == -1) return + var style = '' + if (logEntry.tag.search(/error/) != -1) style += 'color:red' + else if (logEntry.tag.search(/warning/) != -1) style += 'color:orange' + else if (logEntry.tag.search(/debug/) != -1) style += 'color:green' + else if (logEntry.tag.search(/info/) != -1) style += 'color:white' + else style += 'color:yellow' + + this.output(logEntry.message, style) + }, + + clear : function(e) { + this.outputElement.innerHTML = "" + }, + + handleInput : function(e) { + if (e.keyCode == Event.KEY_RETURN ) { + var command = this.inputElement.value + + switch(command) { + case "clear": + Logger.clear() + break + + default: + var consoleOutput = "" + + try { + consoleOutput = eval(this.inputElement.value) + } + catch (e) { + Logger.error("Problem parsing input <" + command + ">", e) + break + } + + Logger.log(consoleOutput) + break + } + + if (this.inputElement.value != "" && this.inputElement.value != this.commandHistory[0]) { + this.commandHistory.unshift(this.inputElement.value) + } + + this.commandIndex = 0 + this.inputElement.value = "" + } + else if (e.keyCode == Event.KEY_UP && this.commandHistory.length > 0) { + this.inputElement.value = this.commandHistory[this.commandIndex] + + if (this.commandIndex < this.commandHistory.length - 1) { + this.commandIndex += 1 + } + } + else if (e.keyCode == Event.KEY_DOWN && this.commandHistory.length > 0) { + if (this.commandIndex > 0) { + this.commandIndex -= 1 + } + + this.inputElement.value = this.commandHistory[this.commandIndex] + } + else { + this.commandIndex = 0 + } + } +}; + + +// ------------------------- +// Helper Functions And Junk +// ------------------------- +function inspect(o) +{ + var objtype = typeof(o); + if (objtype == "undefined") { + return "undefined"; + } else if (objtype == "number" || objtype == "boolean") { + return o + ""; + } else if (o === null) { + return "null"; + } + + try { + var ostring = (o + ""); + } catch (e) { + return "[" + typeof(o) + "]"; + } + + if (typeof(o) == "function") + { + o = ostring.replace(/^\s+/, ""); + var idx = o.indexOf("{"); + if (idx != -1) { + o = o.substr(0, idx) + "{...}"; + } + return o; + } + + var reprString = function (o) + { + return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' + ).replace(/[\f]/g, "\\f" + ).replace(/[\b]/g, "\\b" + ).replace(/[\n]/g, "\\n" + ).replace(/[\t]/g, "\\t" + ).replace(/[\r]/g, "\\r"); + }; + + if (objtype == "string") { + return reprString(o); + } + // recurse + var me = arguments.callee; + // short-circuit for objects that support "json" serialization + // if they return "self" then just pass-through... + var newObj; + if (typeof(o.__json__) == "function") { + newObj = o.__json__(); + if (o !== newObj) { + return me(newObj); + } + } + if (typeof(o.json) == "function") { + newObj = o.json(); + if (o !== newObj) { + return me(newObj); + } + } + // array + if (objtype != "function" && typeof(o.length) == "number") { + var res = []; + for (var i = 0; i < o.length; i++) { + var val = me(o[i]); + if (typeof(val) != "string") { + val = "undefined"; + } + res.push(val); + } + return "[" + res.join(", ") + "]"; + } + + // generic object code path + res = []; + for (var k in o) { + var useKey; + if (typeof(k) == "number") { + useKey = '"' + k + '"'; + } else if (typeof(k) == "string") { + useKey = reprString(k); + } else { + // skip non-string or number keys + continue; + } + val = me(o[k]); + if (typeof(val) != "string") { + // skip non-serializable values + continue; + } + res.push(useKey + ":" + val); + } + return "{" + res.join(", ") + "}"; +}; + +Array.prototype.contains = function(object) { + for(var i = 0; i < this.length; i++) { + if (object == this[i]) return true + } + + return false +}; + +// Helper Alias for simple logging +var puts = function() {return Logger.log(arguments[0], arguments[1])}; + +/************************************* + + Javascript Object Tree + version 1.0 + last revision:04.11.2004 + steve@slayeroffice.com + http://slayeroffice.com + + (c)2004 S.G. Chipman + + Please notify me of any modifications + you make to this code so that I can + update the version hosted on slayeroffice.com + + +************************************/ +if(typeof Prado == "undefined") + var Prado = {}; +Prado.Inspector = +{ + d : document, + types : new Array(), + objs : new Array(), + hidden : new Array(), + opera : window.opera, + displaying : '', + nameList : new Array(), + + format : function(str) { + if(typeof(str) != "string") return str; + str=str.replace(//g,">"); + return str; + }, + + parseJS : function(obj) { + var name; + if(typeof obj == "string") { name = obj; obj = eval(obj); } + win= typeof obj == 'undefined' ? window : obj; + this.displaying = name ? name : win.toString(); + for(js in win) { + try { + if(win[js] && js.toString().indexOf("Inspector")==-1 && (win[js]+"").indexOf("[native code]")==-1) { + + t=typeof(win[js]); + if(!this.objs[t.toString()]) { + this.types[this.types.length]=t; + this.objs[t]={}; + this.nameList[t] = new Array(); + } + this.nameList[t].push(js); + this.objs[t][js] = this.format(win[js]+""); + } + } catch(err) { } + } + + for(i=0;i-1){ + this.d.getElementById(spanID).innerHTML="[-]"; + } else { + this.d.getElementById(spanID).innerHTML="[+]"; + } + }, + + buildInspectionLevel : function() + { + var display = this.displaying; + var list = display.split("."); + var links = ["[object Window]"]; + var name = ''; + if(display.indexOf("[object ") >= 0) return links.join("."); + for(var i = 0; i < list.length; i++) + { + name += (name.length ? "." : "") + list[i]; + links[i+1] = ""+list[i]+""; + } + return links.join("."); + }, + + buildTree : function() { + mHTML = "
Inspecting "+this.buildInspectionLevel()+"
"; + mHTML +="
    "; + this.types.sort(); + var so_objIndex=0; + for(i=0;i[+]" + this.types[i] + " (" + this.nameList[this.types[i]].length + ")
      "; + this.hidden["ul"+i]=0; + for(e=0;e= 0 && /^[a-zA-Z_]/.test(prop)) + { + if(this.displaying.indexOf("[object ") < 0) + more = " more"; + else if(this.displaying.indexOf("[object Window]") >= 0) + more = " more"; + } + mHTML+="
    • [+]" + prop + "
      • " + value + more + "
      "; + this.hidden["mul"+so_objIndex]=0; + so_objIndex++; + } + mHTML+="
    "; + } + mHTML+="
"; + this.d.getElementById("so_mContainer").innerHTML =mHTML; + }, + + handleKeyEvent : function(e) { + keyCode=document.all?window.event.keyCode:e.keyCode; + if(keyCode==27) { + this.cleanUp(); + } + }, + + cleanUp : function() + { + if(this.d.getElementById("so_mContainer")) + { + this.d.body.removeChild(this.d.getElementById("so_mContainer")); + this.d.body.removeChild(this.d.getElementById("so_mStyle")); + if(typeof Event != "undefined") + Event.stopObserving(this.d, "keydown", this.dKeyDownEvent); + this.types = new Array(); + this.objs = new Array(); + this.hidden = new Array(); + } + }, + + disabled : document.all && !this.opera, + + inspect : function(obj) + { + if(this.disabled)return alert("Sorry, this only works in Mozilla and Firefox currently."); + this.cleanUp(); + mObj=this.d.body.appendChild(this.d.createElement("div")); + mObj.id="so_mContainer"; + sObj=this.d.body.appendChild(this.d.createElement("style")); + sObj.id="so_mStyle"; + sObj.type="text/css"; + sObj.innerHTML = this.style; + this.dKeyDownEvent = this.handleKeyEvent.bind(this); + if(typeof Event != "undefined") + Event.observe(this.d, "keydown", this.dKeyDownEvent); + + this.parseJS(obj); + this.buildTree(); + + cObj=mObj.appendChild(this.d.createElement("div")); + cObj.className="credits"; + cObj.innerHTML = "[esc] to close
Javascript Object Tree V2.0."; + + window.scrollTo(0,0); + }, + + style : "#so_mContainer { position:absolute; top:5px; left:5px; background-color:#E3EBED; text-align:left; font:9pt verdana; width:85%; border:2px solid #000; padding:5px; z-index:1000; color:#000; } " + + "#so_mContainer ul { padding-left:20px; } " + + "#so_mContainer ul li { display:block; list-style-type:none; list-style-image:url(); line-height:2em; -moz-border-radius:.75em; font:10px verdana; padding:0; margin:2px; color:#000; } " + + "#so_mContainer li:hover { background-color:#E3EBED; } " + + "#so_mContainer ul li span { position:relative; width:15px; height:15px; margin-right:4px; } " + + "#so_mContainer pre { background-color:#F9FAFB; border:1px solid #638DA1; height:auto; padding:5px; font:9px verdana; color:#000; } " + + "#so_mContainer .topLevel { margin:0; padding:0; } " + + "#so_mContainer .credits { float:left; width:200px; font:6.5pt verdana; color:#000; padding:2px; margin-left:5px; text-align:left; border-top:1px solid #000; margin-top:15px; width:75%; } " + + "#so_mContainer .credits a { font:9px verdana; font-weight:bold; color:#004465; text-decoration:none; background-color:transparent; }" +}; + +//similar function to var_dump in PHP, brings up the javascript object tree UI. +function var_dump(obj) +{ + Prado.Inspector.inspect(obj); +} + +//similar function to print_r for PHP +var print_r = inspect; + diff --git a/framework/Web/Javascripts/source/prado/prado.js b/framework/Web/Javascripts/source/prado/prado.js index ce789456..2fcb2c1e 100644 --- a/framework/Web/Javascripts/source/prado/prado.js +++ b/framework/Web/Javascripts/source/prado/prado.js @@ -1,94 +1,94 @@ -/** - * Prado base namespace - * @namespace Prado - */ -var Prado = -{ - /** - * Version of Prado clientscripts - * @var Version - */ - Version: '3.2', - - /** - * Registry for Prado components - * @var Registry - */ - Registry: $H(), - - /** - * Returns browser information. - *
-	 * var browser = Prado.Browser();
-	 * alert(browser.ie); //should ouput true if IE, false otherwise
-	 * 
- * @function {object} ? - * @version 1.0 - * @returns browserinfo - * @... {string} agent - Reported user agent - * @... {string} ver - Reported agent version - * @... {0|1} dom - 1 for DOM browsers - * @... {0|1} ns4 - 1 for Netscape 4 - * @... {0|1} ns6 - 1 for Netscape 6 and Firefox - * @... {boolean} ie3 - true for IE 3 - * @... {0|1} ie5 - 1 for IE 5 - * @... {0|1} ie6 - 1 for IE 6 - * @... {0|1} ie4 - 1 for IE 4 - * @... {0|1} ie - 1 for IE 4-6 - * @... {0|1} hotjava - 1 for HotJava - * @... {0|1} ver3 - 1 for IE3 and HotJava - * @... {0|1} opera - 1 for Opera - * @... {boolean} opera7 - true for Opera 7 - * @... {0|1} operaOld - 1 for older Opera - * @... {0|1} bw - 1 for IE 4-6, Netscape 4&6, Firefox and Opera - * @... {boolean} mac - true for mac systems - * @... {static} Version - Version of returned structure (1.0) - */ - Browser : function() - { - var info = { Version : "1.0" }; - var is_major = parseInt( navigator.appVersion ); - info.nver = is_major; - info.ver = navigator.appVersion; - info.agent = navigator.userAgent; - info.dom = document.getElementById ? 1 : 0; - info.opera = window.opera ? 1 : 0; - info.ie5 = ( info.ver.indexOf( "MSIE 5" ) > -1 && info.dom && !info.opera ) ? 1 : 0; - info.ie6 = ( info.ver.indexOf( "MSIE 6" ) > -1 && info.dom && !info.opera ) ? 1 : 0; - info.ie4 = ( document.all && !info.dom && !info.opera ) ? 1 : 0; - info.ie = info.ie4 || info.ie5 || info.ie6; - info.mac = info.agent.indexOf( "Mac" ) > -1; - info.ns6 = ( info.dom && parseInt( info.ver ) >= 5 ) ? 1 : 0; - info.ie3 = ( info.ver.indexOf( "MSIE" ) && ( is_major < 4 ) ); - info.hotjava = ( info.agent.toLowerCase().indexOf( 'hotjava' ) != -1 ) ? 1 : 0; - info.ns4 = ( document.layers && !info.dom && !info.hotjava ) ? 1 : 0; - info.bw = ( info.ie6 || info.ie5 || info.ie4 || info.ns4 || info.ns6 || info.opera ); - info.ver3 = ( info.hotjava || info.ie3 ); - info.opera7 = ( ( info.agent.toLowerCase().indexOf( 'opera 7' ) > -1 ) || ( info.agent.toLowerCase().indexOf( 'opera/7' ) > -1 ) ); - info.operaOld = info.opera && !info.opera7; - return info; - }, - - /** - * Import CSS from Url. - * @function ? - * @param doc - document DOM object - * @param css_file - Url to CSS file - */ - ImportCss : function(doc, css_file) - { - if (Prado.Browser().ie) - var styleSheet = doc.createStyleSheet(css_file); - else - { - var elm = doc.createElement("link"); - - elm.rel = "stylesheet"; - elm.href = css_file; - var headArr; - - if (headArr = doc.getElementsByTagName("head")) - headArr[0].appendChild(elm); - } - } -}; +/** + * Prado base namespace + * @namespace Prado + */ +var Prado = +{ + /** + * Version of Prado clientscripts + * @var Version + */ + Version: '3.2', + + /** + * Registry for Prado components + * @var Registry + */ + Registry: $H(), + + /** + * Returns browser information. + *
+	 * var browser = Prado.Browser();
+	 * alert(browser.ie); //should ouput true if IE, false otherwise
+	 * 
+ * @function {object} ? + * @version 1.0 + * @returns browserinfo + * @... {string} agent - Reported user agent + * @... {string} ver - Reported agent version + * @... {0|1} dom - 1 for DOM browsers + * @... {0|1} ns4 - 1 for Netscape 4 + * @... {0|1} ns6 - 1 for Netscape 6 and Firefox + * @... {boolean} ie3 - true for IE 3 + * @... {0|1} ie5 - 1 for IE 5 + * @... {0|1} ie6 - 1 for IE 6 + * @... {0|1} ie4 - 1 for IE 4 + * @... {0|1} ie - 1 for IE 4-6 + * @... {0|1} hotjava - 1 for HotJava + * @... {0|1} ver3 - 1 for IE3 and HotJava + * @... {0|1} opera - 1 for Opera + * @... {boolean} opera7 - true for Opera 7 + * @... {0|1} operaOld - 1 for older Opera + * @... {0|1} bw - 1 for IE 4-6, Netscape 4&6, Firefox and Opera + * @... {boolean} mac - true for mac systems + * @... {static} Version - Version of returned structure (1.0) + */ + Browser : function() + { + var info = { Version : "1.0" }; + var is_major = parseInt( navigator.appVersion ); + info.nver = is_major; + info.ver = navigator.appVersion; + info.agent = navigator.userAgent; + info.dom = document.getElementById ? 1 : 0; + info.opera = window.opera ? 1 : 0; + info.ie5 = ( info.ver.indexOf( "MSIE 5" ) > -1 && info.dom && !info.opera ) ? 1 : 0; + info.ie6 = ( info.ver.indexOf( "MSIE 6" ) > -1 && info.dom && !info.opera ) ? 1 : 0; + info.ie4 = ( document.all && !info.dom && !info.opera ) ? 1 : 0; + info.ie = info.ie4 || info.ie5 || info.ie6; + info.mac = info.agent.indexOf( "Mac" ) > -1; + info.ns6 = ( info.dom && parseInt( info.ver ) >= 5 ) ? 1 : 0; + info.ie3 = ( info.ver.indexOf( "MSIE" ) && ( is_major < 4 ) ); + info.hotjava = ( info.agent.toLowerCase().indexOf( 'hotjava' ) != -1 ) ? 1 : 0; + info.ns4 = ( document.layers && !info.dom && !info.hotjava ) ? 1 : 0; + info.bw = ( info.ie6 || info.ie5 || info.ie4 || info.ns4 || info.ns6 || info.opera ); + info.ver3 = ( info.hotjava || info.ie3 ); + info.opera7 = ( ( info.agent.toLowerCase().indexOf( 'opera 7' ) > -1 ) || ( info.agent.toLowerCase().indexOf( 'opera/7' ) > -1 ) ); + info.operaOld = info.opera && !info.opera7; + return info; + }, + + /** + * Import CSS from Url. + * @function ? + * @param doc - document DOM object + * @param css_file - Url to CSS file + */ + ImportCss : function(doc, css_file) + { + if (Prado.Browser().ie) + var styleSheet = doc.createStyleSheet(css_file); + else + { + var elm = doc.createElement("link"); + + elm.rel = "stylesheet"; + elm.href = css_file; + var headArr; + + if (headArr = doc.getElementsByTagName("head")) + headArr[0].appendChild(elm); + } + } +}; diff --git a/framework/Web/Javascripts/source/prado/ratings/ratings.js b/framework/Web/Javascripts/source/prado/ratings/ratings.js index c7322983..1369c740 100644 --- a/framework/Web/Javascripts/source/prado/ratings/ratings.js +++ b/framework/Web/Javascripts/source/prado/ratings/ratings.js @@ -1,206 +1,206 @@ -Prado.WebUI.TRatingList = Base.extend( -{ - selectedIndex : -1, - rating: -1, - readOnly : false, - - constructor : function(options) - { - var cap = $(options.CaptionID); - this.options = Object.extend( - { - caption : cap ? cap.innerHTML : '' - }, options || {}); - - Prado.WebUI.TRatingList.register(this); - this._init(); - Prado.Registry.set(options.ListID, this); - this.selectedIndex = options.SelectedIndex; - this.rating = options.Rating; - this.readOnly = options.ReadOnly - if(options.Rating <= 0 && options.SelectedIndex >= 0) - this.rating = options.SelectedIndex+1; - this.setReadOnly(this.readOnly); - }, - - _init: function(options) - { - Element.addClassName($(this.options.ListID),this.options.Style); - this.radios = new Array(); - this._mouseOvers = new Array(); - this._mouseOuts = new Array(); - this._clicks = new Array(); - var index=0; - for(var i = 0; i halfMax ? base+1 : base; - for(var i = 0; i halfMax ? base+1 : base; - var hasHalf = remainder >= halfMin && remainder <= halfMax; - for(var i = 0; i index ? 'removeClassName' : 'addClassName'; - Element[action](node, "rating_selected"); - if(i==index+1 && hasHalf) - Element.addClassName(node, "rating_half"); - else - Element.removeClassName(node, "rating_half"); - Element.removeClassName(node,"rating_hover"); - } - }, - - getIndexCaption : function(index) - { - return index > -1 ? this.radios[index].value : this.options.caption; - }, - - showCaption : function(value) - { - var caption = $(this.options.CaptionID); - if(caption) caption.innerHTML = value; - $(this.options.ListID).title = value; - }, - - setCaption : function(value) - { - this.options.caption = value; - this.showCaption(value); - }, - - setReadOnly : function(value) - { - this.readOnly = value; - for(var i = 0; i= 0) + this.rating = options.SelectedIndex+1; + this.setReadOnly(this.readOnly); + }, + + _init: function(options) + { + Element.addClassName($(this.options.ListID),this.options.Style); + this.radios = new Array(); + this._mouseOvers = new Array(); + this._mouseOuts = new Array(); + this._clicks = new Array(); + var index=0; + for(var i = 0; i halfMax ? base+1 : base; + for(var i = 0; i halfMax ? base+1 : base; + var hasHalf = remainder >= halfMin && remainder <= halfMax; + for(var i = 0; i index ? 'removeClassName' : 'addClassName'; + Element[action](node, "rating_selected"); + if(i==index+1 && hasHalf) + Element.addClassName(node, "rating_half"); + else + Element.removeClassName(node, "rating_half"); + Element.removeClassName(node,"rating_hover"); + } + }, + + getIndexCaption : function(index) + { + return index > -1 ? this.radios[index].value : this.options.caption; + }, + + showCaption : function(value) + { + var caption = $(this.options.CaptionID); + if(caption) caption.innerHTML = value; + $(this.options.ListID).title = value; + }, + + setCaption : function(value) + { + this.options.caption = value; + this.showCaption(value); + }, + + setReadOnly : function(value) + { + this.readOnly = value; + for(var i = 0; iPrototype's Function - * @namespace Function - */ -/** - * Similar to bindAsEventLister, but takes additional arguments. - * @function Function.bindEvent - */ -Function.prototype.bindEvent = function() -{ - var __method = this, args = $A(arguments), object = args.shift(); - return function(event) - { - return __method.apply(object, [event || window.event].concat(args)); - } -}; - -/** - * Extension to - * Prototype's Class - * @namespace Class - */ - -/** - * Creates a new class by copying class definition from - * the base and optional definition. - * @function {Class} Class.extend - * @param {function} base - Base class to copy from. - * @param {optional Array} - Additional definition - * @returns Constructor for the extended class - */ -Class.extend = function(base, definition) -{ - var component = Class.create(); - Object.extend(component.prototype, base.prototype); - if(definition) - Object.extend(component.prototype, definition); - return component; -}; - -/** - * Base, version 1.0.2 - * Copyright 2006, Dean Edwards - * License: http://creativecommons.org/licenses/LGPL/2.1/ - * @class Base - */ -var Base = function() { - if (arguments.length) { - if (this == window) { // cast an object to this class - Base.prototype.extend.call(arguments[0], arguments.callee.prototype); - } else { - this.extend(arguments[0]); - } - } -}; - -Base.version = "1.0.2"; - -Base.prototype = { - extend: function(source, value) { - var extend = Base.prototype.extend; - if (arguments.length == 2) { - var ancestor = this[source]; - // overriding? - if ((ancestor instanceof Function) && (value instanceof Function) && - ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) { - var method = value; - // var _prototype = this.constructor.prototype; - // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor; - value = function() { - var previous = this.base; - // this.base = fromPrototype ? _prototype[source] : ancestor; - this.base = ancestor; - var returnValue = method.apply(this, arguments); - this.base = previous; - return returnValue; - }; - // point to the underlying method - value.valueOf = function() { - return method; - }; - value.toString = function() { - return String(method); - }; - } - return this[source] = value; - } else if (source) { - var _prototype = {toSource: null}; - // do the "toString" and other methods manually - var _protected = ["toString", "valueOf"]; - // if we are prototyping then include the constructor - if (Base._prototyping) _protected[2] = "constructor"; - var name; - for (var i = 0; (name = _protected[i]); i++) { - if (source[name] != _prototype[name]) { - extend.call(this, name, source[name]); - } - } - // copy each of the source object's properties to this object - for (var name in source) { - if (!_prototype[name]) { - extend.call(this, name, source[name]); - } - } - } - return this; - }, - - base: function() { - // call this method from any other method to invoke that method's ancestor - } -}; - -Base.extend = function(_instance, _static) { - var extend = Base.prototype.extend; - if (!_instance) _instance = {}; - // build the prototype - Base._prototyping = true; - var _prototype = new this; - extend.call(_prototype, _instance); - var constructor = _prototype.constructor; - _prototype.constructor = this; - delete Base._prototyping; - // create the wrapper for the constructor function - var klass = function() { - if (!Base._prototyping) constructor.apply(this, arguments); - this.constructor = klass; - }; - klass.prototype = _prototype; - // build the class interface - klass.extend = this.extend; - klass.implement = this.implement; - klass.toString = function() { - return String(constructor); - }; - extend.call(klass, _static); - // single instance - var object = constructor ? klass : _prototype; - // class initialisation - if (object.init instanceof Function) object.init(); - return object; -}; - -Base.implement = function(_interface) { - if (_interface instanceof Function) _interface = _interface.prototype; - this.prototype.extend(_interface); -}; - -/** - * Performs a PostBack using javascript. - * @function Prado.PostBack - * @param event - Event that triggered this postback - * @param options - Postback options - * @... {string} FormID - Form that should be posted back - * @... {optional boolean} CausesValidation - Validate before PostBack if true - * @... {optional string} ValidationGroup - Group to Validate - * @... {optional string} ID - Validation ID - * @... {optional string} PostBackUrl - Postback URL - * @... {optional boolean} TrackFocus - Keep track of focused element if true - * @... {string} EventTarget - Id of element that triggered PostBack - * @... {string} EventParameter - EventParameter for PostBack - */ -Prado.PostBack = function(event,options) -{ - var form = $(options['FormID']); - var canSubmit = true; - - if(options['CausesValidation'] && typeof(Prado.Validation) != "undefined") - { - if(!Prado.Validation.validate(options['FormID'], options['ValidationGroup'], $(options['ID']))) - return Event.stop(event); - } - - if(options['PostBackUrl'] && options['PostBackUrl'].length > 0) - form.action = options['PostBackUrl']; - - if(options['TrackFocus']) - { - var lastFocus = $('PRADO_LASTFOCUS'); - if(lastFocus) - { - var active = document.activeElement; //where did this come from - if(active) - lastFocus.value = active.id; - else - lastFocus.value = options['EventTarget']; - } - } - - $('PRADO_POSTBACK_TARGET').value = options['EventTarget']; - $('PRADO_POSTBACK_PARAMETER').value = options['EventParameter']; - /** - * Since google toolbar prevents browser default action, - * we will always disable default client-side browser action - */ - /*if(options['StopEvent']) */ - Event.stop(event); - Event.fireEvent(form,"submit"); -}; - -/** - * Prado utilities to manipulate DOM elements. - * @object Prado.Element - */ -Prado.Element = -{ - /** - * Set the value of a particular element. - * @function ? - * @param {string} element - Element id - * @param {string} value - New element value - */ - setValue : function(element, value) - { - var el = $(element); - if(el && typeof(el.value) != "undefined") - el.value = value; - }, - - /** - * Select options from a selectable element. - * @function ? - * @param {string} element - Element id - * @param {string} method - Name of any {@link Prado.Element.Selection} method - * @param {array|boolean|string} value - Values that should be selected - * @param {int} total - Number of elements - */ - select : function(element, method, value, total) - { - var el = $(element); - if(!el) return; - var selection = Prado.Element.Selection; - if(typeof(selection[method]) == "function") - { - var control = selection.isSelectable(el) ? [el] : selection.getListElements(element,total); - selection[method](control, value); - } - }, - - /** - * Trigger a click event on a DOM element. - * @function ? - * @param {string} element - Element id - */ - click : function(element) - { - var el = $(element); - if(el) - el.click(); - }, - - /** - * Check if an DOM element is disabled. - * @function {boolean} ? - * @param {string} element - Element id - * @returns true if element is disabled - */ - isDisabled : function(element) - { - if(!element.attributes['disabled']) //FF - return false; - var value = element.attributes['disabled'].nodeValue; - if(typeof(value)=="string") - return value.toLowerCase() == "disabled"; - else - return value == true; - }, - - /** - * Sets an attribute of a DOM element. - * @function ? - * @param {string} element - Element id - * @param {string} attribute - Name of attribute - * @param {string} value - Value of attribute - */ - setAttribute : function(element, attribute, value) - { - var el = $(element); - if(!el) return; - if((attribute == "disabled" || attribute == "multiple") && value==false) - el.removeAttribute(attribute); - else if(attribute.match(/^on/i)) //event methods - { - try - { - eval("(func = function(event){"+value+"})"); - el[attribute] = func; - } - catch(e) - { - debugger; - throw "Error in evaluating '"+value+"' for attribute "+attribute+" for element "+element.id; - } - } - else - el.setAttribute(attribute, value); - }, - - /** - * Sets the options for a select element. - * @function ? - * @param {string} element - Element id - * @param {array[]} options - Array of options, each an array of structure - * [ "optionText" , "optionValue" , "optionGroup" ] - */ - setOptions : function(element, options) - { - var el = $(element); - if(!el) return; - var previousGroup = null; - var optGroup=null; - if(el && el.tagName.toLowerCase() == "select") - { - while(el.childNodes.length > 0) - el.removeChild(el.lastChild); - - var optDom = Prado.Element.createOptions(options); - for(var i = 0; i < optDom.length; i++) - el.appendChild(optDom[i]); - } - }, - - /** - * Create opt-group options from an array of options. - * @function {array} ? - * @param {array[]} options - Array of options, each an array of structure - * [ "optionText" , "optionValue" , "optionGroup" ] - * @returns Array of option DOM elements - */ - createOptions : function(options) - { - var previousGroup = null; - var optgroup=null; - var result = []; - for(var i = 0; i 2) - { - var group = option[2]; - if(group!=previousGroup) - { - if(previousGroup!=null && optgroup!=null) - { - result.push(optgroup); - previousGroup=null; - optgroup=null; - } - optgroup = document.createElement('optgroup'); - optgroup.label = group; - previousGroup = group; - } - } - var opt = document.createElement('option'); - opt.text = option[0]; - opt.innerText = option[0]; - opt.value = option[1]; - if(optgroup!=null) - optgroup.appendChild(opt); - else - result.push(opt); - } - if(optgroup!=null) - result.push(optgroup); - return result; - }, - - /** - * Set focus (delayed) on a particular element. - * @function ? - * @param {string} element - Element id - */ - focus : function(element) - { - var obj = $(element); - if(typeof(obj) != "undefined" && typeof(obj.focus) != "undefined") - setTimeout(function(){ obj.focus(); }, 100); - return false; - }, - - /** - * Replace a DOM element either with given content or - * with content from a CallBack response boundary - * using a replacement method. - * @function ? - * @param {string|element} element - DOM element or element id - * @param {string} method - Name of method to use for replacement - * @param {optional string} content - New content of element - * @param {optional string} boundary - Boundary of new content - */ - replace : function(element, method, content, boundary) - { - if(boundary) - { - var result = Prado.Element.extractContent(this.transport.responseText, boundary); - if(result != null) - content = result; - } - if(typeof(element) == "string") - { - if($(element)) - method.toFunction().apply(this,[element,""+content]); - } - else - { - method.toFunction().apply(this,[""+content]); - } - }, - - /** - * Appends a javascript block to the document. - * @function ? - * @param {string} boundary - Boundary containing the javascript code - */ - appendScriptBlock : function(boundary) - { - var content = Prado.Element.extractContent(this.transport.responseText, boundary); - if(content == null) - return; - - var el = document.createElement("script"); - el.type = "text/javascript"; - el.id = 'inline_' + boundary; - el.text = content; - - (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(el); - el.parentNode.removeChild(el); - }, - - /** - * Extract content from a text by its boundary id. - * Boundaries have this form: - *
-	 * <!--123456-->Democontent<!--//123456-->
-	 * 
- * @function {string} ? - * @param {string} text - Text that contains boundaries - * @param {string} boundary - Boundary id - * @returns Content from given boundaries - */ - extractContent : function(text, boundary) - { - var tagStart = ''; - var tagEnd = ''; - var start = text.indexOf(tagStart); - if(start > -1) - { - start += tagStart.length; - var end = text.indexOf(tagEnd,start); - if(end > -1) - return text.substring(start,end); - } - return null; - /*var f = RegExp('(?:)((?:.|\n|\r)+?)(?:)',"m"); - var result = text.match(f); - if(result && result.length >= 2) - return result[1]; - else - return null;*/ - }, - - /** - * Evaluate a javascript snippet from a string. - * @function ? - * @param {string} content - String containing the script - */ - evaluateScript : function(content) - { - try - { - content.evalScripts(); - } - catch(e) - { - if(typeof(Logger) != "undefined") - Logger.error('Error during evaluation of script "'+content+'"'); - else - debugger; - throw e; - } - }, - - /** - * Set CSS style with Camelized keys. - * See Prototype's - * Element.setStyle for details. - * @function ? - * @param {string|element} element - DOM element or element id - * @param {object} styles - Object with style properties/values - */ - setStyle : function (element, styles) - { - var s = {} - // Camelize all styles keys - for (var property in styles) - { - s[property.camelize()]=styles[property].camelize(); - } - Element.setStyle(element, s); - } -}; - -/** - * Utilities for selections. - * @object Prado.Element.Selection - */ -Prado.Element.Selection = -{ - /** - * Check if an DOM element can be selected. - * @function {boolean} ? - * @param {element} el - DOM elemet - * @returns true if element is selectable - */ - isSelectable : function(el) - { - if(el && el.type) - { - switch(el.type.toLowerCase()) - { - case 'checkbox': - case 'radio': - case 'select': - case 'select-multiple': - case 'select-one': - return true; - } - } - return false; - }, - - /** - * Set checked attribute of a checkbox or radiobutton to value. - * @function {boolean} ? - * @param {element} el - DOM element - * @param {boolean} value - New value of checked attribute - * @returns New value of checked attribute - */ - inputValue : function(el, value) - { - switch(el.type.toLowerCase()) - { - case 'checkbox': - case 'radio': - return el.checked = value; - } - }, - - /** - * Set selected attribute for elements options by value. - * If value is boolean, all elements options selected attribute will be set - * to value. Otherwhise all options that have the given value will be selected. - * @function ? - * @param {element[]} elements - Array of selectable DOM elements - * @param {boolean|string} value - Value of options that should be selected or boolean value of selection status - */ - selectValue : function(elements, value) - { - elements.each(function(el) - { - $A(el.options).each(function(option) - { - if(typeof(value) == "boolean") - option.selected = value; - else if(option.value == value) - option.selected = true; - }); - }) - }, - - /** - * Set selected attribute for elements options by array of values. - * @function ? - * @param {element[]} elements - Array of selectable DOM elements - * @param {string[]} value - Array of values to select - */ - selectValues : function(elements, values) - { - var selection = this; - values.each(function(value) - { - selection.selectValue(elements,value); - }) - }, - - /** - * Set selected attribute for elements options by option index. - * @function ? - * @param {element[]} elements - Array of selectable DOM elements - * @param {int} index - Index of option to select - */ - selectIndex : function(elements, index) - { - elements.each(function(el) - { - if(el.type.toLowerCase() == 'select-one') - el.selectedIndex = index; - else - { - for(var i = 0; iScriptaculous' Builder - * @namespace Builder - */ - -Object.extend(Builder, -{ - /** - * Export scriptaculous builder utilities as window[functions] - * @function ? - */ - exportTags:function() - { - var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"]; - tags.each(function(tag) - { - window[tag]=function() - { - var args=$A(arguments); - if(args.length==0) - return Builder.node(tag,null); - if(args.length==1) - return Builder.node(tag,args[0]); - if(args.length>1) - return Builder.node(tag,args.shift(),args); - - }; - }); - } -}); -Builder.exportTags(); - -/** - * Extension to - * Prototype's String - * @namespace String - */ -Object.extend(String.prototype, { - - /** - * Add padding to string - * @function {string} ? - * @param {string} side - "left" to pad the string on the left, "right" to pad right. - * @param {int} len - Minimum string length. - * @param {string} chr - Character(s) to pad - * @returns Padded string - */ - pad : function(side, len, chr) { - if (!chr) chr = ' '; - var s = this; - var left = side.toLowerCase()=='left'; - while (s.lengthExample: - *
 
-	 * "Prado.AJAX.Callback.Action.setValue".toFunction()
-	 * 
- * @function {function} ? - * @returns Reference to the corresponding function - */ - toFunction : function() - { - var commands = this.split(/\./); - var command = window; - commands.each(function(action) - { - if(command[new String(action)]) - command=command[new String(action)]; - }); - if(typeof(command) == "function") - return command; - else - { - if(typeof Logger != "undefined") - Logger.error("Missing function", this); - - throw new Error ("Missing function '"+this+"'"); - } - }, - - /** - * Convert string into integer, returns null if not integer. - * @function {int} ? - * @returns Integer, null if string does not represent an integer. - */ - toInteger : function() - { - var exp = /^\s*[-\+]?\d+\s*$/; - if (this.match(exp) == null) - return null; - var num = parseInt(this, 10); - return (isNaN(num) ? null : num); - }, - - /** - * Convert string into a double/float value. Internationalization - * is not supported - * @function {double} ? - * @param {string} decimalchar - Decimal character, defaults to "." - * @returns Double, null if string does not represent a float value - */ - toDouble : function(decimalchar) - { - if(this.length <= 0) return null; - decimalchar = decimalchar || "."; - var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$"); - var m = this.match(exp); - - if (m == null) - return null; - m[1] = m[1] || ""; - m[2] = m[2] || "0"; - m[4] = m[4] || "0"; - - var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4]; - var num = parseFloat(cleanInput); - return (isNaN(num) ? null : num); - }, - - /** - * Convert strings that represent a currency value to float. - * E.g. "10,000.50" will become "10000.50". The number - * of dicimal digits, grouping and decimal characters can be specified. - * The currency input format is very strict, null will be returned if - * the pattern does not match. - * @function {double} ? - * @param {string} groupchar - Grouping character, defaults to "," - * @param {int} digits - Number of decimal digits - * @param {string} decimalchar - Decimal character, defaults to "." - * @returns Double, null if string does not represent a currency value - */ - toCurrency : function(groupchar, digits, decimalchar) - { - groupchar = groupchar || ","; - decimalchar = decimalchar || "."; - digits = typeof(digits) == "undefined" ? 2 : digits; - - var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)" - + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "") - + "\\s*$"); - var m = this.match(exp); - if (m == null) - return null; - var intermed = m[2] + m[5] ; - var cleanInput = m[1] + intermed.replace( - new RegExp("(\\" + groupchar + ")", "g"), "") - + ((digits > 0) ? "." + m[7] : ""); - var num = parseFloat(cleanInput); - return (isNaN(num) ? null : num); - }, - - /** - * Converts the string to a date by finding values that matches the - * date format pattern. - * @function {Date} ? - * @param {string} format - Date format pattern, e.g. MM-dd-yyyy - * @returns Date extracted from the string - */ - toDate : function(format) - { - return Date.SimpleParse(this, format); - } -}); - -/** - * Extension to - * Prototype's Event - * @namespace Event - */ -Object.extend(Event, -{ - /** - * Register a function to be executed when the page is loaded. - * Note that the page is only loaded if all resources (e.g. images) - * are loaded. - *
Example: - *
Show an alert box with message "Page Loaded!" when the - * page finished loading. - *
-	 * Event.OnLoad(function(){ alert("Page Loaded!"); });
-	 * 
- * @function ? - * @param {function} fn - Function to execute when page is loaded. - */ - OnLoad : function (fn) - { - // opera onload is in document, not window - var w = document.addEventListener && - !window.addEventListener ? document : window; - Event.observe(w,'load',fn); - }, - - /** - * Returns the unicode character generated by key. - * @param {event} e - Keyboard event - * @function {int} ? - * @returns Unicode character code generated by the key that was struck. - */ - keyCode : function(e) - { - return e.keyCode != null ? e.keyCode : e.charCode - }, - - /** - * Checks if an Event is of type HTMLEvent. - * @function {boolean} ? - * @param {string} type - Event type or event name. - * @return true if event is of type HTMLEvent. - */ - isHTMLEvent : function(type) - { - var events = ['abort', 'blur', 'change', 'error', 'focus', - 'load', 'reset', 'resize', 'scroll', 'select', - 'submit', 'unload']; - return events.include(type); - }, - - /** - * Checks if an Event is a mouse event. - * @function {boolean} ? - * @param {string} type - Event type or event name - * @return true if event is of type MouseEvent. - */ - isMouseEvent : function(type) - { - var events = ['click', 'mousedown', 'mousemove', 'mouseout', - 'mouseover', 'mouseup']; - return events.include(type); - }, - - /** - * Dispatch the DOM event of a given type on a DOM - * element. Only HTMLEvent and MouseEvent can be - * dispatched, keyboard events or UIEvent can not be dispatch - * via javascript consistently. - * For the "submit" event the submit() method is called. - * @function ? - * @param {element|string} element - Element id string or DOM element. - * @param {string} type - Event type to dispatch. - */ - fireEvent : function(element,type) - { - element = $(element); - if(type == "submit") - return element.submit(); - if(document.createEvent) - { - if(Event.isHTMLEvent(type)) - { - var event = document.createEvent('HTMLEvents'); - event.initEvent(type, true, true); - } - else if(Event.isMouseEvent(type)) - { - var event = document.createEvent('MouseEvents'); - if (event.initMouseEvent) - { - event.initMouseEvent(type,true,true, - document.defaultView, 1, 0, 0, 0, 0, false, - false, false, false, 0, null); - } - else - { - // Safari - // TODO we should be initialising other mouse-event related attributes here - event.initEvent(type, true, true); - } - } - element.dispatchEvent(event); - } - else if(document.createEventObject) - { - var evObj = document.createEventObject(); - element.fireEvent('on'+type, evObj); - } - else if(typeof(element['on'+type]) == "function") - element['on'+type](); - } -}); - - -/** - * Extension to - * Prototype's Date - * @namespace Date - */ -Object.extend(Date.prototype, -{ - /** - * SimpleFormat - * @function ? - * @param {string} format - TODO - * @param {string} data - TODO - * @returns TODO - */ - SimpleFormat: function(format, data) - { - data = data || {}; - var bits = new Array(); - bits['d'] = this.getDate(); - bits['dd'] = String(this.getDate()).zerofill(2); - - bits['M'] = this.getMonth()+1; - bits['MM'] = String(this.getMonth()+1).zerofill(2); - if(data.AbbreviatedMonthNames) - bits['MMM'] = data.AbbreviatedMonthNames[this.getMonth()]; - if(data.MonthNames) - bits['MMMM'] = data.MonthNames[this.getMonth()]; - var yearStr = "" + this.getFullYear(); - yearStr = (yearStr.length == 2) ? '19' + yearStr: yearStr; - bits['yyyy'] = yearStr; - bits['yy'] = bits['yyyy'].toString().substr(2,2); - - // do some funky regexs to replace the format string - // with the real values - var frm = new String(format); - for (var sect in bits) - { - var reg = new RegExp("\\b"+sect+"\\b" ,"g"); - frm = frm.replace(reg, bits[sect]); - } - return frm; - }, - - /** - * toISODate - * @function {string} ? - * @returns TODO - */ - toISODate : function() - { - var y = this.getFullYear(); - var m = String(this.getMonth() + 1).zerofill(2); - var d = String(this.getDate()).zerofill(2); - return String(y) + String(m) + String(d); - } -}); - -Object.extend(Date, -{ - /** - * SimpleParse - * @function ? - * @param {string} format - TODO - * @param {string} data - TODO - * @returns TODO - */ - SimpleParse: function(value, format) - { - var val=String(value); - format=String(format); - - if(val.length <= 0) return null; - - if(format.length <= 0) return new Date(value); - - var isInteger = function (val) - { - var digits="1234567890"; - for (var i=0; i < val.length; i++) - { - if (digits.indexOf(val.charAt(i))==-1) { return false; } - } - return true; - }; - - var getInt = function(str,i,minlength,maxlength) - { - for (var x=maxlength; x>=minlength; x--) - { - var token=str.substring(i,i+x); - if (token.length < minlength) { return null; } - if (isInteger(token)) { return token; } - } - return null; - }; - - var i_val=0; - var i_format=0; - var c=""; - var token=""; - var token2=""; - var x,y; - var now=new Date(); - var year=now.getFullYear(); - var month=now.getMonth()+1; - var date=1; - - while (i_format < format.length) - { - // Get next token from format string - c=format.charAt(i_format); - token=""; - while ((format.charAt(i_format)==c) && (i_format < format.length)) - { - token += format.charAt(i_format++); - } - - // Extract contents of value based on format token - if (token=="yyyy" || token=="yy" || token=="y") - { - if (token=="yyyy") { x=4;y=4; } - if (token=="yy") { x=2;y=2; } - if (token=="y") { x=2;y=4; } - year=getInt(val,i_val,x,y); - if (year==null) { return null; } - i_val += year.length; - if (year.length==2) - { - if (year > 70) { year=1900+(year-0); } - else { year=2000+(year-0); } - } - } - - else if (token=="MM"||token=="M") - { - month=getInt(val,i_val,token.length,2); - if(month==null||(month<1)||(month>12)){return null;} - i_val+=month.length; - } - else if (token=="dd"||token=="d") - { - date=getInt(val,i_val,token.length,2); - if(date==null||(date<1)||(date>31)){return null;} - i_val+=date.length; - } - else - { - if (val.substring(i_val,i_val+token.length)!=token) {return null;} - else {i_val+=token.length;} - } - } - - // If there are any trailing characters left in the value, it doesn't match - if (i_val != val.length) { return null; } - - // Is date valid for month? - if (month==2) - { - // Check for leap year - if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year - if (date > 29){ return null; } - } - else { if (date > 28) { return null; } } - } - - if ((month==4)||(month==6)||(month==9)||(month==11)) - { - if (date > 30) { return null; } - } - - var newdate=new Date(year,month-1,date, 0, 0, 0); - return newdate; - } -}); - -/** - * Prado utilities for effects. - * @object Prado.Effect - */ -Prado.Effect = -{ - /** - * Highlights an element - * @function ? - * @param {element} element - DOM element to highlight - * @param {optional object} options - Highlight options - */ - Highlight : function (element,options) - { - new Effect.Highlight(element,options); - } -}; +/** + * Utilities and extions to Prototype/Scriptaculous + * @file scriptaculous-adapter.js + */ + +/** + * Extension to + * Prototype's Function + * @namespace Function + */ +/** + * Similar to bindAsEventLister, but takes additional arguments. + * @function Function.bindEvent + */ +Function.prototype.bindEvent = function() +{ + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) + { + return __method.apply(object, [event || window.event].concat(args)); + } +}; + +/** + * Extension to + * Prototype's Class + * @namespace Class + */ + +/** + * Creates a new class by copying class definition from + * the base and optional definition. + * @function {Class} Class.extend + * @param {function} base - Base class to copy from. + * @param {optional Array} - Additional definition + * @returns Constructor for the extended class + */ +Class.extend = function(base, definition) +{ + var component = Class.create(); + Object.extend(component.prototype, base.prototype); + if(definition) + Object.extend(component.prototype, definition); + return component; +}; + +/** + * Base, version 1.0.2 + * Copyright 2006, Dean Edwards + * License: http://creativecommons.org/licenses/LGPL/2.1/ + * @class Base + */ +var Base = function() { + if (arguments.length) { + if (this == window) { // cast an object to this class + Base.prototype.extend.call(arguments[0], arguments.callee.prototype); + } else { + this.extend(arguments[0]); + } + } +}; + +Base.version = "1.0.2"; + +Base.prototype = { + extend: function(source, value) { + var extend = Base.prototype.extend; + if (arguments.length == 2) { + var ancestor = this[source]; + // overriding? + if ((ancestor instanceof Function) && (value instanceof Function) && + ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) { + var method = value; + // var _prototype = this.constructor.prototype; + // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor; + value = function() { + var previous = this.base; + // this.base = fromPrototype ? _prototype[source] : ancestor; + this.base = ancestor; + var returnValue = method.apply(this, arguments); + this.base = previous; + return returnValue; + }; + // point to the underlying method + value.valueOf = function() { + return method; + }; + value.toString = function() { + return String(method); + }; + } + return this[source] = value; + } else if (source) { + var _prototype = {toSource: null}; + // do the "toString" and other methods manually + var _protected = ["toString", "valueOf"]; + // if we are prototyping then include the constructor + if (Base._prototyping) _protected[2] = "constructor"; + var name; + for (var i = 0; (name = _protected[i]); i++) { + if (source[name] != _prototype[name]) { + extend.call(this, name, source[name]); + } + } + // copy each of the source object's properties to this object + for (var name in source) { + if (!_prototype[name]) { + extend.call(this, name, source[name]); + } + } + } + return this; + }, + + base: function() { + // call this method from any other method to invoke that method's ancestor + } +}; + +Base.extend = function(_instance, _static) { + var extend = Base.prototype.extend; + if (!_instance) _instance = {}; + // build the prototype + Base._prototyping = true; + var _prototype = new this; + extend.call(_prototype, _instance); + var constructor = _prototype.constructor; + _prototype.constructor = this; + delete Base._prototyping; + // create the wrapper for the constructor function + var klass = function() { + if (!Base._prototyping) constructor.apply(this, arguments); + this.constructor = klass; + }; + klass.prototype = _prototype; + // build the class interface + klass.extend = this.extend; + klass.implement = this.implement; + klass.toString = function() { + return String(constructor); + }; + extend.call(klass, _static); + // single instance + var object = constructor ? klass : _prototype; + // class initialisation + if (object.init instanceof Function) object.init(); + return object; +}; + +Base.implement = function(_interface) { + if (_interface instanceof Function) _interface = _interface.prototype; + this.prototype.extend(_interface); +}; + +/** + * Performs a PostBack using javascript. + * @function Prado.PostBack + * @param event - Event that triggered this postback + * @param options - Postback options + * @... {string} FormID - Form that should be posted back + * @... {optional boolean} CausesValidation - Validate before PostBack if true + * @... {optional string} ValidationGroup - Group to Validate + * @... {optional string} ID - Validation ID + * @... {optional string} PostBackUrl - Postback URL + * @... {optional boolean} TrackFocus - Keep track of focused element if true + * @... {string} EventTarget - Id of element that triggered PostBack + * @... {string} EventParameter - EventParameter for PostBack + */ +Prado.PostBack = function(event,options) +{ + var form = $(options['FormID']); + var canSubmit = true; + + if(options['CausesValidation'] && typeof(Prado.Validation) != "undefined") + { + if(!Prado.Validation.validate(options['FormID'], options['ValidationGroup'], $(options['ID']))) + return Event.stop(event); + } + + if(options['PostBackUrl'] && options['PostBackUrl'].length > 0) + form.action = options['PostBackUrl']; + + if(options['TrackFocus']) + { + var lastFocus = $('PRADO_LASTFOCUS'); + if(lastFocus) + { + var active = document.activeElement; //where did this come from + if(active) + lastFocus.value = active.id; + else + lastFocus.value = options['EventTarget']; + } + } + + $('PRADO_POSTBACK_TARGET').value = options['EventTarget']; + $('PRADO_POSTBACK_PARAMETER').value = options['EventParameter']; + /** + * Since google toolbar prevents browser default action, + * we will always disable default client-side browser action + */ + /*if(options['StopEvent']) */ + Event.stop(event); + Event.fireEvent(form,"submit"); +}; + +/** + * Prado utilities to manipulate DOM elements. + * @object Prado.Element + */ +Prado.Element = +{ + /** + * Set the value of a particular element. + * @function ? + * @param {string} element - Element id + * @param {string} value - New element value + */ + setValue : function(element, value) + { + var el = $(element); + if(el && typeof(el.value) != "undefined") + el.value = value; + }, + + /** + * Select options from a selectable element. + * @function ? + * @param {string} element - Element id + * @param {string} method - Name of any {@link Prado.Element.Selection} method + * @param {array|boolean|string} value - Values that should be selected + * @param {int} total - Number of elements + */ + select : function(element, method, value, total) + { + var el = $(element); + if(!el) return; + var selection = Prado.Element.Selection; + if(typeof(selection[method]) == "function") + { + var control = selection.isSelectable(el) ? [el] : selection.getListElements(element,total); + selection[method](control, value); + } + }, + + /** + * Trigger a click event on a DOM element. + * @function ? + * @param {string} element - Element id + */ + click : function(element) + { + var el = $(element); + if(el) + el.click(); + }, + + /** + * Check if an DOM element is disabled. + * @function {boolean} ? + * @param {string} element - Element id + * @returns true if element is disabled + */ + isDisabled : function(element) + { + if(!element.attributes['disabled']) //FF + return false; + var value = element.attributes['disabled'].nodeValue; + if(typeof(value)=="string") + return value.toLowerCase() == "disabled"; + else + return value == true; + }, + + /** + * Sets an attribute of a DOM element. + * @function ? + * @param {string} element - Element id + * @param {string} attribute - Name of attribute + * @param {string} value - Value of attribute + */ + setAttribute : function(element, attribute, value) + { + var el = $(element); + if(!el) return; + if((attribute == "disabled" || attribute == "multiple") && value==false) + el.removeAttribute(attribute); + else if(attribute.match(/^on/i)) //event methods + { + try + { + eval("(func = function(event){"+value+"})"); + el[attribute] = func; + } + catch(e) + { + debugger; + throw "Error in evaluating '"+value+"' for attribute "+attribute+" for element "+element.id; + } + } + else + el.setAttribute(attribute, value); + }, + + /** + * Sets the options for a select element. + * @function ? + * @param {string} element - Element id + * @param {array[]} options - Array of options, each an array of structure + * [ "optionText" , "optionValue" , "optionGroup" ] + */ + setOptions : function(element, options) + { + var el = $(element); + if(!el) return; + var previousGroup = null; + var optGroup=null; + if(el && el.tagName.toLowerCase() == "select") + { + while(el.childNodes.length > 0) + el.removeChild(el.lastChild); + + var optDom = Prado.Element.createOptions(options); + for(var i = 0; i < optDom.length; i++) + el.appendChild(optDom[i]); + } + }, + + /** + * Create opt-group options from an array of options. + * @function {array} ? + * @param {array[]} options - Array of options, each an array of structure + * [ "optionText" , "optionValue" , "optionGroup" ] + * @returns Array of option DOM elements + */ + createOptions : function(options) + { + var previousGroup = null; + var optgroup=null; + var result = []; + for(var i = 0; i 2) + { + var group = option[2]; + if(group!=previousGroup) + { + if(previousGroup!=null && optgroup!=null) + { + result.push(optgroup); + previousGroup=null; + optgroup=null; + } + optgroup = document.createElement('optgroup'); + optgroup.label = group; + previousGroup = group; + } + } + var opt = document.createElement('option'); + opt.text = option[0]; + opt.innerText = option[0]; + opt.value = option[1]; + if(optgroup!=null) + optgroup.appendChild(opt); + else + result.push(opt); + } + if(optgroup!=null) + result.push(optgroup); + return result; + }, + + /** + * Set focus (delayed) on a particular element. + * @function ? + * @param {string} element - Element id + */ + focus : function(element) + { + var obj = $(element); + if(typeof(obj) != "undefined" && typeof(obj.focus) != "undefined") + setTimeout(function(){ obj.focus(); }, 100); + return false; + }, + + /** + * Replace a DOM element either with given content or + * with content from a CallBack response boundary + * using a replacement method. + * @function ? + * @param {string|element} element - DOM element or element id + * @param {string} method - Name of method to use for replacement + * @param {optional string} content - New content of element + * @param {optional string} boundary - Boundary of new content + */ + replace : function(element, method, content, boundary) + { + if(boundary) + { + var result = Prado.Element.extractContent(this.transport.responseText, boundary); + if(result != null) + content = result; + } + if(typeof(element) == "string") + { + if($(element)) + method.toFunction().apply(this,[element,""+content]); + } + else + { + method.toFunction().apply(this,[""+content]); + } + }, + + /** + * Appends a javascript block to the document. + * @function ? + * @param {string} boundary - Boundary containing the javascript code + */ + appendScriptBlock : function(boundary) + { + var content = Prado.Element.extractContent(this.transport.responseText, boundary); + if(content == null) + return; + + var el = document.createElement("script"); + el.type = "text/javascript"; + el.id = 'inline_' + boundary; + el.text = content; + + (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(el); + el.parentNode.removeChild(el); + }, + + /** + * Extract content from a text by its boundary id. + * Boundaries have this form: + *
+	 * <!--123456-->Democontent<!--//123456-->
+	 * 
+ * @function {string} ? + * @param {string} text - Text that contains boundaries + * @param {string} boundary - Boundary id + * @returns Content from given boundaries + */ + extractContent : function(text, boundary) + { + var tagStart = ''; + var tagEnd = ''; + var start = text.indexOf(tagStart); + if(start > -1) + { + start += tagStart.length; + var end = text.indexOf(tagEnd,start); + if(end > -1) + return text.substring(start,end); + } + return null; + /*var f = RegExp('(?:)((?:.|\n|\r)+?)(?:)',"m"); + var result = text.match(f); + if(result && result.length >= 2) + return result[1]; + else + return null;*/ + }, + + /** + * Evaluate a javascript snippet from a string. + * @function ? + * @param {string} content - String containing the script + */ + evaluateScript : function(content) + { + try + { + content.evalScripts(); + } + catch(e) + { + if(typeof(Logger) != "undefined") + Logger.error('Error during evaluation of script "'+content+'"'); + else + debugger; + throw e; + } + }, + + /** + * Set CSS style with Camelized keys. + * See Prototype's + * Element.setStyle for details. + * @function ? + * @param {string|element} element - DOM element or element id + * @param {object} styles - Object with style properties/values + */ + setStyle : function (element, styles) + { + var s = {} + // Camelize all styles keys + for (var property in styles) + { + s[property.camelize()]=styles[property].camelize(); + } + Element.setStyle(element, s); + } +}; + +/** + * Utilities for selections. + * @object Prado.Element.Selection + */ +Prado.Element.Selection = +{ + /** + * Check if an DOM element can be selected. + * @function {boolean} ? + * @param {element} el - DOM elemet + * @returns true if element is selectable + */ + isSelectable : function(el) + { + if(el && el.type) + { + switch(el.type.toLowerCase()) + { + case 'checkbox': + case 'radio': + case 'select': + case 'select-multiple': + case 'select-one': + return true; + } + } + return false; + }, + + /** + * Set checked attribute of a checkbox or radiobutton to value. + * @function {boolean} ? + * @param {element} el - DOM element + * @param {boolean} value - New value of checked attribute + * @returns New value of checked attribute + */ + inputValue : function(el, value) + { + switch(el.type.toLowerCase()) + { + case 'checkbox': + case 'radio': + return el.checked = value; + } + }, + + /** + * Set selected attribute for elements options by value. + * If value is boolean, all elements options selected attribute will be set + * to value. Otherwhise all options that have the given value will be selected. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {boolean|string} value - Value of options that should be selected or boolean value of selection status + */ + selectValue : function(elements, value) + { + elements.each(function(el) + { + $A(el.options).each(function(option) + { + if(typeof(value) == "boolean") + option.selected = value; + else if(option.value == value) + option.selected = true; + }); + }) + }, + + /** + * Set selected attribute for elements options by array of values. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {string[]} value - Array of values to select + */ + selectValues : function(elements, values) + { + var selection = this; + values.each(function(value) + { + selection.selectValue(elements,value); + }) + }, + + /** + * Set selected attribute for elements options by option index. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {int} index - Index of option to select + */ + selectIndex : function(elements, index) + { + elements.each(function(el) + { + if(el.type.toLowerCase() == 'select-one') + el.selectedIndex = index; + else + { + for(var i = 0; iScriptaculous' Builder + * @namespace Builder + */ + +Object.extend(Builder, +{ + /** + * Export scriptaculous builder utilities as window[functions] + * @function ? + */ + exportTags:function() + { + var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"]; + tags.each(function(tag) + { + window[tag]=function() + { + var args=$A(arguments); + if(args.length==0) + return Builder.node(tag,null); + if(args.length==1) + return Builder.node(tag,args[0]); + if(args.length>1) + return Builder.node(tag,args.shift(),args); + + }; + }); + } +}); +Builder.exportTags(); + +/** + * Extension to + * Prototype's String + * @namespace String + */ +Object.extend(String.prototype, { + + /** + * Add padding to string + * @function {string} ? + * @param {string} side - "left" to pad the string on the left, "right" to pad right. + * @param {int} len - Minimum string length. + * @param {string} chr - Character(s) to pad + * @returns Padded string + */ + pad : function(side, len, chr) { + if (!chr) chr = ' '; + var s = this; + var left = side.toLowerCase()=='left'; + while (s.lengthExample: + *
 
+	 * "Prado.AJAX.Callback.Action.setValue".toFunction()
+	 * 
+ * @function {function} ? + * @returns Reference to the corresponding function + */ + toFunction : function() + { + var commands = this.split(/\./); + var command = window; + commands.each(function(action) + { + if(command[new String(action)]) + command=command[new String(action)]; + }); + if(typeof(command) == "function") + return command; + else + { + if(typeof Logger != "undefined") + Logger.error("Missing function", this); + + throw new Error ("Missing function '"+this+"'"); + } + }, + + /** + * Convert string into integer, returns null if not integer. + * @function {int} ? + * @returns Integer, null if string does not represent an integer. + */ + toInteger : function() + { + var exp = /^\s*[-\+]?\d+\s*$/; + if (this.match(exp) == null) + return null; + var num = parseInt(this, 10); + return (isNaN(num) ? null : num); + }, + + /** + * Convert string into a double/float value. Internationalization + * is not supported + * @function {double} ? + * @param {string} decimalchar - Decimal character, defaults to "." + * @returns Double, null if string does not represent a float value + */ + toDouble : function(decimalchar) + { + if(this.length <= 0) return null; + decimalchar = decimalchar || "."; + var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$"); + var m = this.match(exp); + + if (m == null) + return null; + m[1] = m[1] || ""; + m[2] = m[2] || "0"; + m[4] = m[4] || "0"; + + var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4]; + var num = parseFloat(cleanInput); + return (isNaN(num) ? null : num); + }, + + /** + * Convert strings that represent a currency value to float. + * E.g. "10,000.50" will become "10000.50". The number + * of dicimal digits, grouping and decimal characters can be specified. + * The currency input format is very strict, null will be returned if + * the pattern does not match. + * @function {double} ? + * @param {string} groupchar - Grouping character, defaults to "," + * @param {int} digits - Number of decimal digits + * @param {string} decimalchar - Decimal character, defaults to "." + * @returns Double, null if string does not represent a currency value + */ + toCurrency : function(groupchar, digits, decimalchar) + { + groupchar = groupchar || ","; + decimalchar = decimalchar || "."; + digits = typeof(digits) == "undefined" ? 2 : digits; + + var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)" + + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "") + + "\\s*$"); + var m = this.match(exp); + if (m == null) + return null; + var intermed = m[2] + m[5] ; + var cleanInput = m[1] + intermed.replace( + new RegExp("(\\" + groupchar + ")", "g"), "") + + ((digits > 0) ? "." + m[7] : ""); + var num = parseFloat(cleanInput); + return (isNaN(num) ? null : num); + }, + + /** + * Converts the string to a date by finding values that matches the + * date format pattern. + * @function {Date} ? + * @param {string} format - Date format pattern, e.g. MM-dd-yyyy + * @returns Date extracted from the string + */ + toDate : function(format) + { + return Date.SimpleParse(this, format); + } +}); + +/** + * Extension to + * Prototype's Event + * @namespace Event + */ +Object.extend(Event, +{ + /** + * Register a function to be executed when the page is loaded. + * Note that the page is only loaded if all resources (e.g. images) + * are loaded. + *
Example: + *
Show an alert box with message "Page Loaded!" when the + * page finished loading. + *
+	 * Event.OnLoad(function(){ alert("Page Loaded!"); });
+	 * 
+ * @function ? + * @param {function} fn - Function to execute when page is loaded. + */ + OnLoad : function (fn) + { + // opera onload is in document, not window + var w = document.addEventListener && + !window.addEventListener ? document : window; + Event.observe(w,'load',fn); + }, + + /** + * Returns the unicode character generated by key. + * @param {event} e - Keyboard event + * @function {int} ? + * @returns Unicode character code generated by the key that was struck. + */ + keyCode : function(e) + { + return e.keyCode != null ? e.keyCode : e.charCode + }, + + /** + * Checks if an Event is of type HTMLEvent. + * @function {boolean} ? + * @param {string} type - Event type or event name. + * @return true if event is of type HTMLEvent. + */ + isHTMLEvent : function(type) + { + var events = ['abort', 'blur', 'change', 'error', 'focus', + 'load', 'reset', 'resize', 'scroll', 'select', + 'submit', 'unload']; + return events.include(type); + }, + + /** + * Checks if an Event is a mouse event. + * @function {boolean} ? + * @param {string} type - Event type or event name + * @return true if event is of type MouseEvent. + */ + isMouseEvent : function(type) + { + var events = ['click', 'mousedown', 'mousemove', 'mouseout', + 'mouseover', 'mouseup']; + return events.include(type); + }, + + /** + * Dispatch the DOM event of a given type on a DOM + * element. Only HTMLEvent and MouseEvent can be + * dispatched, keyboard events or UIEvent can not be dispatch + * via javascript consistently. + * For the "submit" event the submit() method is called. + * @function ? + * @param {element|string} element - Element id string or DOM element. + * @param {string} type - Event type to dispatch. + */ + fireEvent : function(element,type) + { + element = $(element); + if(type == "submit") + return element.submit(); + if(document.createEvent) + { + if(Event.isHTMLEvent(type)) + { + var event = document.createEvent('HTMLEvents'); + event.initEvent(type, true, true); + } + else if(Event.isMouseEvent(type)) + { + var event = document.createEvent('MouseEvents'); + if (event.initMouseEvent) + { + event.initMouseEvent(type,true,true, + document.defaultView, 1, 0, 0, 0, 0, false, + false, false, false, 0, null); + } + else + { + // Safari + // TODO we should be initialising other mouse-event related attributes here + event.initEvent(type, true, true); + } + } + element.dispatchEvent(event); + } + else if(document.createEventObject) + { + var evObj = document.createEventObject(); + element.fireEvent('on'+type, evObj); + } + else if(typeof(element['on'+type]) == "function") + element['on'+type](); + } +}); + + +/** + * Extension to + * Prototype's Date + * @namespace Date + */ +Object.extend(Date.prototype, +{ + /** + * SimpleFormat + * @function ? + * @param {string} format - TODO + * @param {string} data - TODO + * @returns TODO + */ + SimpleFormat: function(format, data) + { + data = data || {}; + var bits = new Array(); + bits['d'] = this.getDate(); + bits['dd'] = String(this.getDate()).zerofill(2); + + bits['M'] = this.getMonth()+1; + bits['MM'] = String(this.getMonth()+1).zerofill(2); + if(data.AbbreviatedMonthNames) + bits['MMM'] = data.AbbreviatedMonthNames[this.getMonth()]; + if(data.MonthNames) + bits['MMMM'] = data.MonthNames[this.getMonth()]; + var yearStr = "" + this.getFullYear(); + yearStr = (yearStr.length == 2) ? '19' + yearStr: yearStr; + bits['yyyy'] = yearStr; + bits['yy'] = bits['yyyy'].toString().substr(2,2); + + // do some funky regexs to replace the format string + // with the real values + var frm = new String(format); + for (var sect in bits) + { + var reg = new RegExp("\\b"+sect+"\\b" ,"g"); + frm = frm.replace(reg, bits[sect]); + } + return frm; + }, + + /** + * toISODate + * @function {string} ? + * @returns TODO + */ + toISODate : function() + { + var y = this.getFullYear(); + var m = String(this.getMonth() + 1).zerofill(2); + var d = String(this.getDate()).zerofill(2); + return String(y) + String(m) + String(d); + } +}); + +Object.extend(Date, +{ + /** + * SimpleParse + * @function ? + * @param {string} format - TODO + * @param {string} data - TODO + * @returns TODO + */ + SimpleParse: function(value, format) + { + var val=String(value); + format=String(format); + + if(val.length <= 0) return null; + + if(format.length <= 0) return new Date(value); + + var isInteger = function (val) + { + var digits="1234567890"; + for (var i=0; i < val.length; i++) + { + if (digits.indexOf(val.charAt(i))==-1) { return false; } + } + return true; + }; + + var getInt = function(str,i,minlength,maxlength) + { + for (var x=maxlength; x>=minlength; x--) + { + var token=str.substring(i,i+x); + if (token.length < minlength) { return null; } + if (isInteger(token)) { return token; } + } + return null; + }; + + var i_val=0; + var i_format=0; + var c=""; + var token=""; + var token2=""; + var x,y; + var now=new Date(); + var year=now.getFullYear(); + var month=now.getMonth()+1; + var date=1; + + while (i_format < format.length) + { + // Get next token from format string + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) + { + token += format.charAt(i_format++); + } + + // Extract contents of value based on format token + if (token=="yyyy" || token=="yy" || token=="y") + { + if (token=="yyyy") { x=4;y=4; } + if (token=="yy") { x=2;y=2; } + if (token=="y") { x=2;y=4; } + year=getInt(val,i_val,x,y); + if (year==null) { return null; } + i_val += year.length; + if (year.length==2) + { + if (year > 70) { year=1900+(year-0); } + else { year=2000+(year-0); } + } + } + + else if (token=="MM"||token=="M") + { + month=getInt(val,i_val,token.length,2); + if(month==null||(month<1)||(month>12)){return null;} + i_val+=month.length; + } + else if (token=="dd"||token=="d") + { + date=getInt(val,i_val,token.length,2); + if(date==null||(date<1)||(date>31)){return null;} + i_val+=date.length; + } + else + { + if (val.substring(i_val,i_val+token.length)!=token) {return null;} + else {i_val+=token.length;} + } + } + + // If there are any trailing characters left in the value, it doesn't match + if (i_val != val.length) { return null; } + + // Is date valid for month? + if (month==2) + { + // Check for leap year + if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year + if (date > 29){ return null; } + } + else { if (date > 28) { return null; } } + } + + if ((month==4)||(month==6)||(month==9)||(month==11)) + { + if (date > 30) { return null; } + } + + var newdate=new Date(year,month-1,date, 0, 0, 0); + return newdate; + } +}); + +/** + * Prado utilities for effects. + * @object Prado.Effect + */ +Prado.Effect = +{ + /** + * Highlights an element + * @function ? + * @param {element} element - DOM element to highlight + * @param {optional object} options - Highlight options + */ + Highlight : function (element,options) + { + new Effect.Highlight(element,options); + } +}; diff --git a/framework/Web/Javascripts/source/prado/validator/validation3.js b/framework/Web/Javascripts/source/prado/validator/validation3.js index 597b4b5b..1fd1b570 100644 --- a/framework/Web/Javascripts/source/prado/validator/validation3.js +++ b/framework/Web/Javascripts/source/prado/validator/validation3.js @@ -1,1943 +1,1943 @@ -/** - * Prado client-side javascript validation fascade. - * - *

There are 4 basic classes: {@link Prado.Validation}, - * {@link Prado.ValidationManager}, {@link Prado.WebUI.TValidationSummary} - * and {@link Prado.WebUI.TBaseValidator}, - * that interact together to perform validation. - * The {@link Prado.Validation} class co-ordinates together the - * validation scheme and is responsible for maintaining references - * to ValidationManagers.

- * - *

The {@link Prado.ValidationManager} class is responsible for - * maintaining refereneces - * to individual validators, validation summaries and their associated - * groupings.

- * - *

The {@link Prado.WebUI.TValidationSummary} takes care of displaying - * the validator error messages - * as html output or an alert output.

- * - *

The {@link Prado.WebUI.TBaseValidator} is the base class for all - * validators and contains - * methods to interact with the actual inputs, data type conversion.

- * - *

An instance of {@link Prado.ValidationManager} must be instantiated first for a - * particular form before instantiating validators and summaries.

- * - *

Usage example: adding a required field to a text box input with - * ID "input1" in a form with ID "form1".

- *
- * <script type="text/javascript" src="../prado.js"></script>
- * <script type="text/javascript" src="../validator.js"></script>
- * <form id="form1" action="...">
- * <div>
- * 	<input type="text" id="input1" />
- *  <span id="validator1" style="display:none; color:red">*</span>
- *  <input type="submit text="submit" />
- * <script type="text/javascript">
- * new Prado.ValidationManager({FormID : 'form1'});
- * var options =
- * {
- *		ID :				'validator1',
- *		FormID :			'form1',
- *		ErrorMessage :		'*',
- *		ControlToValidate : 'input1'
- * }
- * new Prado.WebUI.TRequiredFieldValidator(options);
- * new Prado.WebUI.TValidationSummary({ID:'summary1',FormID:'form1'});
- *
- * //watch the form onsubmit event, check validators, stop if not valid.
- * Event.observe("form1", "submit" function(ev)
- * {
- * 	 if(Prado.WebUI.Validation.isValid("form1") == false)
- * 		Event.stop(ev);
- * });
- * </script>
- * </div>
- * </form>
- * 
- * - * @module validation - */ - -Prado.Validation = Class.create(); - -/** - * Global Validation Object. - * - *

To validate the inputs of a particular form, call - * {@link Prado.Validation.validate}(formID, groupID) - * where formID is the HTML form ID, and the optional - * groupID if present will only validate the validators - * in a particular group.

- *

Use {@link Prado.Validation.validateControl}(controlClientID) - * to trigger validation for a single control.

- * - * @object {static} Prado.Validation - */ -Object.extend(Prado.Validation, -{ - /** - * Hash of registered validation managers - * @var managers - */ - managers : {}, - - /** - * Validate the validators (those that DO NOT - * belong to a particular group) in the form specified by the - * formID parameter. If groupID is specified - * only validators belonging to that group will be validated. - * @function {boolean} ? - * @param {string} formID - ID of the form to validate - * @param {string} groupID - ID of the ValidationGroup to validate. - * @param {element} invoker - DOM element that calls for validation - * @returns true if validation succeeded - */ - validate : function(formID, groupID, invoker) - { - formID = formID || this.getForm(); - if(this.managers[formID]) - { - return this.managers[formID].validate(groupID, invoker); - } - else - { - throw new Error("Form '"+formID+"' is not registered with Prado.Validation"); - } - }, - - /** - * Validate all validators of a specific control. - * @function {boolean} ? - * @param {string} id - ID of DOM element to validate - * @return true if all validators are valid or no validators present, false otherwise. - */ - validateControl : function(id) - { - var formId=this.getForm(); - - if (this.managers[formId]) - { - return this.managers[formId].validateControl(id); - } else { - throw new Error("A validation manager needs to be created first."); - } - }, - - /** - * Return first registered form - * @function {string} ? - * @returns ID of first form. - */ - getForm : function() - { - var keys = $H(this.managers).keys(); - return keys[0]; - }, - - /** - * Check if the validators are valid for a particular form (and group). - * The validators states will not be changed. - * The validate function should be called first. - * @function {boolean} ? - * @param {string} formID - ID of the form to validate - * @param {string} groupID - ID of the ValiationGroup to validate. - * @return true if form is valid - */ - isValid : function(formID, groupID) - { - formID = formID || this.getForm(); - if(this.managers[formID]) - return this.managers[formID].isValid(groupID); - return true; - }, - - /** - * Reset the validators for a given group. - * The group is searched in the first registered form. - * @function ? - * @param {string} groupID - ID of the ValidationGroup to reset. - */ - reset : function(groupID) - { - var formID = this.getForm(); - if(this.managers[formID]) - this.managers[formID].reset(groupID); - }, - - /** - * Add a new validator to a particular form. - * @function {ValidationManager} ? - * @param {string} formID - ID of the form that the validator belongs to. - * @param {TBaseValidator} validator - Validator object - * @return ValidationManager for the form - */ - addValidator : function(formID, validator) - { - if(this.managers[formID]) - this.managers[formID].addValidator(validator); - else - throw new Error("A validation manager for form '"+formID+"' needs to be created first."); - return this.managers[formID]; - }, - - /** - * Add a new validation summary. - * @function {ValidationManager} ? - * @param {string} formID - ID of the form that the validation summary belongs to. - * @param {TValidationSummary} validator - TValidationSummary object - * @return ValidationManager for the form - */ - addSummary : function(formID, validator) - { - if(this.managers[formID]) - this.managers[formID].addSummary(validator); - else - throw new Error("A validation manager for form '"+formID+"' needs to be created first."); - return this.managers[formID]; - }, - - setErrorMessage : function(validatorID, message) - { - $H(Prado.Validation.managers).each(function(manager) - { - manager[1].validators.each(function(validator) - { - if(validator.options.ID == validatorID) - { - validator.options.ErrorMessage = message; - $(validatorID).innerHTML = message; - } - }); - }); - }, - - updateActiveCustomValidator : function(validatorID, isValid) - { - $H(Prado.Validation.managers).each(function(manager) - { - manager[1].validators.each(function(validator) - { - if(validator.options.ID == validatorID) - { - validator.updateIsValid(isValid); - } - }); - }); - } -}); - -/** - * Manages validators for a particular HTML form. - * - *

The manager contains references to all the validators - * summaries, and their groupings for a particular form. - * Generally, {@link Prado.Validation} methods should be called rather - * than calling directly the ValidationManager.

- * - * @class Prado.ValidationManager - */ -Prado.ValidationManager = Class.create(); -Prado.ValidationManager.prototype = -{ - /** - * Hash of registered validators by control's clientID - * @var controls - */ - controls: {}, - - /** - * Initialize ValidationManager. - * @constructor {protected} ? - * @param {object} options - Options for initialization - * @... {string} FormID - ID of form of this manager - */ - initialize : function(options) - { - if(!Prado.Validation.managers[options.FormID]) - { - /** - * List of validators - * @var {TBaseValidator[]} validators - */ - this.validators = []; - /** - * List of validation summaries - * @var {TValidationSummary[]} summaries - */ - this.summaries = []; - /** - * List of ValidationGroups - * @var {string[]} groups - */ - this.groups = []; - /** - * Options of this ValidationManager - * @var {object} options - */ - this.options = {}; - - this.options = options; - - Prado.Validation.managers[options.FormID] = this; - } - else - { - var manager = Prado.Validation.managers[options.FormID]; - this.validators = manager.validators; - this.summaries = manager.summaries; - this.groups = manager.groups; - this.options = manager.options; - } - }, - - /** - * Reset all validators in the given group. - * If group is null, validators without a group are used. - * @function ? - * @param {string} group - ID of ValidationGroup - */ - reset : function(group) - { - this.validatorPartition(group)[0].invoke('reset'); - this.updateSummary(group, true); - }, - - /** - * Validate the validators managed by this validation manager. - * If group is set, only validate validators in that group. - * @function {boolean} ? - * @param {optional string} group - ID of ValidationGroup - * @param {element} source - DOM element that calls for validation - * @return true if all validators are valid, false otherwise. - */ - validate : function(group, source) - { - var partition = this.validatorPartition(group); - var valid = partition[0].invoke('validate', source).all(); - this.focusOnError(partition[0]); - partition[1].invoke('hide'); - this.updateSummary(group, true); - return valid; - }, - - /** - * Perform validation for all validators of a single control. - * @function {boolean} ? - * @param {string} id - ID of DOM element to validate - * @return true if all validators are valid or no validators present, false otherwise. - */ - validateControl : function (id) - { - return this.controls[id] ? this.controls[id].invoke('validate',null).all() : true; - }, - - /** - * Focus on the first validator that is invalid and options.FocusOnError is true. - * @function ? - * @param {TBaseValidator[]} validators - Array of validator objects - */ - focusOnError : function(validators) - { - for(var i = 0; i < validators.length; i++) - { - if(!validators[i].isValid && validators[i].options.FocusOnError) - return Prado.Element.focus(validators[i].options.FocusElementID); - } - }, - - /** - * Get all validators in a group and all other validators. - * Returns an array with two arrays of validators. The first - * array contains all validators in the group if group is given, - * otherwhise all validators without a group. The second array - * contains all other validators. - * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? - * @param {optional string} group - ID of ValidationGroup - * @return Array with two arrays of validators. - */ - validatorPartition : function(group) - { - return group ? this.validatorsInGroup(group) : this.validatorsWithoutGroup(); - }, - - /** - * Get all validators in a group. - * Returns an array with two arrays of validators. The first - * array contains all validators in the group. The second array - * contains all other validators. - * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? - * @param {optional string} groupID - ID of ValidationGroup - * @return Array with two arrays of validators. - */ - validatorsInGroup : function(groupID) - { - if(this.groups.include(groupID)) - { - return this.validators.partition(function(val) - { - return val.group == groupID; - }); - } - else - return [[],[]]; - }, - - /** - * Get all validators without a group. - * Returns an array with two arrays of validators. The first - * array contains all validators without a group. The second - * array contains all other validators. - * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? - * @return Array with two arrays of validators: Array[0] has all - * validators without a group, Array[1] all other validators. - */ - validatorsWithoutGroup : function() - { - return this.validators.partition(function(val) - { - return !val.group; - }); - }, - - /** - * Get the state of validators. - * If group is set, only validators in that group are checked. - * Otherwhise only validators without a group are checked. - * @function {booelan} ? - * @param {optional string} group - ID of ValidationGroup - * @return true if all validators (in a group, if supplied) are valid. - */ - isValid : function(group) - { - return this.validatorPartition(group)[0].pluck('isValid').all(); - }, - - /** - * Add a validator to this manager. - * @function ? - * @param {TBaseValidator} validator - Validator object - */ - addValidator : function(validator) - { - // Remove previously registered validator with same ID - // to prevent stale validators created by AJAX updates - this.removeValidator(validator); - - this.validators.push(validator); - if(validator.group && !this.groups.include(validator.group)) - this.groups.push(validator.group); - - if (typeof this.controls[validator.control.id] === 'undefined') - this.controls[validator.control.id] = Array(); - this.controls[validator.control.id].push(validator); - }, - - /** - * Add a validation summary. - * @function ? - * @param {TValidationSummary} summary - Validation summary. - */ - addSummary : function(summary) - { - this.summaries.push(summary); - }, - - /** - * Remove a validator from this manager - * @function ? - * @param {TBaseValidator} validator - Validator object - */ - removeValidator : function(validator) - { - this.validators = this.validators.reject(function(v) - { - return (v.options.ID==validator.options.ID); - }); - if (this.controls[validator.control.id]) - this.controls[validator.control.id].reject( function(v) - { - return (v.options.ID==validator.options.ID) - }); - }, - - /** - * Gets validators with errors. - * If group is set, only validators in that group are returned. - * Otherwhise only validators without a group are returned. - * @function {TBaseValidator[]} ? - * @param {optional string} group - ID of ValidationGroup - * @return array list of validators with error. - */ - getValidatorsWithError : function(group) - { - return this.validatorPartition(group)[0].findAll(function(validator) - { - return !validator.isValid; - }); - }, - - /** - * Update the summary of a particular group. - * If group is set, only the summary for validators in that - * group is updated. Otherwhise only the summary for validators - * without a group is updated. - * @function ? - * @param {optional string} group - ID of ValidationGroup - * @param {boolean} refresh - Wether the summary should be refreshed - */ - updateSummary : function(group, refresh) - { - var validators = this.getValidatorsWithError(group); - this.summaries.each(function(summary) - { - var inGroup = group && summary.group == group; - var noGroup = !group || !summary.group; - if(inGroup || noGroup) - summary.updateSummary(validators, refresh); - else - summary.hideSummary(true); - }); - } -}; - -/** - * TValidationSummary displays a summary of validation errors. - * - *

The summary is displayed inline on a Web page, - * in a message box, or both. By default, a validation summary will collect - * ErrorMessage of all failed validators on the page. If - * 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 DisplayMode option. - * The messages shown can be prefixed with HeaderText.

- * - *

The summary can be displayed on the Web page and in a message box by setting - * the ShowSummary and ShowMessageBox - * options, respectively.

- * - * @class Prado.WebUI.TValidationSummary - */ -Prado.WebUI.TValidationSummary = Class.create(); -Prado.WebUI.TValidationSummary.prototype = -{ - /** - * Initialize TValidationSummary. - * @constructor {protected} ? - * @param {object} options - Options for initialization - * @... {string} ID - ID of validation summary element - * @... {string} FormID - ID of form of this manager - * @... {optional string} ValidationGroup - ID of ValidationGroup. - * @... {optional boolean} ShowMessageBox - true to show the summary in an alert box. - * @... {optional boolean} ShowSummary - true to show the inline summary. - * @... {optional string} HeaderText - Summary header text - * @... {optional string} DisplayMode - Summary display style, 'BulletList', 'List', 'SingleParagraph' - * @... {optional boolean} Refresh - true to update the summary upon validator state change. - * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. - * @... {optional boolean} ScrollToSummary - true to scroll to the validation summary upon refresh. - * @... {optional function} OnHideSummary - Called on hide event. - * @... {optional function} OnShowSummary - Called on show event. - */ - initialize : function(options) - { - /** - * Validator options - * @var {object} options - */ - this.options = options; - /** - * ValidationGroup - * @var {string} group - */ - this.group = options.ValidationGroup; - /** - * Summary DOM element - * @var {element} messages - */ - this.messages = $(options.ID); - Prado.Registry.set(options.ID, this); - if(this.messages) - { - /** - * Current visibility state of summary - * @var {boolean} visible - */ - this.visible = this.messages.style.visibility != "hidden" - this.visible = this.visible && this.messages.style.display != "none"; - Prado.Validation.addSummary(options.FormID, this); - } - }, - - /** - * Update the validation summary. - * @function ? - * @param {TBaseValidator[]} validators - List of validators that failed validation. - * @param {boolean} update - true if visible summary should be updated - */ - updateSummary : function(validators, update) - { - if(validators.length <= 0) - { - if(update || this.options.Refresh != false) - { - return this.hideSummary(validators); - } - return; - } - - var refresh = update || this.visible == false || this.options.Refresh != false; - // Also, do not refresh summary if at least 1 validator is waiting for callback response. - // This will avoid the flickering of summary if the validator passes its test - refresh = refresh && validators.any(function(v) { return !v.requestDispatched; }); - - if(this.options.ShowSummary != false && refresh) - { - this.updateHTMLMessages(this.getMessages(validators)); - this.showSummary(validators); - } - - if(this.options.ScrollToSummary != false && refresh) - window.scrollTo(this.messages.offsetLeft-20, this.messages.offsetTop-20); - - if(this.options.ShowMessageBox == true && refresh) - { - this.alertMessages(this.getMessages(validators)); - this.visible = true; - } - }, - - /** - * Display the validator error messages as inline HTML. - * @function ? - * @param {string[]} messages - Array of error messages. - */ - updateHTMLMessages : function(messages) - { - while(this.messages.childNodes.length > 0) - this.messages.removeChild(this.messages.lastChild); - this.messages.insert(this.formatSummary(messages)); - }, - - /** - * Display the validator error messages as an alert box. - * @function ? - * @param {string[]} messages - Array of error messages. - */ - alertMessages : function(messages) - { - var text = this.formatMessageBox(messages); - setTimeout(function(){ alert(text); },20); - }, - - /** - * Get messages from validators. - * @function {string[]} ? - * @param {TBaseValidator[]} validators - Array of validators. - * @return Array of validator error messages. - */ - getMessages : function(validators) - { - var messages = []; - validators.each(function(validator) - { - var message = validator.getErrorMessage(); - if(typeof(message) == 'string' && message.length > 0) - messages.push(message); - }) - return messages; - }, - - /** - * Hide the validation summary. - * @function ? - * @param {TBaseValidator[]} validators - Array of validators. - */ - hideSummary : function(validators) - { if(typeof(this.options.OnHideSummary) == "function") - { - this.messages.style.visibility="visible"; - this.options.OnHideSummary(this,validators) - } - else - { - this.messages.style.visibility="hidden"; - if(this.options.Display == "None" || this.options.Display == "Dynamic") - this.messages.hide(); - } - this.visible = false; - }, - - /** - * Shows the validation summary. - * @function ? - * @param {TBaseValidator[]} validators - Array of validators. - */ - showSummary : function(validators) - { - this.messages.style.visibility="visible"; - if(typeof(this.options.OnShowSummary) == "function") - this.options.OnShowSummary(this,validators); - else - this.messages.show(); - this.visible = true; - }, - - /** - * Return the format parameters for the summary. - * @function {object} ? - * @param {string} type - Format type: "List", "SingleParagraph", "HeaderOnly" or "BulletList" (default) - * @return Object with format parameters: - * @... {string} header - Text for header - * @... {string} first - Text to prepend before message list - * @... {string} pre - Text to prepend before each message - * @... {string} post - Text to append after each message - * @... {string} first - Text to append after message list - */ - formats : function(type) - { - switch(type) - { - case "SimpleList": - return { header : "
", first : "", pre : "", post : "
", last : ""}; - case "SingleParagraph": - return { header : " ", first : "", pre : "", post : " ", last : "
"}; - case "HeaderOnly": - return { header : "", first : ""}; - case "BulletList": - default: - return { header : "", first : "
    ", pre : "
  • ", post : "
  • ", last : "
"}; - } - }, - - /** - * Format the message summary. - * @function {string} ? - * @param {string[]} messages - Array of error messages. - * @return Formatted message - */ - formatSummary : function(messages) - { - var format = this.formats(this.options.DisplayMode); - var output = this.options.HeaderText ? this.options.HeaderText + format.header : ""; - output += format.first; - messages.each(function(message) - { - output += message.length > 0 ? format.pre + message + format.post : ""; - }); -// for(var i = 0; i < messages.length; i++) - // output += (messages[i].length>0) ? format.pre + messages[i] + format.post : ""; - output += format.last; - return output; - }, - /** - * Format the message alert box. - * @function {string} ? - * @param {string[]} messages - Array of error messages. - * @return Formatted message for alert - */ - formatMessageBox : function(messages) - { - if(this.options.DisplayMode == 'HeaderOnly' && this.options.HeaderText) - return this.options.HeaderText; - - var output = this.options.HeaderText ? this.options.HeaderText + "\n" : ""; - for(var i = 0; i < messages.length; i++) - { - switch(this.options.DisplayMode) - { - case "List": - output += messages[i] + "\n"; - break; - case "BulletList": - default: - output += " - " + messages[i] + "\n"; - break; - case "SingleParagraph": - output += messages[i] + " "; - break; - } - } - return output; - } -}; - -/** - * TBaseValidator serves as the base class for validator controls. - * - *

Validation is performed when a postback control, such as a TButton, - * a TLinkButton or a TTextBox (under AutoPostBack mode) is submitting - * the page and its CausesValidation option is true. - * The input control to be validated is specified by ControlToValidate - * option.

- * - * @class Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TBaseValidator = Class.create(Prado.WebUI.Control, -{ - /** - * Initialize TBaseValidator. - * @constructor {protected} ? - * @param {object} options - Options for initialization. - * @... {string} ID - ID of validator - * @... {string} FormID - ID of form of this manager. - * @... {string} ControlToValidate - ID of control to validate. - * @... {optional string} InitialValue - Initial value of control to validate. - * @... {optional string} ErrorMessage - Validation error message. - * @... {optional string} ValidationGroup - ID of ValidationGroup. - * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. - * @... {optional boolean} ObserveChanges - True to observer changes of ControlToValidate - * @... {optional boolean} FocusOnError - True to focus on validation error. - * @... {optional string} FocusElementID - ID of element to focus on error. - * @... {optional string} ControlCssClass - Css class to use on ControlToValidate on error - * @... {optional function} OnValidate - Called immediately after validation. - * @... {optional function} OnValidationSuccess - Called after successful validation. - * @... {optional function} OnValidationError - Called after validation error. - */ - initialize : function(options) - { - this.observers = new Array(); - this.intervals = new Array(); - - /* options.OnValidate = options.OnValidate || Prototype.emptyFunction; - options.OnSuccess = options.OnSuccess || Prototype.emptyFunction; - options.OnError = options.OnError || Prototype.emptyFunction; - */ - /** - * Wether the validator is enabled (default true) - * @var {boolean} enabled - */ - this.enabled = options.Enabled; - /** - * Visibility state of validator(default false) - * @var {boolean} visible - */ - this.visible = false; - /** - * State of validation (default true) - * @var {boolean} isValid - */ - this.isValid = true; - /** - * DOM elements that are observed by this validator - * @var {private element[]} _isObserving - */ - this._isObserving = {}; - /** - * ValidationGroup - * @var {string} group - */ - this.group = null; - /** - * Wether a request was dispatched (default false) - * @var {boolean} requestDispatched - */ - this.requestDispatched = false; - - /** - * Validator options - * @var {object} options - */ - this.options = options; - /** - * DOM element of control to validate - * @var {element} control - */ - this.control = $(options.ControlToValidate); - /** - * DOM element of validator - * @var {element} message - */ - this.message = $(options.ID); - - Prado.Registry.set(options.ID, this); - - if (this.onInit) this.onInit(); - - if(this.control && this.message) - { - this.group = options.ValidationGroup; - - /** - * ValidationManager of this validator - * @var {ValidationManager} manager - */ - this.manager = Prado.Validation.addValidator(options.FormID, this); - } - }, - - /** - * Get error message. - * @function {string} ? - * @return Validation error message. - */ - getErrorMessage : function() - { - return this.options.ErrorMessage; - }, - - /** - * Update the validator. - * Updating the validator control will set the validator - * visible property to true. - * @function ? - */ - updateControl: function() - { - this.refreshControlAndMessage(); - - //if(this.options.FocusOnError && !this.isValid ) - // Prado.Element.focus(this.options.FocusElementID); - - this.visible = true; - }, - - /** - * Updates span and input CSS class. - * @function ? - */ - refreshControlAndMessage : function() - { - this.visible = true; - if(this.message) - { - if(this.options.Display == "Dynamic") - { - var msg=this.message; - this.isValid ? setTimeout(function() { msg.hide(); }, 250) : msg.show(); - } - this.message.style.visibility = this.isValid ? "hidden" : "visible"; - } - if(this.control) - this.updateControlCssClass(this.control, this.isValid); - }, - - /** - * Update CSS class of control to validate. - * Add a css class to the input control if validator is invalid, - * removes the css class if valid. - * @function ? - * @param {element} control - DOM element of control to validate - * @param {boolean} valid - Validation state of control - */ - updateControlCssClass : function(control, valid) - { - var CssClass = this.options.ControlCssClass; - if(typeof(CssClass) == "string" && CssClass.length > 0) - { - if(valid) - { - if (control.lastValidator == this.options.ID) - { - control.lastValidator = null; - control.removeClassName(CssClass); - } - } - else - { - control.lastValidator = this.options.ID; - control.addClassName(CssClass); - } - } - }, - - /** - * Hide the validator messages and remove any validation changes. - * @function ? - */ - hide : function() - { - this.reset(); - this.visible = false; - }, - - /** - * Reset validator. - * Sets isValid = true and updates the validator display. - * @function ? - */ - reset : function() - { - this.isValid = true; - this.updateControl(); - }, - - /** - * Perform validation. - * Calls evaluateIsValid() function to set the value of isValid property. - * Triggers onValidate event and onSuccess or onError event. - * @function {boolean} ? - * @param {element} invoker - DOM element that triggered validation - * @return Valdation state of control. - */ - validate : function(invoker) - { - //try to find the control. - if(!this.control) - this.control = $(this.options.ControlToValidate); - - if(!this.control || this.control.disabled) - { - this.isValid = true; - return this.isValid; - } - - if(typeof(this.options.OnValidate) == "function") - { - if(this.requestDispatched == false) - this.options.OnValidate(this, invoker); - } - - if(this.enabled && !this.control.getAttribute('disabled')) - this.isValid = this.evaluateIsValid(); - else - this.isValid = true; - - this.updateValidationDisplay(invoker); - this.observeChanges(this.control); - - return this.isValid; - }, - - /** - * Update validation display. - * Updates the validation messages and the control to validate. - * @param {element} invoker - DOM element that triggered validation - */ - updateValidationDisplay : function(invoker) - { - if(this.isValid) - { - if(typeof(this.options.OnValidationSuccess) == "function") - { - if(this.requestDispatched == false) - { - this.refreshControlAndMessage(); - this.options.OnValidationSuccess(this, invoker); - } - } - else - this.updateControl(); - } - else - { - if(typeof(this.options.OnValidationError) == "function") - { - if(this.requestDispatched == false) - { - this.refreshControlAndMessage(); - this.options.OnValidationError(this, invoker) - } - } - else - this.updateControl(); - } - }, - - /** - * Add control to observe for changes. - * Re-validates upon change. If the validator is not visible, - * no updates are propagated. - * @function ? - * @param {element} control - DOM element of control to observe - */ - observeChanges : function(control) - { - if(!control) return; - - var canObserveChanges = this.options.ObserveChanges != false; - var currentlyObserving = this._isObserving[control.id+this.options.ID]; - - if(canObserveChanges && !currentlyObserving) - { - var validator = this; - - this.observe(control, 'change', function() - { - if(validator.visible) - { - validator.validate(); - validator.manager.updateSummary(validator.group); - } - }); - this._isObserving[control.id+this.options.ID] = true; - } - }, - - /** - * Trim a string. - * @function {string} ? - * @param {string} value - String that should be trimmed. - * @return Trimmed string, empty string if value is not string. - */ - trim : function(value) - { - return typeof(value) == "string" ? value.trim() : ""; - }, - - /** - * Convert the value to a specific data type. - * @function {mixed|null} ? - * @param {string} dataType - Data type: "Integer", "Double", "Date" or "String" - * @param {mixed} value - Value to convert. - * @return Converted data value. - */ - convert : function(dataType, value) - { - if(typeof(value) == "undefined") - value = this.getValidationValue(); - var string = new String(value); - switch(dataType) - { - case "Integer": - return string.toInteger(); - case "Double" : - case "Float" : - return string.toDouble(this.options.DecimalChar); - case "Date": - if(typeof(value) != "string") - return value; - else - { - var value = string.toDate(this.options.DateFormat); - if(value && typeof(value.getTime) == "function") - return value.getTime(); - else - return null; - } - case "String": - return string.toString(); - } - return value; - }, - - /** - * Get value that should be validated. - * The ControlType property comes from TBaseValidator::getClientControlClass() - * Be sure to update the TBaseValidator::$_clientClass if new cases are added. - * @function {mixed} ? - * @param {optional element} control - Control to get value from (default: this.control) - * @return Control value to validate - */ - getRawValidationValue : function(control) - { - if(!control) - control = this.control - switch(this.options.ControlType) - { - case 'TDatePicker': - if(control.type == "text") - { - var value = this.trim($F(control)); - - if(this.options.DateFormat) - { - var date = value.toDate(this.options.DateFormat); - return date == null ? value : date; - } - else - return value; - } - else - { - this.observeDatePickerChanges(); - - return Prado.WebUI.TDatePicker.getDropDownDate(control);//.getTime(); - } - case 'THtmlArea': - if(typeof tinyMCE != "undefined") - tinyMCE.triggerSave(); - return $F(control); - case 'TRadioButton': - if(this.options.GroupName) - return this.getRadioButtonGroupValue(); - default: - if(this.isListControlType()) - return this.getFirstSelectedListValue(); - else - return $F(control); - } - }, - - /** - * Get a trimmed value that should be validated. - * The ControlType property comes from TBaseValidator::getClientControlClass() - * Be sure to update the TBaseValidator::$_clientClass if new cases are added. - * @function {mixed} ? - * @param {optional element} control - Control to get value fron (default: this.control) - * @return Control value to validate - */ - getValidationValue : function(control) - { - var value = this.getRawValidationValue(control); - if(!control) - control = this.control - switch(this.options.ControlType) - { - case 'TDatePicker': - return value; - case 'THtmlArea': - return this.trim(value); - case 'TRadioButton': - return value; - default: - if(this.isListControlType()) - return value; - else - return this.trim(value); - } - }, - - /** - * Get value of radio button group - * @function {string} ? - * @return Value of a radio button group - */ - getRadioButtonGroupValue : function() - { - var name = this.control.name; - var value = ""; - $A(document.getElementsByName(name)).each(function(el) - { - if(el.checked) - value = el.value; - }); - return value; - }, - - /** - * Observe changes in the drop down list date picker, IE only. - * @function ? - */ - observeDatePickerChanges : function() - { - if(Prado.Browser().ie) - { - var DatePicker = Prado.WebUI.TDatePicker; - this.observeChanges(DatePicker.getDayListControl(this.control)); - this.observeChanges(DatePicker.getMonthListControl(this.control)); - this.observeChanges(DatePicker.getYearListControl(this.control)); - } - }, - - /** - * Gets number of selections and their values. - * @function {object} ? - * @param {element[]} elements - Elements to get values from. - * @param {string} initialValue - Initial value of element - * @return Object: - * @... {mixed[]} values - Array of selected values - * @... {int} checks - Number of selections - */ - getSelectedValuesAndChecks : function(elements, initialValue) - { - var checked = 0; - var values = []; - var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected'; - elements.each(function(element) - { - if(element[isSelected] && element.value != initialValue) - { - checked++; - values.push(element.value); - } - }); - return {'checks' : checked, 'values' : values}; - }, - - /** - * Get list elements of TCheckBoxList or TListBox. - * Gets an array of the list control item input elements, for TCheckBoxList - * checkbox input elements are returned, for TListBox HTML option elements - * are returned. - * @function {element[]} ? - * @return Array of list control option DOM elements. - */ - getListElements : function() - { - switch(this.options.ControlType) - { - case 'TCheckBoxList': case 'TRadioButtonList': - var elements = []; - for(var i = 0; i < this.options.TotalItems; i++) - { - var element = $(this.options.ControlToValidate+"_c"+i); - if(this.isCheckBoxType(element)) - elements.push(element); - } - return elements; - case 'TListBox': - var elements = []; - var element = $(this.options.ControlToValidate); - var type; - if(element && (type = element.type.toLowerCase())) - { - if(type == "select-one" || type == "select-multiple") - elements = $A(element.options); - } - return elements; - default: - return []; - } - }, - - /** - * Check if control is of type checkbox or radio. - * @function {boolean} ? - * @param {element} element - DOM element to check. - * @return True if element is of checkbox or radio type. - */ - isCheckBoxType : function(element) - { - if(element && element.type) - { - var type = element.type.toLowerCase(); - return type == "checkbox" || type == "radio"; - } - return false; - }, - - /** - * Check if control to validate is a TListControl type. - * @function {boolean} ? - * @return True if control to validate is a TListControl type. - */ - isListControlType : function() - { - var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox']; - return list.include(this.options.ControlType); - }, - - /** - * Get first selected list value or initial value if none found. - * @function {string} ? - * @return First selected list value, initial value if none found. - */ - getFirstSelectedListValue : function() - { - var initial = ""; - if(typeof(this.options.InitialValue) != "undefined") - initial = this.options.InitialValue; - var elements = this.getListElements(); - var selection = this.getSelectedValuesAndChecks(elements, initial); - return selection.values.length > 0 ? selection.values[0] : initial; - } -}); - - -/** - * TRequiredFieldValidator makes the associated input control a required field. - * - *

The input control fails validation if its value does not change from - * the InitialValue option upon losing focus.

- * - * @class Prado.WebUI.TRequiredFieldValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if the input value is not empty nor equal to the initial value. - */ - evaluateIsValid : function() - { - var a = this.getValidationValue(); - var b = this.trim(this.options.InitialValue); - return(a != b); - } -}); - - -/** - * TCompareValidator compares the value entered by the user into an input - * control with the value entered into another input control or a constant value. - * - *

To compare the associated input control with another input control, - * set the ControlToCompare option to the ID path - * of the control to compare with. To compare the associated input control with - * a constant value, specify the constant value to compare with by setting the - * ValueToCompare option.

- * - *

The DataType property is used to specify the data type - * of both comparison values. Both values are automatically converted to this data - * type before the comparison 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 format can be set by the DateFormat option. - * - String A string data type.

- * - * Use the Operator property to specify the type of comparison - * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual, - * LessThan and LessThanEqual. - * - * @class Prado.WebUI.TCompareValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Additional constructor options. - * @constructor initialize - * @param {object} options - Additional constructor options: - * @... {string} ControlToCompare - Control with compare value. - * @... {string} ValueToCompare - Value to compare. - * @... {string} Operator - Type of comparison: "Equal", "NotEqual", "GreaterThan", - * "GreaterThanEqual", "LessThan" or "LessThanEqual". - * @... {string} Type - Type of values: "Integer", "Float", "Date" or "String". - * @... {string} DateFormat - Valid date format. - */ - - //_observingComparee : false, - - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if comparision condition is met. - */ - evaluateIsValid : function() - { - var value = this.getValidationValue(); - if (value.length <= 0) - return true; - - var comparee = $(this.options.ControlToCompare); - - if(comparee) - var compareTo = this.getValidationValue(comparee); - else - var compareTo = this.options.ValueToCompare || ""; - - var isValid = this.compare(value, compareTo); - - if(comparee) - { - this.updateControlCssClass(comparee, isValid); - this.observeChanges(comparee); - } - return isValid; - }, - - /** - * Compare two operands. - * The operand values are casted to type defined - * by DataType option. False is returned if the first - * operand converts to null. Returns true if the second operand - * converts to null. The comparision is done based on the - * Operator option. - * @function ? - * @param {mixed} operand1 - First operand. - * @param {mixed} operand2 - Second operand. - */ - compare : function(operand1, operand2) - { - var op1, op2; - if((op1 = this.convert(this.options.DataType, operand1)) == null) - return false; - if ((op2 = this.convert(this.options.DataType, operand2)) == null) - return true; - switch (this.options.Operator) - { - case "NotEqual": - return (op1 != op2); - case "GreaterThan": - return (op1 > op2); - case "GreaterThanEqual": - return (op1 >= op2); - case "LessThan": - return (op1 < op2); - case "LessThanEqual": - return (op1 <= op2); - default: - return (op1 == op2); - } - } -}); - -/** - * TCustomValidator performs user-defined client-side validation on an - * input component. - * - *

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:

- * - *
- * <script type="text/javascript">
- * function ValidationFunctionName(sender, parameter)
- * {
- *    if(parameter == ...)
- *       return true;
- *    else
- *       return false;
- * }
- * </script>
- * 
- * - *

Use the ClientValidationFunction option - * to specify the name of the client-side validation script function associated - * with the TCustomValidator.

- * - * @class Prado.WebUI.TCustomValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Additional constructor options. - * @constructor initialize - * @param {object} options - Additional constructor options: - * @... {function} ClientValidationFunction - Custom validation function. - */ - - /** - * Evaluate validation state - * Returns true if no valid custom validation function is present. - * @function {boolean} ? - * @return True if custom validation returned true. - */ - evaluateIsValid : function() - { - var value = this.getValidationValue(); - var clientFunction = this.options.ClientValidationFunction; - if(typeof(clientFunction) == "string" && clientFunction.length > 0) - { - var validate = clientFunction.toFunction(); - return validate(this, value); - } - return true; - } -}); - -/** - * Uses callback request to perform validation. - * - * @class Prado.WebUI.TActiveCustomValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TActiveCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Override the parent implementation to store the invoker, in order to - * re-validate after the callback has returned - * Calls evaluateIsValid() function to set the value of isValid property. - * Triggers onValidate event and onSuccess or onError event. - * @function {boolean} ? - * @param {element} invoker - DOM element that triggered validation - * @return True if valid. - */ - validate : function(invoker) - { - this.invoker = invoker; - - //try to find the control. - if(!this.control) - this.control = $(this.options.ControlToValidate); - - if(!this.control || this.control.disabled) - { - this.isValid = true; - return this.isValid; - } - - if(typeof(this.options.OnValidate) == "function") - { - if(this.requestDispatched == false) - this.options.OnValidate(this, invoker); - } - - return true; - }, - - /** - * Send CallBack to start serverside validation. - * @function {boolean} ? - * @return True if valid. - */ - evaluateIsValid : function() - { - return this.isValid; - }, - - /** - * Parse CallBack response data on success. - * @function ? - * @param {CallbackRequest} request - CallbackRequest. - * @param {string} data - Response data. - */ - updateIsValid : function(data) - { - this.isValid = data; - this.requestDispatched = false; - if(typeof(this.options.onSuccess) == "function") - this.options.onSuccess(null,data); - this.updateValidationDisplay(); - this.manager.updateSummary(this.group); - } -}); - -/** - * TRangeValidator tests whether an input value is within a specified range. - * - *

TRangeValidator uses three key properties to perform its validation.

- * - *

The MinValue and MaxValue options specify the minimum - * and maximum values of the valid range.

- *

The DataType option is - * used to specify the data type of the value and the minimum and maximum range values. - * The 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 DateFormat option, which must be recognizable - * by Date.SimpleParse javascript function. - * - String A string data type. - * - * @class Prado.WebUI.TRangeValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Additional constructor options. - * @constructor initialize - * @param {object} options - Additional constructor options: - * @... {string} MinValue - Minimum range value - * @... {string} MaxValue - Maximum range value - * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" - * @... {string} DateFormat - Date format for data type Date. - */ - - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if value is in range or value is empty, - * false otherwhise and when type conversion failed. - */ - evaluateIsValid : function() - { - var value = this.getValidationValue(); - if(value.length <= 0) - return true; - if(typeof(this.options.DataType) == "undefined") - this.options.DataType = "String"; - - if(this.options.DataType != "StringLength") - { - var min = this.convert(this.options.DataType, this.options.MinValue || null); - var max = this.convert(this.options.DataType, this.options.MaxValue || null); - value = this.convert(this.options.DataType, value); - } - else - { - var min = this.options.MinValue || 0; - var max = this.options.MaxValue || Number.POSITIVE_INFINITY; - value = value.length; - } - - if(value == null) - return false; - - var valid = true; - - if(min != null) - valid = valid && (this.options.StrictComparison ? value > min : value >= min); - if(max != null) - valid = valid && (this.options.StrictComparison ? value < max : value <= max); - return valid; - } -}); - -/** - * TRegularExpressionValidator validates whether the value of an associated - * input component matches the pattern specified by a regular expression. - * - * @class Prado.WebUI.TRegularExpressionValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Additional constructor option. - * @constructor initialize - * @param {object} options - Additional constructor option: - * @... {string} ValidationExpression - Regular expression to match against. - * @... {string} PatternModifiers - Pattern modifiers: combinations of g, i, and m - */ - - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if value matches regular expression or value is empty. - */ - evaluateIsValid : function() - { - var value = this.getRawValidationValue(); - if (value.length <= 0) - return true; - - var rx = new RegExp('^'+this.options.ValidationExpression+'$',this.options.PatternModifiers); - var matches = rx.exec(value); - return (matches != null && value == matches[0]); - } -}); - -/** - * TEmailAddressValidator validates whether the value of an associated - * input component is a valid email address. - * - * @class Prado.WebUI.TEmailAddressValidator - * @extends Prado.WebUI.TRegularExpressionValidator - */ -Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator; - - -/** - * TListControlValidator checks the number of selection and their values - * for a TListControl that allows multiple selections. - * - * @class Prado.WebUI.TListControlValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if number of selections and/or their values match requirements. - */ - evaluateIsValid : function() - { - var elements = this.getListElements(); - if(elements && elements.length <= 0) - return true; - - this.observeListElements(elements); - - var selection = this.getSelectedValuesAndChecks(elements); - return this.isValidList(selection.checks, selection.values); - }, - - /** - * Observe list elements for of changes (only IE) - * @function ? - * @param {element[]} elements - Array of DOM elements to observe - */ - observeListElements : function(elements) - { - if(Prado.Browser().ie && this.isCheckBoxType(elements[0])) - { - var validator = this; - elements.each(function(element) - { - validator.observeChanges(element); - }); - } - }, - - /** - * Check if list is valid. - * Determine if the number of checked values and the checked values - * satisfy the required number of checks and/or the checked values - * equal to the required values. - * @function {boolean} ? - * @param {int} checked - Number of required checked values - * @param {string[]} values - Array of required checked values - * @return True if checked values and number of checks are satisfied. - */ - isValidList : function(checked, values) - { - var exists = true; - - //check the required values - var required = this.getRequiredValues(); - if(required.length > 0) - { - if(values.length < required.length) - return false; - required.each(function(requiredValue) - { - exists = exists && values.include(requiredValue); - }); - } - - var min = typeof(this.options.Min) == "undefined" ? - Number.NEGATIVE_INFINITY : this.options.Min; - var max = typeof(this.options.Max) == "undefined" ? - Number.POSITIVE_INFINITY : this.options.Max; - return exists && checked >= min && checked <= max; - }, - - /** - * Get list of required values. - * @function {string[]} ? - * @return Array of required values that must be selected. - */ - getRequiredValues : function() - { - var required = []; - if(this.options.Required && this.options.Required.length > 0) - required = this.options.Required.split(/,\s*/); - return required; - } -}); - - -/** - * TDataTypeValidator verifies if the input data is of the type specified - * by DataType option. - * - *

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 option DateFormat - * will be used to determine how to parse the date string.

- * - * @class Prado.WebUI.TDataTypeValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Additional constructor option. - * @constructor initialize - * @param {object} options - Additional constructor option: - * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" - * @... {string} DateFormat - Date format for data type Date. - */ - - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if value matches required data type. - */ - evaluateIsValid : function() - { - var value = this.getValidationValue(); - if(value.length <= 0) - return true; - return this.convert(this.options.DataType, value) != null; - } -}); - -/** - * TCaptchaValidator verifies if the input data is the same as - * the token shown in the associated CAPTCHA control. - * - * @class Prado.WebUI.TCaptchaValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TCaptchaValidator = Class.extend(Prado.WebUI.TBaseValidator, -{ - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if value matches captcha text - */ - evaluateIsValid : function() - { - var a = this.getValidationValue(); - var h = 0; - if (this.options.CaseSensitive==false) - a = a.toUpperCase(); - for(var i = a.length-1; i >= 0; --i) - h += a.charCodeAt(i); - return h == this.options.TokenHash; - }, - - crc32 : function(str) - { - function Utf8Encode(string) - { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) - { - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } - } - - return utftext; - }; - - str = Utf8Encode(str); - - var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; - var crc = 0; - var x = 0; - var y = 0; - - crc = crc ^ (-1); - for( var i = 0, iTop = str.length; i < iTop; i++ ) - { - y = ( crc ^ str.charCodeAt( i ) ) & 0xFF; - x = "0x" + table.substr( y * 9, 8 ); - crc = ( crc >>> 8 ) ^ x; - } - return crc ^ (-1); - } -}); - - -/** - * TReCaptchaValidator client-side control. - * - * @class Prado.WebUI.TReCaptchaValidator - * @extends Prado.WebUI.TBaseValidator - */ -Prado.WebUI.TReCaptchaValidator = Class.create(Prado.WebUI.TBaseValidator, -{ - onInit : function() - { - var obj = this; - var elements = document.getElementsByName(this.options.ResponseFieldName); - if (elements) - if (elements.length>=1) - { - this.observe(elements[0],'change',function() { obj.responseChanged() }); - this.observe(elements[0],'keydown',function() { obj.responseChanged() }); - } - }, - - responseChanged: function() - { - var field = $(this.options.ID+'_1'); - if (field.value=='1') return; - field.value = '1'; - Prado.Validation.validateControl(this.options.ID); - }, - - /** - * Evaluate validation state - * @function {boolean} ? - * @return True if the captcha has validate, False otherwise. - */ - evaluateIsValid : function() - { - return ($(this.options.ID+'_1').value=='1'); - } -}); - +/** + * Prado client-side javascript validation fascade. + * + *

There are 4 basic classes: {@link Prado.Validation}, + * {@link Prado.ValidationManager}, {@link Prado.WebUI.TValidationSummary} + * and {@link Prado.WebUI.TBaseValidator}, + * that interact together to perform validation. + * The {@link Prado.Validation} class co-ordinates together the + * validation scheme and is responsible for maintaining references + * to ValidationManagers.

+ * + *

The {@link Prado.ValidationManager} class is responsible for + * maintaining refereneces + * to individual validators, validation summaries and their associated + * groupings.

+ * + *

The {@link Prado.WebUI.TValidationSummary} takes care of displaying + * the validator error messages + * as html output or an alert output.

+ * + *

The {@link Prado.WebUI.TBaseValidator} is the base class for all + * validators and contains + * methods to interact with the actual inputs, data type conversion.

+ * + *

An instance of {@link Prado.ValidationManager} must be instantiated first for a + * particular form before instantiating validators and summaries.

+ * + *

Usage example: adding a required field to a text box input with + * ID "input1" in a form with ID "form1".

+ *
+ * <script type="text/javascript" src="../prado.js"></script>
+ * <script type="text/javascript" src="../validator.js"></script>
+ * <form id="form1" action="...">
+ * <div>
+ * 	<input type="text" id="input1" />
+ *  <span id="validator1" style="display:none; color:red">*</span>
+ *  <input type="submit text="submit" />
+ * <script type="text/javascript">
+ * new Prado.ValidationManager({FormID : 'form1'});
+ * var options =
+ * {
+ *		ID :				'validator1',
+ *		FormID :			'form1',
+ *		ErrorMessage :		'*',
+ *		ControlToValidate : 'input1'
+ * }
+ * new Prado.WebUI.TRequiredFieldValidator(options);
+ * new Prado.WebUI.TValidationSummary({ID:'summary1',FormID:'form1'});
+ *
+ * //watch the form onsubmit event, check validators, stop if not valid.
+ * Event.observe("form1", "submit" function(ev)
+ * {
+ * 	 if(Prado.WebUI.Validation.isValid("form1") == false)
+ * 		Event.stop(ev);
+ * });
+ * </script>
+ * </div>
+ * </form>
+ * 
+ * + * @module validation + */ + +Prado.Validation = Class.create(); + +/** + * Global Validation Object. + * + *

To validate the inputs of a particular form, call + * {@link Prado.Validation.validate}(formID, groupID) + * where formID is the HTML form ID, and the optional + * groupID if present will only validate the validators + * in a particular group.

+ *

Use {@link Prado.Validation.validateControl}(controlClientID) + * to trigger validation for a single control.

+ * + * @object {static} Prado.Validation + */ +Object.extend(Prado.Validation, +{ + /** + * Hash of registered validation managers + * @var managers + */ + managers : {}, + + /** + * Validate the validators (those that DO NOT + * belong to a particular group) in the form specified by the + * formID parameter. If groupID is specified + * only validators belonging to that group will be validated. + * @function {boolean} ? + * @param {string} formID - ID of the form to validate + * @param {string} groupID - ID of the ValidationGroup to validate. + * @param {element} invoker - DOM element that calls for validation + * @returns true if validation succeeded + */ + validate : function(formID, groupID, invoker) + { + formID = formID || this.getForm(); + if(this.managers[formID]) + { + return this.managers[formID].validate(groupID, invoker); + } + else + { + throw new Error("Form '"+formID+"' is not registered with Prado.Validation"); + } + }, + + /** + * Validate all validators of a specific control. + * @function {boolean} ? + * @param {string} id - ID of DOM element to validate + * @return true if all validators are valid or no validators present, false otherwise. + */ + validateControl : function(id) + { + var formId=this.getForm(); + + if (this.managers[formId]) + { + return this.managers[formId].validateControl(id); + } else { + throw new Error("A validation manager needs to be created first."); + } + }, + + /** + * Return first registered form + * @function {string} ? + * @returns ID of first form. + */ + getForm : function() + { + var keys = $H(this.managers).keys(); + return keys[0]; + }, + + /** + * Check if the validators are valid for a particular form (and group). + * The validators states will not be changed. + * The validate function should be called first. + * @function {boolean} ? + * @param {string} formID - ID of the form to validate + * @param {string} groupID - ID of the ValiationGroup to validate. + * @return true if form is valid + */ + isValid : function(formID, groupID) + { + formID = formID || this.getForm(); + if(this.managers[formID]) + return this.managers[formID].isValid(groupID); + return true; + }, + + /** + * Reset the validators for a given group. + * The group is searched in the first registered form. + * @function ? + * @param {string} groupID - ID of the ValidationGroup to reset. + */ + reset : function(groupID) + { + var formID = this.getForm(); + if(this.managers[formID]) + this.managers[formID].reset(groupID); + }, + + /** + * Add a new validator to a particular form. + * @function {ValidationManager} ? + * @param {string} formID - ID of the form that the validator belongs to. + * @param {TBaseValidator} validator - Validator object + * @return ValidationManager for the form + */ + addValidator : function(formID, validator) + { + if(this.managers[formID]) + this.managers[formID].addValidator(validator); + else + throw new Error("A validation manager for form '"+formID+"' needs to be created first."); + return this.managers[formID]; + }, + + /** + * Add a new validation summary. + * @function {ValidationManager} ? + * @param {string} formID - ID of the form that the validation summary belongs to. + * @param {TValidationSummary} validator - TValidationSummary object + * @return ValidationManager for the form + */ + addSummary : function(formID, validator) + { + if(this.managers[formID]) + this.managers[formID].addSummary(validator); + else + throw new Error("A validation manager for form '"+formID+"' needs to be created first."); + return this.managers[formID]; + }, + + setErrorMessage : function(validatorID, message) + { + $H(Prado.Validation.managers).each(function(manager) + { + manager[1].validators.each(function(validator) + { + if(validator.options.ID == validatorID) + { + validator.options.ErrorMessage = message; + $(validatorID).innerHTML = message; + } + }); + }); + }, + + updateActiveCustomValidator : function(validatorID, isValid) + { + $H(Prado.Validation.managers).each(function(manager) + { + manager[1].validators.each(function(validator) + { + if(validator.options.ID == validatorID) + { + validator.updateIsValid(isValid); + } + }); + }); + } +}); + +/** + * Manages validators for a particular HTML form. + * + *

The manager contains references to all the validators + * summaries, and their groupings for a particular form. + * Generally, {@link Prado.Validation} methods should be called rather + * than calling directly the ValidationManager.

+ * + * @class Prado.ValidationManager + */ +Prado.ValidationManager = Class.create(); +Prado.ValidationManager.prototype = +{ + /** + * Hash of registered validators by control's clientID + * @var controls + */ + controls: {}, + + /** + * Initialize ValidationManager. + * @constructor {protected} ? + * @param {object} options - Options for initialization + * @... {string} FormID - ID of form of this manager + */ + initialize : function(options) + { + if(!Prado.Validation.managers[options.FormID]) + { + /** + * List of validators + * @var {TBaseValidator[]} validators + */ + this.validators = []; + /** + * List of validation summaries + * @var {TValidationSummary[]} summaries + */ + this.summaries = []; + /** + * List of ValidationGroups + * @var {string[]} groups + */ + this.groups = []; + /** + * Options of this ValidationManager + * @var {object} options + */ + this.options = {}; + + this.options = options; + + Prado.Validation.managers[options.FormID] = this; + } + else + { + var manager = Prado.Validation.managers[options.FormID]; + this.validators = manager.validators; + this.summaries = manager.summaries; + this.groups = manager.groups; + this.options = manager.options; + } + }, + + /** + * Reset all validators in the given group. + * If group is null, validators without a group are used. + * @function ? + * @param {string} group - ID of ValidationGroup + */ + reset : function(group) + { + this.validatorPartition(group)[0].invoke('reset'); + this.updateSummary(group, true); + }, + + /** + * Validate the validators managed by this validation manager. + * If group is set, only validate validators in that group. + * @function {boolean} ? + * @param {optional string} group - ID of ValidationGroup + * @param {element} source - DOM element that calls for validation + * @return true if all validators are valid, false otherwise. + */ + validate : function(group, source) + { + var partition = this.validatorPartition(group); + var valid = partition[0].invoke('validate', source).all(); + this.focusOnError(partition[0]); + partition[1].invoke('hide'); + this.updateSummary(group, true); + return valid; + }, + + /** + * Perform validation for all validators of a single control. + * @function {boolean} ? + * @param {string} id - ID of DOM element to validate + * @return true if all validators are valid or no validators present, false otherwise. + */ + validateControl : function (id) + { + return this.controls[id] ? this.controls[id].invoke('validate',null).all() : true; + }, + + /** + * Focus on the first validator that is invalid and options.FocusOnError is true. + * @function ? + * @param {TBaseValidator[]} validators - Array of validator objects + */ + focusOnError : function(validators) + { + for(var i = 0; i < validators.length; i++) + { + if(!validators[i].isValid && validators[i].options.FocusOnError) + return Prado.Element.focus(validators[i].options.FocusElementID); + } + }, + + /** + * Get all validators in a group and all other validators. + * Returns an array with two arrays of validators. The first + * array contains all validators in the group if group is given, + * otherwhise all validators without a group. The second array + * contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @param {optional string} group - ID of ValidationGroup + * @return Array with two arrays of validators. + */ + validatorPartition : function(group) + { + return group ? this.validatorsInGroup(group) : this.validatorsWithoutGroup(); + }, + + /** + * Get all validators in a group. + * Returns an array with two arrays of validators. The first + * array contains all validators in the group. The second array + * contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @param {optional string} groupID - ID of ValidationGroup + * @return Array with two arrays of validators. + */ + validatorsInGroup : function(groupID) + { + if(this.groups.include(groupID)) + { + return this.validators.partition(function(val) + { + return val.group == groupID; + }); + } + else + return [[],[]]; + }, + + /** + * Get all validators without a group. + * Returns an array with two arrays of validators. The first + * array contains all validators without a group. The second + * array contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @return Array with two arrays of validators: Array[0] has all + * validators without a group, Array[1] all other validators. + */ + validatorsWithoutGroup : function() + { + return this.validators.partition(function(val) + { + return !val.group; + }); + }, + + /** + * Get the state of validators. + * If group is set, only validators in that group are checked. + * Otherwhise only validators without a group are checked. + * @function {booelan} ? + * @param {optional string} group - ID of ValidationGroup + * @return true if all validators (in a group, if supplied) are valid. + */ + isValid : function(group) + { + return this.validatorPartition(group)[0].pluck('isValid').all(); + }, + + /** + * Add a validator to this manager. + * @function ? + * @param {TBaseValidator} validator - Validator object + */ + addValidator : function(validator) + { + // Remove previously registered validator with same ID + // to prevent stale validators created by AJAX updates + this.removeValidator(validator); + + this.validators.push(validator); + if(validator.group && !this.groups.include(validator.group)) + this.groups.push(validator.group); + + if (typeof this.controls[validator.control.id] === 'undefined') + this.controls[validator.control.id] = Array(); + this.controls[validator.control.id].push(validator); + }, + + /** + * Add a validation summary. + * @function ? + * @param {TValidationSummary} summary - Validation summary. + */ + addSummary : function(summary) + { + this.summaries.push(summary); + }, + + /** + * Remove a validator from this manager + * @function ? + * @param {TBaseValidator} validator - Validator object + */ + removeValidator : function(validator) + { + this.validators = this.validators.reject(function(v) + { + return (v.options.ID==validator.options.ID); + }); + if (this.controls[validator.control.id]) + this.controls[validator.control.id].reject( function(v) + { + return (v.options.ID==validator.options.ID) + }); + }, + + /** + * Gets validators with errors. + * If group is set, only validators in that group are returned. + * Otherwhise only validators without a group are returned. + * @function {TBaseValidator[]} ? + * @param {optional string} group - ID of ValidationGroup + * @return array list of validators with error. + */ + getValidatorsWithError : function(group) + { + return this.validatorPartition(group)[0].findAll(function(validator) + { + return !validator.isValid; + }); + }, + + /** + * Update the summary of a particular group. + * If group is set, only the summary for validators in that + * group is updated. Otherwhise only the summary for validators + * without a group is updated. + * @function ? + * @param {optional string} group - ID of ValidationGroup + * @param {boolean} refresh - Wether the summary should be refreshed + */ + updateSummary : function(group, refresh) + { + var validators = this.getValidatorsWithError(group); + this.summaries.each(function(summary) + { + var inGroup = group && summary.group == group; + var noGroup = !group || !summary.group; + if(inGroup || noGroup) + summary.updateSummary(validators, refresh); + else + summary.hideSummary(true); + }); + } +}; + +/** + * TValidationSummary displays a summary of validation errors. + * + *

The summary is displayed inline on a Web page, + * in a message box, or both. By default, a validation summary will collect + * ErrorMessage of all failed validators on the page. If + * 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 DisplayMode option. + * The messages shown can be prefixed with HeaderText.

+ * + *

The summary can be displayed on the Web page and in a message box by setting + * the ShowSummary and ShowMessageBox + * options, respectively.

+ * + * @class Prado.WebUI.TValidationSummary + */ +Prado.WebUI.TValidationSummary = Class.create(); +Prado.WebUI.TValidationSummary.prototype = +{ + /** + * Initialize TValidationSummary. + * @constructor {protected} ? + * @param {object} options - Options for initialization + * @... {string} ID - ID of validation summary element + * @... {string} FormID - ID of form of this manager + * @... {optional string} ValidationGroup - ID of ValidationGroup. + * @... {optional boolean} ShowMessageBox - true to show the summary in an alert box. + * @... {optional boolean} ShowSummary - true to show the inline summary. + * @... {optional string} HeaderText - Summary header text + * @... {optional string} DisplayMode - Summary display style, 'BulletList', 'List', 'SingleParagraph' + * @... {optional boolean} Refresh - true to update the summary upon validator state change. + * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. + * @... {optional boolean} ScrollToSummary - true to scroll to the validation summary upon refresh. + * @... {optional function} OnHideSummary - Called on hide event. + * @... {optional function} OnShowSummary - Called on show event. + */ + initialize : function(options) + { + /** + * Validator options + * @var {object} options + */ + this.options = options; + /** + * ValidationGroup + * @var {string} group + */ + this.group = options.ValidationGroup; + /** + * Summary DOM element + * @var {element} messages + */ + this.messages = $(options.ID); + Prado.Registry.set(options.ID, this); + if(this.messages) + { + /** + * Current visibility state of summary + * @var {boolean} visible + */ + this.visible = this.messages.style.visibility != "hidden" + this.visible = this.visible && this.messages.style.display != "none"; + Prado.Validation.addSummary(options.FormID, this); + } + }, + + /** + * Update the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - List of validators that failed validation. + * @param {boolean} update - true if visible summary should be updated + */ + updateSummary : function(validators, update) + { + if(validators.length <= 0) + { + if(update || this.options.Refresh != false) + { + return this.hideSummary(validators); + } + return; + } + + var refresh = update || this.visible == false || this.options.Refresh != false; + // Also, do not refresh summary if at least 1 validator is waiting for callback response. + // This will avoid the flickering of summary if the validator passes its test + refresh = refresh && validators.any(function(v) { return !v.requestDispatched; }); + + if(this.options.ShowSummary != false && refresh) + { + this.updateHTMLMessages(this.getMessages(validators)); + this.showSummary(validators); + } + + if(this.options.ScrollToSummary != false && refresh) + window.scrollTo(this.messages.offsetLeft-20, this.messages.offsetTop-20); + + if(this.options.ShowMessageBox == true && refresh) + { + this.alertMessages(this.getMessages(validators)); + this.visible = true; + } + }, + + /** + * Display the validator error messages as inline HTML. + * @function ? + * @param {string[]} messages - Array of error messages. + */ + updateHTMLMessages : function(messages) + { + while(this.messages.childNodes.length > 0) + this.messages.removeChild(this.messages.lastChild); + this.messages.insert(this.formatSummary(messages)); + }, + + /** + * Display the validator error messages as an alert box. + * @function ? + * @param {string[]} messages - Array of error messages. + */ + alertMessages : function(messages) + { + var text = this.formatMessageBox(messages); + setTimeout(function(){ alert(text); },20); + }, + + /** + * Get messages from validators. + * @function {string[]} ? + * @param {TBaseValidator[]} validators - Array of validators. + * @return Array of validator error messages. + */ + getMessages : function(validators) + { + var messages = []; + validators.each(function(validator) + { + var message = validator.getErrorMessage(); + if(typeof(message) == 'string' && message.length > 0) + messages.push(message); + }) + return messages; + }, + + /** + * Hide the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - Array of validators. + */ + hideSummary : function(validators) + { if(typeof(this.options.OnHideSummary) == "function") + { + this.messages.style.visibility="visible"; + this.options.OnHideSummary(this,validators) + } + else + { + this.messages.style.visibility="hidden"; + if(this.options.Display == "None" || this.options.Display == "Dynamic") + this.messages.hide(); + } + this.visible = false; + }, + + /** + * Shows the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - Array of validators. + */ + showSummary : function(validators) + { + this.messages.style.visibility="visible"; + if(typeof(this.options.OnShowSummary) == "function") + this.options.OnShowSummary(this,validators); + else + this.messages.show(); + this.visible = true; + }, + + /** + * Return the format parameters for the summary. + * @function {object} ? + * @param {string} type - Format type: "List", "SingleParagraph", "HeaderOnly" or "BulletList" (default) + * @return Object with format parameters: + * @... {string} header - Text for header + * @... {string} first - Text to prepend before message list + * @... {string} pre - Text to prepend before each message + * @... {string} post - Text to append after each message + * @... {string} first - Text to append after message list + */ + formats : function(type) + { + switch(type) + { + case "SimpleList": + return { header : "
", first : "", pre : "", post : "
", last : ""}; + case "SingleParagraph": + return { header : " ", first : "", pre : "", post : " ", last : "
"}; + case "HeaderOnly": + return { header : "", first : ""}; + case "BulletList": + default: + return { header : "", first : "
    ", pre : "
  • ", post : "
  • ", last : "
"}; + } + }, + + /** + * Format the message summary. + * @function {string} ? + * @param {string[]} messages - Array of error messages. + * @return Formatted message + */ + formatSummary : function(messages) + { + var format = this.formats(this.options.DisplayMode); + var output = this.options.HeaderText ? this.options.HeaderText + format.header : ""; + output += format.first; + messages.each(function(message) + { + output += message.length > 0 ? format.pre + message + format.post : ""; + }); +// for(var i = 0; i < messages.length; i++) + // output += (messages[i].length>0) ? format.pre + messages[i] + format.post : ""; + output += format.last; + return output; + }, + /** + * Format the message alert box. + * @function {string} ? + * @param {string[]} messages - Array of error messages. + * @return Formatted message for alert + */ + formatMessageBox : function(messages) + { + if(this.options.DisplayMode == 'HeaderOnly' && this.options.HeaderText) + return this.options.HeaderText; + + var output = this.options.HeaderText ? this.options.HeaderText + "\n" : ""; + for(var i = 0; i < messages.length; i++) + { + switch(this.options.DisplayMode) + { + case "List": + output += messages[i] + "\n"; + break; + case "BulletList": + default: + output += " - " + messages[i] + "\n"; + break; + case "SingleParagraph": + output += messages[i] + " "; + break; + } + } + return output; + } +}; + +/** + * TBaseValidator serves as the base class for validator controls. + * + *

Validation is performed when a postback control, such as a TButton, + * a TLinkButton or a TTextBox (under AutoPostBack mode) is submitting + * the page and its CausesValidation option is true. + * The input control to be validated is specified by ControlToValidate + * option.

+ * + * @class Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TBaseValidator = Class.create(Prado.WebUI.Control, +{ + /** + * Initialize TBaseValidator. + * @constructor {protected} ? + * @param {object} options - Options for initialization. + * @... {string} ID - ID of validator + * @... {string} FormID - ID of form of this manager. + * @... {string} ControlToValidate - ID of control to validate. + * @... {optional string} InitialValue - Initial value of control to validate. + * @... {optional string} ErrorMessage - Validation error message. + * @... {optional string} ValidationGroup - ID of ValidationGroup. + * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. + * @... {optional boolean} ObserveChanges - True to observer changes of ControlToValidate + * @... {optional boolean} FocusOnError - True to focus on validation error. + * @... {optional string} FocusElementID - ID of element to focus on error. + * @... {optional string} ControlCssClass - Css class to use on ControlToValidate on error + * @... {optional function} OnValidate - Called immediately after validation. + * @... {optional function} OnValidationSuccess - Called after successful validation. + * @... {optional function} OnValidationError - Called after validation error. + */ + initialize : function(options) + { + this.observers = new Array(); + this.intervals = new Array(); + + /* options.OnValidate = options.OnValidate || Prototype.emptyFunction; + options.OnSuccess = options.OnSuccess || Prototype.emptyFunction; + options.OnError = options.OnError || Prototype.emptyFunction; + */ + /** + * Wether the validator is enabled (default true) + * @var {boolean} enabled + */ + this.enabled = options.Enabled; + /** + * Visibility state of validator(default false) + * @var {boolean} visible + */ + this.visible = false; + /** + * State of validation (default true) + * @var {boolean} isValid + */ + this.isValid = true; + /** + * DOM elements that are observed by this validator + * @var {private element[]} _isObserving + */ + this._isObserving = {}; + /** + * ValidationGroup + * @var {string} group + */ + this.group = null; + /** + * Wether a request was dispatched (default false) + * @var {boolean} requestDispatched + */ + this.requestDispatched = false; + + /** + * Validator options + * @var {object} options + */ + this.options = options; + /** + * DOM element of control to validate + * @var {element} control + */ + this.control = $(options.ControlToValidate); + /** + * DOM element of validator + * @var {element} message + */ + this.message = $(options.ID); + + Prado.Registry.set(options.ID, this); + + if (this.onInit) this.onInit(); + + if(this.control && this.message) + { + this.group = options.ValidationGroup; + + /** + * ValidationManager of this validator + * @var {ValidationManager} manager + */ + this.manager = Prado.Validation.addValidator(options.FormID, this); + } + }, + + /** + * Get error message. + * @function {string} ? + * @return Validation error message. + */ + getErrorMessage : function() + { + return this.options.ErrorMessage; + }, + + /** + * Update the validator. + * Updating the validator control will set the validator + * visible property to true. + * @function ? + */ + updateControl: function() + { + this.refreshControlAndMessage(); + + //if(this.options.FocusOnError && !this.isValid ) + // Prado.Element.focus(this.options.FocusElementID); + + this.visible = true; + }, + + /** + * Updates span and input CSS class. + * @function ? + */ + refreshControlAndMessage : function() + { + this.visible = true; + if(this.message) + { + if(this.options.Display == "Dynamic") + { + var msg=this.message; + this.isValid ? setTimeout(function() { msg.hide(); }, 250) : msg.show(); + } + this.message.style.visibility = this.isValid ? "hidden" : "visible"; + } + if(this.control) + this.updateControlCssClass(this.control, this.isValid); + }, + + /** + * Update CSS class of control to validate. + * Add a css class to the input control if validator is invalid, + * removes the css class if valid. + * @function ? + * @param {element} control - DOM element of control to validate + * @param {boolean} valid - Validation state of control + */ + updateControlCssClass : function(control, valid) + { + var CssClass = this.options.ControlCssClass; + if(typeof(CssClass) == "string" && CssClass.length > 0) + { + if(valid) + { + if (control.lastValidator == this.options.ID) + { + control.lastValidator = null; + control.removeClassName(CssClass); + } + } + else + { + control.lastValidator = this.options.ID; + control.addClassName(CssClass); + } + } + }, + + /** + * Hide the validator messages and remove any validation changes. + * @function ? + */ + hide : function() + { + this.reset(); + this.visible = false; + }, + + /** + * Reset validator. + * Sets isValid = true and updates the validator display. + * @function ? + */ + reset : function() + { + this.isValid = true; + this.updateControl(); + }, + + /** + * Perform validation. + * Calls evaluateIsValid() function to set the value of isValid property. + * Triggers onValidate event and onSuccess or onError event. + * @function {boolean} ? + * @param {element} invoker - DOM element that triggered validation + * @return Valdation state of control. + */ + validate : function(invoker) + { + //try to find the control. + if(!this.control) + this.control = $(this.options.ControlToValidate); + + if(!this.control || this.control.disabled) + { + this.isValid = true; + return this.isValid; + } + + if(typeof(this.options.OnValidate) == "function") + { + if(this.requestDispatched == false) + this.options.OnValidate(this, invoker); + } + + if(this.enabled && !this.control.getAttribute('disabled')) + this.isValid = this.evaluateIsValid(); + else + this.isValid = true; + + this.updateValidationDisplay(invoker); + this.observeChanges(this.control); + + return this.isValid; + }, + + /** + * Update validation display. + * Updates the validation messages and the control to validate. + * @param {element} invoker - DOM element that triggered validation + */ + updateValidationDisplay : function(invoker) + { + if(this.isValid) + { + if(typeof(this.options.OnValidationSuccess) == "function") + { + if(this.requestDispatched == false) + { + this.refreshControlAndMessage(); + this.options.OnValidationSuccess(this, invoker); + } + } + else + this.updateControl(); + } + else + { + if(typeof(this.options.OnValidationError) == "function") + { + if(this.requestDispatched == false) + { + this.refreshControlAndMessage(); + this.options.OnValidationError(this, invoker) + } + } + else + this.updateControl(); + } + }, + + /** + * Add control to observe for changes. + * Re-validates upon change. If the validator is not visible, + * no updates are propagated. + * @function ? + * @param {element} control - DOM element of control to observe + */ + observeChanges : function(control) + { + if(!control) return; + + var canObserveChanges = this.options.ObserveChanges != false; + var currentlyObserving = this._isObserving[control.id+this.options.ID]; + + if(canObserveChanges && !currentlyObserving) + { + var validator = this; + + this.observe(control, 'change', function() + { + if(validator.visible) + { + validator.validate(); + validator.manager.updateSummary(validator.group); + } + }); + this._isObserving[control.id+this.options.ID] = true; + } + }, + + /** + * Trim a string. + * @function {string} ? + * @param {string} value - String that should be trimmed. + * @return Trimmed string, empty string if value is not string. + */ + trim : function(value) + { + return typeof(value) == "string" ? value.trim() : ""; + }, + + /** + * Convert the value to a specific data type. + * @function {mixed|null} ? + * @param {string} dataType - Data type: "Integer", "Double", "Date" or "String" + * @param {mixed} value - Value to convert. + * @return Converted data value. + */ + convert : function(dataType, value) + { + if(typeof(value) == "undefined") + value = this.getValidationValue(); + var string = new String(value); + switch(dataType) + { + case "Integer": + return string.toInteger(); + case "Double" : + case "Float" : + return string.toDouble(this.options.DecimalChar); + case "Date": + if(typeof(value) != "string") + return value; + else + { + var value = string.toDate(this.options.DateFormat); + if(value && typeof(value.getTime) == "function") + return value.getTime(); + else + return null; + } + case "String": + return string.toString(); + } + return value; + }, + + /** + * Get value that should be validated. + * The ControlType property comes from TBaseValidator::getClientControlClass() + * Be sure to update the TBaseValidator::$_clientClass if new cases are added. + * @function {mixed} ? + * @param {optional element} control - Control to get value from (default: this.control) + * @return Control value to validate + */ + getRawValidationValue : function(control) + { + if(!control) + control = this.control + switch(this.options.ControlType) + { + case 'TDatePicker': + if(control.type == "text") + { + var value = this.trim($F(control)); + + if(this.options.DateFormat) + { + var date = value.toDate(this.options.DateFormat); + return date == null ? value : date; + } + else + return value; + } + else + { + this.observeDatePickerChanges(); + + return Prado.WebUI.TDatePicker.getDropDownDate(control);//.getTime(); + } + case 'THtmlArea': + if(typeof tinyMCE != "undefined") + tinyMCE.triggerSave(); + return $F(control); + case 'TRadioButton': + if(this.options.GroupName) + return this.getRadioButtonGroupValue(); + default: + if(this.isListControlType()) + return this.getFirstSelectedListValue(); + else + return $F(control); + } + }, + + /** + * Get a trimmed value that should be validated. + * The ControlType property comes from TBaseValidator::getClientControlClass() + * Be sure to update the TBaseValidator::$_clientClass if new cases are added. + * @function {mixed} ? + * @param {optional element} control - Control to get value fron (default: this.control) + * @return Control value to validate + */ + getValidationValue : function(control) + { + var value = this.getRawValidationValue(control); + if(!control) + control = this.control + switch(this.options.ControlType) + { + case 'TDatePicker': + return value; + case 'THtmlArea': + return this.trim(value); + case 'TRadioButton': + return value; + default: + if(this.isListControlType()) + return value; + else + return this.trim(value); + } + }, + + /** + * Get value of radio button group + * @function {string} ? + * @return Value of a radio button group + */ + getRadioButtonGroupValue : function() + { + var name = this.control.name; + var value = ""; + $A(document.getElementsByName(name)).each(function(el) + { + if(el.checked) + value = el.value; + }); + return value; + }, + + /** + * Observe changes in the drop down list date picker, IE only. + * @function ? + */ + observeDatePickerChanges : function() + { + if(Prado.Browser().ie) + { + var DatePicker = Prado.WebUI.TDatePicker; + this.observeChanges(DatePicker.getDayListControl(this.control)); + this.observeChanges(DatePicker.getMonthListControl(this.control)); + this.observeChanges(DatePicker.getYearListControl(this.control)); + } + }, + + /** + * Gets number of selections and their values. + * @function {object} ? + * @param {element[]} elements - Elements to get values from. + * @param {string} initialValue - Initial value of element + * @return Object: + * @... {mixed[]} values - Array of selected values + * @... {int} checks - Number of selections + */ + getSelectedValuesAndChecks : function(elements, initialValue) + { + var checked = 0; + var values = []; + var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected'; + elements.each(function(element) + { + if(element[isSelected] && element.value != initialValue) + { + checked++; + values.push(element.value); + } + }); + return {'checks' : checked, 'values' : values}; + }, + + /** + * Get list elements of TCheckBoxList or TListBox. + * Gets an array of the list control item input elements, for TCheckBoxList + * checkbox input elements are returned, for TListBox HTML option elements + * are returned. + * @function {element[]} ? + * @return Array of list control option DOM elements. + */ + getListElements : function() + { + switch(this.options.ControlType) + { + case 'TCheckBoxList': case 'TRadioButtonList': + var elements = []; + for(var i = 0; i < this.options.TotalItems; i++) + { + var element = $(this.options.ControlToValidate+"_c"+i); + if(this.isCheckBoxType(element)) + elements.push(element); + } + return elements; + case 'TListBox': + var elements = []; + var element = $(this.options.ControlToValidate); + var type; + if(element && (type = element.type.toLowerCase())) + { + if(type == "select-one" || type == "select-multiple") + elements = $A(element.options); + } + return elements; + default: + return []; + } + }, + + /** + * Check if control is of type checkbox or radio. + * @function {boolean} ? + * @param {element} element - DOM element to check. + * @return True if element is of checkbox or radio type. + */ + isCheckBoxType : function(element) + { + if(element && element.type) + { + var type = element.type.toLowerCase(); + return type == "checkbox" || type == "radio"; + } + return false; + }, + + /** + * Check if control to validate is a TListControl type. + * @function {boolean} ? + * @return True if control to validate is a TListControl type. + */ + isListControlType : function() + { + var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox']; + return list.include(this.options.ControlType); + }, + + /** + * Get first selected list value or initial value if none found. + * @function {string} ? + * @return First selected list value, initial value if none found. + */ + getFirstSelectedListValue : function() + { + var initial = ""; + if(typeof(this.options.InitialValue) != "undefined") + initial = this.options.InitialValue; + var elements = this.getListElements(); + var selection = this.getSelectedValuesAndChecks(elements, initial); + return selection.values.length > 0 ? selection.values[0] : initial; + } +}); + + +/** + * TRequiredFieldValidator makes the associated input control a required field. + * + *

The input control fails validation if its value does not change from + * the InitialValue option upon losing focus.

+ * + * @class Prado.WebUI.TRequiredFieldValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if the input value is not empty nor equal to the initial value. + */ + evaluateIsValid : function() + { + var a = this.getValidationValue(); + var b = this.trim(this.options.InitialValue); + return(a != b); + } +}); + + +/** + * TCompareValidator compares the value entered by the user into an input + * control with the value entered into another input control or a constant value. + * + *

To compare the associated input control with another input control, + * set the ControlToCompare option to the ID path + * of the control to compare with. To compare the associated input control with + * a constant value, specify the constant value to compare with by setting the + * ValueToCompare option.

+ * + *

The DataType property is used to specify the data type + * of both comparison values. Both values are automatically converted to this data + * type before the comparison 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 format can be set by the DateFormat option. + * - String A string data type.

+ * + * Use the Operator property to specify the type of comparison + * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual, + * LessThan and LessThanEqual. + * + * @class Prado.WebUI.TCompareValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {string} ControlToCompare - Control with compare value. + * @... {string} ValueToCompare - Value to compare. + * @... {string} Operator - Type of comparison: "Equal", "NotEqual", "GreaterThan", + * "GreaterThanEqual", "LessThan" or "LessThanEqual". + * @... {string} Type - Type of values: "Integer", "Float", "Date" or "String". + * @... {string} DateFormat - Valid date format. + */ + + //_observingComparee : false, + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if comparision condition is met. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if (value.length <= 0) + return true; + + var comparee = $(this.options.ControlToCompare); + + if(comparee) + var compareTo = this.getValidationValue(comparee); + else + var compareTo = this.options.ValueToCompare || ""; + + var isValid = this.compare(value, compareTo); + + if(comparee) + { + this.updateControlCssClass(comparee, isValid); + this.observeChanges(comparee); + } + return isValid; + }, + + /** + * Compare two operands. + * The operand values are casted to type defined + * by DataType option. False is returned if the first + * operand converts to null. Returns true if the second operand + * converts to null. The comparision is done based on the + * Operator option. + * @function ? + * @param {mixed} operand1 - First operand. + * @param {mixed} operand2 - Second operand. + */ + compare : function(operand1, operand2) + { + var op1, op2; + if((op1 = this.convert(this.options.DataType, operand1)) == null) + return false; + if ((op2 = this.convert(this.options.DataType, operand2)) == null) + return true; + switch (this.options.Operator) + { + case "NotEqual": + return (op1 != op2); + case "GreaterThan": + return (op1 > op2); + case "GreaterThanEqual": + return (op1 >= op2); + case "LessThan": + return (op1 < op2); + case "LessThanEqual": + return (op1 <= op2); + default: + return (op1 == op2); + } + } +}); + +/** + * TCustomValidator performs user-defined client-side validation on an + * input component. + * + *

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:

+ * + *
+ * <script type="text/javascript">
+ * function ValidationFunctionName(sender, parameter)
+ * {
+ *    if(parameter == ...)
+ *       return true;
+ *    else
+ *       return false;
+ * }
+ * </script>
+ * 
+ * + *

Use the ClientValidationFunction option + * to specify the name of the client-side validation script function associated + * with the TCustomValidator.

+ * + * @class Prado.WebUI.TCustomValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {function} ClientValidationFunction - Custom validation function. + */ + + /** + * Evaluate validation state + * Returns true if no valid custom validation function is present. + * @function {boolean} ? + * @return True if custom validation returned true. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + var clientFunction = this.options.ClientValidationFunction; + if(typeof(clientFunction) == "string" && clientFunction.length > 0) + { + var validate = clientFunction.toFunction(); + return validate(this, value); + } + return true; + } +}); + +/** + * Uses callback request to perform validation. + * + * @class Prado.WebUI.TActiveCustomValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TActiveCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Override the parent implementation to store the invoker, in order to + * re-validate after the callback has returned + * Calls evaluateIsValid() function to set the value of isValid property. + * Triggers onValidate event and onSuccess or onError event. + * @function {boolean} ? + * @param {element} invoker - DOM element that triggered validation + * @return True if valid. + */ + validate : function(invoker) + { + this.invoker = invoker; + + //try to find the control. + if(!this.control) + this.control = $(this.options.ControlToValidate); + + if(!this.control || this.control.disabled) + { + this.isValid = true; + return this.isValid; + } + + if(typeof(this.options.OnValidate) == "function") + { + if(this.requestDispatched == false) + this.options.OnValidate(this, invoker); + } + + return true; + }, + + /** + * Send CallBack to start serverside validation. + * @function {boolean} ? + * @return True if valid. + */ + evaluateIsValid : function() + { + return this.isValid; + }, + + /** + * Parse CallBack response data on success. + * @function ? + * @param {CallbackRequest} request - CallbackRequest. + * @param {string} data - Response data. + */ + updateIsValid : function(data) + { + this.isValid = data; + this.requestDispatched = false; + if(typeof(this.options.onSuccess) == "function") + this.options.onSuccess(null,data); + this.updateValidationDisplay(); + this.manager.updateSummary(this.group); + } +}); + +/** + * TRangeValidator tests whether an input value is within a specified range. + * + *

TRangeValidator uses three key properties to perform its validation.

+ * + *

The MinValue and MaxValue options specify the minimum + * and maximum values of the valid range.

+ *

The DataType option is + * used to specify the data type of the value and the minimum and maximum range values. + * The 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 DateFormat option, which must be recognizable + * by Date.SimpleParse javascript function. + * - String A string data type. + * + * @class Prado.WebUI.TRangeValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {string} MinValue - Minimum range value + * @... {string} MaxValue - Maximum range value + * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" + * @... {string} DateFormat - Date format for data type Date. + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value is in range or value is empty, + * false otherwhise and when type conversion failed. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if(value.length <= 0) + return true; + if(typeof(this.options.DataType) == "undefined") + this.options.DataType = "String"; + + if(this.options.DataType != "StringLength") + { + var min = this.convert(this.options.DataType, this.options.MinValue || null); + var max = this.convert(this.options.DataType, this.options.MaxValue || null); + value = this.convert(this.options.DataType, value); + } + else + { + var min = this.options.MinValue || 0; + var max = this.options.MaxValue || Number.POSITIVE_INFINITY; + value = value.length; + } + + if(value == null) + return false; + + var valid = true; + + if(min != null) + valid = valid && (this.options.StrictComparison ? value > min : value >= min); + if(max != null) + valid = valid && (this.options.StrictComparison ? value < max : value <= max); + return valid; + } +}); + +/** + * TRegularExpressionValidator validates whether the value of an associated + * input component matches the pattern specified by a regular expression. + * + * @class Prado.WebUI.TRegularExpressionValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor option. + * @constructor initialize + * @param {object} options - Additional constructor option: + * @... {string} ValidationExpression - Regular expression to match against. + * @... {string} PatternModifiers - Pattern modifiers: combinations of g, i, and m + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches regular expression or value is empty. + */ + evaluateIsValid : function() + { + var value = this.getRawValidationValue(); + if (value.length <= 0) + return true; + + var rx = new RegExp('^'+this.options.ValidationExpression+'$',this.options.PatternModifiers); + var matches = rx.exec(value); + return (matches != null && value == matches[0]); + } +}); + +/** + * TEmailAddressValidator validates whether the value of an associated + * input component is a valid email address. + * + * @class Prado.WebUI.TEmailAddressValidator + * @extends Prado.WebUI.TRegularExpressionValidator + */ +Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator; + + +/** + * TListControlValidator checks the number of selection and their values + * for a TListControl that allows multiple selections. + * + * @class Prado.WebUI.TListControlValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if number of selections and/or their values match requirements. + */ + evaluateIsValid : function() + { + var elements = this.getListElements(); + if(elements && elements.length <= 0) + return true; + + this.observeListElements(elements); + + var selection = this.getSelectedValuesAndChecks(elements); + return this.isValidList(selection.checks, selection.values); + }, + + /** + * Observe list elements for of changes (only IE) + * @function ? + * @param {element[]} elements - Array of DOM elements to observe + */ + observeListElements : function(elements) + { + if(Prado.Browser().ie && this.isCheckBoxType(elements[0])) + { + var validator = this; + elements.each(function(element) + { + validator.observeChanges(element); + }); + } + }, + + /** + * Check if list is valid. + * Determine if the number of checked values and the checked values + * satisfy the required number of checks and/or the checked values + * equal to the required values. + * @function {boolean} ? + * @param {int} checked - Number of required checked values + * @param {string[]} values - Array of required checked values + * @return True if checked values and number of checks are satisfied. + */ + isValidList : function(checked, values) + { + var exists = true; + + //check the required values + var required = this.getRequiredValues(); + if(required.length > 0) + { + if(values.length < required.length) + return false; + required.each(function(requiredValue) + { + exists = exists && values.include(requiredValue); + }); + } + + var min = typeof(this.options.Min) == "undefined" ? + Number.NEGATIVE_INFINITY : this.options.Min; + var max = typeof(this.options.Max) == "undefined" ? + Number.POSITIVE_INFINITY : this.options.Max; + return exists && checked >= min && checked <= max; + }, + + /** + * Get list of required values. + * @function {string[]} ? + * @return Array of required values that must be selected. + */ + getRequiredValues : function() + { + var required = []; + if(this.options.Required && this.options.Required.length > 0) + required = this.options.Required.split(/,\s*/); + return required; + } +}); + + +/** + * TDataTypeValidator verifies if the input data is of the type specified + * by DataType option. + * + *

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 option DateFormat + * will be used to determine how to parse the date string.

+ * + * @class Prado.WebUI.TDataTypeValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor option. + * @constructor initialize + * @param {object} options - Additional constructor option: + * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" + * @... {string} DateFormat - Date format for data type Date. + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches required data type. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if(value.length <= 0) + return true; + return this.convert(this.options.DataType, value) != null; + } +}); + +/** + * TCaptchaValidator verifies if the input data is the same as + * the token shown in the associated CAPTCHA control. + * + * @class Prado.WebUI.TCaptchaValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCaptchaValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches captcha text + */ + evaluateIsValid : function() + { + var a = this.getValidationValue(); + var h = 0; + if (this.options.CaseSensitive==false) + a = a.toUpperCase(); + for(var i = a.length-1; i >= 0; --i) + h += a.charCodeAt(i); + return h == this.options.TokenHash; + }, + + crc32 : function(str) + { + function Utf8Encode(string) + { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) + { + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + + return utftext; + }; + + str = Utf8Encode(str); + + var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; + var crc = 0; + var x = 0; + var y = 0; + + crc = crc ^ (-1); + for( var i = 0, iTop = str.length; i < iTop; i++ ) + { + y = ( crc ^ str.charCodeAt( i ) ) & 0xFF; + x = "0x" + table.substr( y * 9, 8 ); + crc = ( crc >>> 8 ) ^ x; + } + return crc ^ (-1); + } +}); + + +/** + * TReCaptchaValidator client-side control. + * + * @class Prado.WebUI.TReCaptchaValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TReCaptchaValidator = Class.create(Prado.WebUI.TBaseValidator, +{ + onInit : function() + { + var obj = this; + var elements = document.getElementsByName(this.options.ResponseFieldName); + if (elements) + if (elements.length>=1) + { + this.observe(elements[0],'change',function() { obj.responseChanged() }); + this.observe(elements[0],'keydown',function() { obj.responseChanged() }); + } + }, + + responseChanged: function() + { + var field = $(this.options.ID+'_1'); + if (field.value=='1') return; + field.value = '1'; + Prado.Validation.validateControl(this.options.ID); + }, + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if the captcha has validate, False otherwise. + */ + evaluateIsValid : function() + { + return ($(this.options.ID+'_1').value=='1'); + } +}); + diff --git a/framework/Web/Services/TFeedService.php b/framework/Web/Services/TFeedService.php index ab1b9c35..4f757d8f 100644 --- a/framework/Web/Services/TFeedService.php +++ b/framework/Web/Services/TFeedService.php @@ -1,187 +1,187 @@ - - * @author Knut Urdalen - * @link http://www.pradosoft.com - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.Services - */ - -/** - * TFeedService class - * - * TFeedService provides to end-users feed content. - * - * TFeedService manages a set of feeds. The service parameter, referring - * to the ID of the feed, specifies which feed content to be provided to end-users. - * - * To use TFeedService, configure it in application configuration as follows, - * - * - * - * - * - * - * - * where each <feed> element specifies a feed identified by its "id" value (case-sensitive). - * - * PHP configuration style: - * - * array( - * 'feed' => array( - * 'ch1' => array( - * 'class' => 'Path.To.FeedClass1', - * 'properties' => array( - * ... - * ), - * ), - * ) - * - * - * The class attribute indicates which PHP class will provide the actual feed - * content. Note, the class must implement {@link IFeedContentProvider} interface. - * Other initial properties for the feed class may also be specified in the - * corresponding <feed> element. - * - * To retrieve the feed content identified by "ch2", use the URL - * /path/to/index.php?feed=ch2 - * - * @author Qiang Xue - * @author Knut Urdalen - * @author Carl G. Mathisen - * @package System.Web.Services - * @since 3.1 - */ -class TFeedService extends TService -{ - private $_feeds=array(); - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param mixed configuration for this module, can be null - */ - public function init($config) - { - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - if(is_array($config)) - { - foreach($config as $id => $feed) - $this->_feeds[$id] = $feed; - } - } - else - { - foreach($config->getElementsByTagName('feed') as $feed) - { - if(($id=$feed->getAttributes()->remove('id'))!==null) - $this->_feeds[$id]=$feed; - else - throw new TConfigurationException('feedservice_id_required'); - } - } - } - - /** - * @return string the requested feed path - */ - protected function determineRequestedFeedPath() - { - return $this->getRequest()->getServiceParameter(); - } - - /** - * Runs the service. - * This method is invoked by application automatically. - */ - public function run() - { - $id=$this->getRequest()->getServiceParameter(); - if(isset($this->_feeds[$id])) - { - $feedConfig=$this->_feeds[$id]; - $properties = array(); - $feed = null; - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - if(isset($feedConfig['class'])) - { - $feed=Prado::createComponent($feedConfig['class']); - if($service instanceof IFeedContentProvider) - $properties=isset($feedConfig['properties'])?$feedConfig['properties']:array(); - else - throw new TConfigurationException('jsonservice_response_type_invalid',$id); - } - else - throw new TConfigurationException('jsonservice_class_required',$id); - } - else - { - $properties=$feedConfig->getAttributes(); - if(($class=$properties->remove('class'))!==null) - { - $feed=Prado::createComponent($class); - if(!($feed instanceof IFeedContentProvider)) - throw new TConfigurationException('feedservice_feedtype_invalid',$id); - } - else - throw new TConfigurationException('feedservice_class_required',$id); - } - - // init feed properties - foreach($properties as $name=>$value) - $feed->setSubproperty($name,$value); - $feed->init($feedConfig); - - $content=$feed->getFeedContent(); - //$this->getResponse()->setContentType('application/rss+xml'); - $this->getResponse()->setContentType($feed->getContentType()); - $this->getResponse()->write($content); - } - else - throw new THttpException(404,'feedservice_feed_unknown',$id); - } -} - -/** - * IFeedContentProvider interface. - * - * IFeedContentProvider interface must be implemented by a feed class who - * provides feed content. - * - * @author Qiang Xue - * @author Knut Urdalen - * @package System.Web.Services - * @since 3.1 - */ -interface IFeedContentProvider -{ - /** - * Initializes the feed content provider. - * This method is invoked (before {@link getFeedContent}) - * when the feed provider is requested by a user. - * @param TXmlElement configurations specified within the <feed> element - * corresponding to this feed provider when configuring {@link TFeedService}. - */ - public function init($config); - /** - * @return string feed content in proper XML format - */ - public function getFeedContent(); - /** - * Sets the content type of the feed content to be sent. - * Some examples are: - * RSS 1.0 feed: application/rdf+xml - * RSS 2.0 feed: application/rss+xml or application/xml or text/xml - * ATOM feed: application/atom+xml - * @return string the content type for the feed content. - * @since 3.1.1 - */ - public function getContentType(); -} - + + * @author Knut Urdalen + * @link http://www.pradosoft.com + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.Services + */ + +/** + * TFeedService class + * + * TFeedService provides to end-users feed content. + * + * TFeedService manages a set of feeds. The service parameter, referring + * to the ID of the feed, specifies which feed content to be provided to end-users. + * + * To use TFeedService, configure it in application configuration as follows, + * + * + * + * + * + * + * + * where each <feed> element specifies a feed identified by its "id" value (case-sensitive). + * + * PHP configuration style: + * + * array( + * 'feed' => array( + * 'ch1' => array( + * 'class' => 'Path.To.FeedClass1', + * 'properties' => array( + * ... + * ), + * ), + * ) + * + * + * The class attribute indicates which PHP class will provide the actual feed + * content. Note, the class must implement {@link IFeedContentProvider} interface. + * Other initial properties for the feed class may also be specified in the + * corresponding <feed> element. + * + * To retrieve the feed content identified by "ch2", use the URL + * /path/to/index.php?feed=ch2 + * + * @author Qiang Xue + * @author Knut Urdalen + * @author Carl G. Mathisen + * @package System.Web.Services + * @since 3.1 + */ +class TFeedService extends TService +{ + private $_feeds=array(); + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + */ + public function init($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(is_array($config)) + { + foreach($config as $id => $feed) + $this->_feeds[$id] = $feed; + } + } + else + { + foreach($config->getElementsByTagName('feed') as $feed) + { + if(($id=$feed->getAttributes()->remove('id'))!==null) + $this->_feeds[$id]=$feed; + else + throw new TConfigurationException('feedservice_id_required'); + } + } + } + + /** + * @return string the requested feed path + */ + protected function determineRequestedFeedPath() + { + return $this->getRequest()->getServiceParameter(); + } + + /** + * Runs the service. + * This method is invoked by application automatically. + */ + public function run() + { + $id=$this->getRequest()->getServiceParameter(); + if(isset($this->_feeds[$id])) + { + $feedConfig=$this->_feeds[$id]; + $properties = array(); + $feed = null; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($feedConfig['class'])) + { + $feed=Prado::createComponent($feedConfig['class']); + if($service instanceof IFeedContentProvider) + $properties=isset($feedConfig['properties'])?$feedConfig['properties']:array(); + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + else + { + $properties=$feedConfig->getAttributes(); + if(($class=$properties->remove('class'))!==null) + { + $feed=Prado::createComponent($class); + if(!($feed instanceof IFeedContentProvider)) + throw new TConfigurationException('feedservice_feedtype_invalid',$id); + } + else + throw new TConfigurationException('feedservice_class_required',$id); + } + + // init feed properties + foreach($properties as $name=>$value) + $feed->setSubproperty($name,$value); + $feed->init($feedConfig); + + $content=$feed->getFeedContent(); + //$this->getResponse()->setContentType('application/rss+xml'); + $this->getResponse()->setContentType($feed->getContentType()); + $this->getResponse()->write($content); + } + else + throw new THttpException(404,'feedservice_feed_unknown',$id); + } +} + +/** + * IFeedContentProvider interface. + * + * IFeedContentProvider interface must be implemented by a feed class who + * provides feed content. + * + * @author Qiang Xue + * @author Knut Urdalen + * @package System.Web.Services + * @since 3.1 + */ +interface IFeedContentProvider +{ + /** + * Initializes the feed content provider. + * This method is invoked (before {@link getFeedContent}) + * when the feed provider is requested by a user. + * @param TXmlElement configurations specified within the <feed> element + * corresponding to this feed provider when configuring {@link TFeedService}. + */ + public function init($config); + /** + * @return string feed content in proper XML format + */ + public function getFeedContent(); + /** + * Sets the content type of the feed content to be sent. + * Some examples are: + * RSS 1.0 feed: application/rdf+xml + * RSS 2.0 feed: application/rss+xml or application/xml or text/xml + * ATOM feed: application/atom+xml + * @return string the content type for the feed content. + * @since 3.1.1 + */ + public function getContentType(); +} + diff --git a/framework/Web/Services/TJsonService.php b/framework/Web/Services/TJsonService.php index 13710101..b4e87e27 100644 --- a/framework/Web/Services/TJsonService.php +++ b/framework/Web/Services/TJsonService.php @@ -1,213 +1,213 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.Services - */ - -/** - * TJsonService class provides to end-users javascript content response in - * JSON format. - * - * TJsonService manages a set of {@link TJsonResponse}, each - * representing specific response with javascript content. - * The service parameter, referring to the ID of the service, specifies - * which javascript content to be provided to end-users. - * - * To use TJsonService, configure it in application configuration as follows, - * - * - * - * - * - * - * where each JSON response is specified via a <json> element. - * Initial property values can be configured in a <json> element. - * - * - * PHP configuration style: - * - * 'services' => array( - * 'get_article' => array( - * 'class' => 'Path.To.JsonResponseClass1', - * 'properties' => array( - * ... - * ) - * ) - * ) - * - * - * To retrieve the JSON content provided by "get_article", use the URL - * index.php?json=get_article - * - * @author Wei Zhuo - * @author Carl G. Mathisen - * @version $Id$ - * @package System.Web.Services - * @since 3.1 - */ -class TJsonService extends TService -{ - /** - * @var array registered services - */ - private $_services=array(); - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param mixed configuration for this module, can be null - */ - public function init($xml) - { - $this->loadJsonServices($xml); - } - - /** - * Load the service definitions. - * @param mixed configuration for this module, can be null - */ - protected function loadJsonServices($config) - { - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - if(is_array($config)) - { - foreach($config['json'] as $id => $json) - $this->_services[$id] = $json; - } - } - else - { - foreach($config->getElementsByTagName('json') as $json) - { - if(($id=$json->getAttribute('id'))!==null) - $this->_services[$id]=$json; - else - throw new TConfigurationException('jsonservice_id_required'); - } - } - } - - /** - * Runs the service. - * This method is invoked by application automatically. - */ - public function run() - { - $id=$this->getRequest()->getServiceParameter(); - if(isset($this->_services[$id])) - { - $serviceConfig=$this->_services[$id]; - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - if(isset($serviceConfig['class'])) - { - $service=Prado::createComponent($serviceConfig['class']); - if($service instanceof TJsonResponse) - { - $properties = isset($serviceConfig['properties'])?$serviceConfig['properties']:array(); - $this->createJsonResponse($service,$properties,$serviceConfig); - } - else - throw new TConfigurationException('jsonservice_response_type_invalid',$id); - } - else - throw new TConfigurationException('jsonservice_class_required',$id); - } - else - { - $properties=$serviceConfig->getAttributes(); - if(($class=$properties->remove('class'))!==null) - { - $service=Prado::createComponent($class); - if($service instanceof TJsonResponse) - $this->createJsonResponse($service,$properties,$serviceConfig); - else - throw new TConfigurationException('jsonservice_response_type_invalid',$id); - } - else - throw new TConfigurationException('jsonservice_class_required',$id); - } - } - else - throw new THttpException(404,'jsonservice_provider_unknown',$id); - } - - /** - * Renders content provided by TJsonResponse::getJsonContent() as - * javascript in JSON format. - */ - protected function createJsonResponse($service,$properties,$config) - { - // init service properties - foreach($properties as $name=>$value) - $service->setSubproperty($name,$value); - $service->init($config); - - //send content if not null - if(($content=$service->getJsonContent())!==null) - { - $response = $this->getResponse(); - $response->setContentType('text/javascript'); - $response->setCharset('UTF-8'); - //send content - $response->write(TJavaScript::jsonEncode($content)); - } - } -} - -/** - * TJsonResponse Class - * - * TJsonResponse is the base class for all JSON response provider classes. - * - * Derived classes must implement {@link getJsonContent()} to return - * an object or literals to be converted to JSON format. The response - * will be empty if the returned content is null. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web.Services - * @since 3.1 - */ -abstract class TJsonResponse extends TApplicationComponent -{ - private $_id=''; - - /** - * Initializes the feed. - * @param TXmlElement configurations specified in {@link TJsonService}. - */ - public function init($config) - { - } - - /** - * @return string ID of this response - */ - public function getID() - { - return $this->_id; - } - - /** - * @param string ID of this response - */ - public function setID($value) - { - $this->_id=$value; - } - - /** - * @return object json response content, null to suppress output. - */ - abstract public function getJsonContent(); -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.Services + */ + +/** + * TJsonService class provides to end-users javascript content response in + * JSON format. + * + * TJsonService manages a set of {@link TJsonResponse}, each + * representing specific response with javascript content. + * The service parameter, referring to the ID of the service, specifies + * which javascript content to be provided to end-users. + * + * To use TJsonService, configure it in application configuration as follows, + * + * + * + * + * + * + * where each JSON response is specified via a <json> element. + * Initial property values can be configured in a <json> element. + * + * + * PHP configuration style: + * + * 'services' => array( + * 'get_article' => array( + * 'class' => 'Path.To.JsonResponseClass1', + * 'properties' => array( + * ... + * ) + * ) + * ) + * + * + * To retrieve the JSON content provided by "get_article", use the URL + * index.php?json=get_article + * + * @author Wei Zhuo + * @author Carl G. Mathisen + * @version $Id$ + * @package System.Web.Services + * @since 3.1 + */ +class TJsonService extends TService +{ + /** + * @var array registered services + */ + private $_services=array(); + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + */ + public function init($xml) + { + $this->loadJsonServices($xml); + } + + /** + * Load the service definitions. + * @param mixed configuration for this module, can be null + */ + protected function loadJsonServices($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(is_array($config)) + { + foreach($config['json'] as $id => $json) + $this->_services[$id] = $json; + } + } + else + { + foreach($config->getElementsByTagName('json') as $json) + { + if(($id=$json->getAttribute('id'))!==null) + $this->_services[$id]=$json; + else + throw new TConfigurationException('jsonservice_id_required'); + } + } + } + + /** + * Runs the service. + * This method is invoked by application automatically. + */ + public function run() + { + $id=$this->getRequest()->getServiceParameter(); + if(isset($this->_services[$id])) + { + $serviceConfig=$this->_services[$id]; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($serviceConfig['class'])) + { + $service=Prado::createComponent($serviceConfig['class']); + if($service instanceof TJsonResponse) + { + $properties = isset($serviceConfig['properties'])?$serviceConfig['properties']:array(); + $this->createJsonResponse($service,$properties,$serviceConfig); + } + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + else + { + $properties=$serviceConfig->getAttributes(); + if(($class=$properties->remove('class'))!==null) + { + $service=Prado::createComponent($class); + if($service instanceof TJsonResponse) + $this->createJsonResponse($service,$properties,$serviceConfig); + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + } + else + throw new THttpException(404,'jsonservice_provider_unknown',$id); + } + + /** + * Renders content provided by TJsonResponse::getJsonContent() as + * javascript in JSON format. + */ + protected function createJsonResponse($service,$properties,$config) + { + // init service properties + foreach($properties as $name=>$value) + $service->setSubproperty($name,$value); + $service->init($config); + + //send content if not null + if(($content=$service->getJsonContent())!==null) + { + $response = $this->getResponse(); + $response->setContentType('text/javascript'); + $response->setCharset('UTF-8'); + //send content + $response->write(TJavaScript::jsonEncode($content)); + } + } +} + +/** + * TJsonResponse Class + * + * TJsonResponse is the base class for all JSON response provider classes. + * + * Derived classes must implement {@link getJsonContent()} to return + * an object or literals to be converted to JSON format. The response + * will be empty if the returned content is null. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web.Services + * @since 3.1 + */ +abstract class TJsonResponse extends TApplicationComponent +{ + private $_id=''; + + /** + * Initializes the feed. + * @param TXmlElement configurations specified in {@link TJsonService}. + */ + public function init($config) + { + } + + /** + * @return string ID of this response + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string ID of this response + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return object json response content, null to suppress output. + */ + abstract public function getJsonContent(); +} + +?> diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php index 4576a080..2d74f2a1 100644 --- a/framework/Web/Services/TPageService.php +++ b/framework/Web/Services/TPageService.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.Services - */ - -/** - * Include classes to be used by page service - */ -Prado::using('System.Web.UI.TPage'); -Prado::using('System.Web.UI.TTemplateManager'); -Prado::using('System.Web.UI.TThemeManager'); - -/** - * TPageService class. - * - * TPageService implements the service for serving user page requests. - * - * Pages that are available to client users are stored under a directory specified by - * {@link setBasePath BasePath}. The directory may contain subdirectories. - * Pages serving for a similar goal are usually placed under the same directory. - * A directory may contain a configuration file config.xml whose content - * is similar to that of application configuration file. - * - * A page is requested via page path, which is a dot-connected directory names - * appended by the page name. Assume '/Users/Admin' is the directory - * containing the page 'Update'. Then the page can be requested via 'Users.Admin.Update'. - * By default, the {@link setBasePath BasePath} of the page service is the "pages" - * directory under the application base path. You may change this default - * by setting {@link setBasePath BasePath} with a different path you prefer. - * - * Page name refers to the file name (without extension) of the page template. - * In order to differentiate from the common control template files, the extension - * name of the page template files must be '.page'. If there is a PHP file with - * the same page name under the same directory as the template file, that file - * will be considered as the page class file and the file name is the page class name. - * If such a file is not found, the page class is assumed as {@link TPage}. - * - * Modules can be configured and loaded in page directory configurations. - * Configuration of a module in a subdirectory will overwrite its parent - * directory's configuration, if both configurations refer to the same module. - * - * By default, TPageService will automatically load two modules: - * - {@link TTemplateManager} : manages page and control templates - * - {@link TThemeManager} : manages themes used in a Prado application - * - * In page directory configurations, static authorization rules can also be specified, - * which governs who and which roles can access particular pages. - * Refer to {@link TAuthorizationRule} for more details about authorization rules. - * Page authorization rules can be configured within an tag in - * each page directory configuration as follows, - * - * - * - * - * - * where the 'pages' attribute may be filled with a sequence of comma-separated - * page IDs. If 'pages' attribute does not appear in a rule, the rule will be - * applied to all pages in this directory and all subdirectories (recursively). - * Application of authorization rules are in a bottom-up fashion, starting from - * the directory containing the requested page up to all parent directories. - * The first matching rule will be used. The last rule always allows all users - * accessing to any resources. - * - * @author Qiang Xue - * @author Carl G. Mathisen - * @version $Id$ - * @package System.Web.Services - * @since 3.0 - */ -class TPageService extends TService -{ - /** - * Configuration file name - */ - const CONFIG_FILE_XML='config.xml'; - /** - * Configuration file name - */ - const CONFIG_FILE_PHP='config.php'; - /** - * Default base path - */ - const DEFAULT_BASEPATH='Pages'; - /** - * Fallback base path - used to be the default up to Prado < 3.2 - */ - const FALLBACK_BASEPATH='pages'; - /** - * Prefix of ID used for storing parsed configuration in cache - */ - const CONFIG_CACHE_PREFIX='prado:pageservice:'; - /** - * Page template file extension - */ - const PAGE_FILE_EXT='.page'; - /** - * @var string root path of pages - */ - private $_basePath=null; - /** - * @var string base path class in namespace format - */ - private $_basePageClass='TPage'; - /** - * @var string clientscript manager class in namespace format - * @since 3.1.7 - */ - private $_clientScriptManagerClass='System.Web.UI.TClientScriptManager'; - /** - * @var string default page - */ - private $_defaultPage='Home'; - /** - * @var string requested page (path) - */ - private $_pagePath=null; - /** - * @var TPage the requested page - */ - private $_page=null; - /** - * @var array list of initial page property values - */ - private $_properties=array(); - /** - * @var boolean whether service is initialized - */ - private $_initialized=false; - /** - * @var TThemeManager theme manager - */ - private $_themeManager=null; - /** - * @var TTemplateManager template manager - */ - private $_templateManager=null; - - /** - * Initializes the service. - * This method is required by IService interface and is invoked by application. - * @param TXmlElement service configuration - */ - public function init($config) - { - Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); - - $pageConfig=$this->loadPageConfig($config); - - $this->initPageContext($pageConfig); - - $this->_initialized=true; - } - - /** - * Initializes page context. - * Page context includes path alias settings, namespace usages, - * parameter initialization, module loadings, page initial properties - * and authorization rules. - * @param TPageConfiguration - */ - protected function initPageContext($pageConfig) - { - $application=$this->getApplication(); - foreach($pageConfig->getApplicationConfigurations() as $appConfig) - $application->applyConfiguration($appConfig); - - $this->applyConfiguration($pageConfig); - } - - /** - * Applies a page configuration. - * @param TPageConfiguration the configuration - */ - protected function applyConfiguration($config) - { - // initial page properties (to be set when page runs) - $this->_properties=array_merge($this->_properties, $config->getProperties()); - $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules()); - $pagePath=$this->getRequestedPagePath(); - // external configurations - foreach($config->getExternalConfigurations() as $filePath=>$params) - { - list($configPagePath,$condition)=$params; - if($condition!==true) - $condition=$this->evaluateExpression($condition); - if($condition) - { - if(($path=Prado::getPathOfNamespace($filePath,Prado::getApplication()->getConfigurationFileExt()))===null || !is_file($path)) - throw new TConfigurationException('pageservice_includefile_invalid',$filePath); - $c=new TPageConfiguration($pagePath); - $c->loadFromFile($path,$configPagePath); - $this->applyConfiguration($c); - } - } - - } - - /** - * Determines the requested page path. - * @return string page path requested - */ - protected function determineRequestedPagePath() - { - $pagePath=$this->getRequest()->getServiceParameter(); - if(empty($pagePath)) - $pagePath=$this->getDefaultPage(); - return $pagePath; - } - - /** - * Collects configuration for a page. - * @param TXmlElement additional configuration specified in the application configuration - * @return TPageConfiguration - */ - protected function loadPageConfig($config) - { - $application=$this->getApplication(); - $pagePath=$this->getRequestedPagePath(); - if(($cache=$application->getCache())===null) - { - $pageConfig=new TPageConfiguration($pagePath); - if($config!==null) - { - if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); - else - $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); - } - $pageConfig->loadFromFiles($this->getBasePath()); - } - else - { - $configCached=true; - $currentTimestamp=array(); - $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath); - if(is_array($arr)) - { - list($pageConfig,$timestamps)=$arr; - if($application->getMode()!==TApplicationMode::Performance) - { - foreach($timestamps as $fileName=>$timestamp) - { - if($fileName===0) // application config file - { - $appConfigFile=$application->getConfigurationFile(); - $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); - if($currentTimestamp[0]>$timestamp || ($timestamp>0 && !$currentTimestamp[0])) - $configCached=false; - } - else - { - $currentTimestamp[$fileName]=@filemtime($fileName); - if($currentTimestamp[$fileName]>$timestamp || ($timestamp>0 && !$currentTimestamp[$fileName])) - $configCached=false; - } - } - } - } - else - { - $configCached=false; - $paths=explode('.',$pagePath); - $configPath=$this->getBasePath(); - $fileName = $this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP - ? self::CONFIG_FILE_PHP - : self::CONFIG_FILE_XML; - foreach($paths as $path) - { - $configFile=$configPath.DIRECTORY_SEPARATOR.$fileName; - $currentTimestamp[$configFile]=@filemtime($configFile); - $configPath.=DIRECTORY_SEPARATOR.$path; - } - $appConfigFile=$application->getConfigurationFile(); - $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); - } - if(!$configCached) - { - $pageConfig=new TPageConfiguration($pagePath); - if($config!==null) - { - if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); - else - $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); - } - $pageConfig->loadFromFiles($this->getBasePath()); - $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); - } - } - return $pageConfig; - } - - /** - * @return TTemplateManager template manager - */ - public function getTemplateManager() - { - if(!$this->_templateManager) - { - $this->_templateManager=new TTemplateManager; - $this->_templateManager->init(null); - } - return $this->_templateManager; - } - - /** - * @param TTemplateManager template manager - */ - public function setTemplateManager(TTemplateManager $value) - { - $this->_templateManager=$value; - } - - /** - * @return TThemeManager theme manager - */ - public function getThemeManager() - { - if(!$this->_themeManager) - { - $this->_themeManager=new TThemeManager; - $this->_themeManager->init(null); - } - return $this->_themeManager; - } - - /** - * @param TThemeManager theme manager - */ - public function setThemeManager(TThemeManager $value) - { - $this->_themeManager=$value; - } - - /** - * @return string the requested page path - */ - public function getRequestedPagePath() - { - if($this->_pagePath===null) - { - $this->_pagePath=strtr($this->determineRequestedPagePath(),'/\\','..'); - if(empty($this->_pagePath)) - throw new THttpException(404,'pageservice_page_required'); - } - return $this->_pagePath; - } - - /** - * @return TPage the requested page - */ - public function getRequestedPage() - { - return $this->_page; - } - - /** - * @return string default page path to be served if no explicit page is request. Defaults to 'Home'. - */ - public function getDefaultPage() - { - return $this->_defaultPage; - } - - /** - * @param string default page path to be served if no explicit page is request - * @throws TInvalidOperationException if the page service is initialized - */ - public function setDefaultPage($value) - { - if($this->_initialized) - throw new TInvalidOperationException('pageservice_defaultpage_unchangeable'); - else - $this->_defaultPage=$value; - } - - /** - * @return string the URL for the default page - */ - public function getDefaultPageUrl() - { - return $this->constructUrl($this->getDefaultPage()); - } - - /** - * @return string the root directory for storing pages. Defaults to the 'pages' directory under the application base path. - */ - public function getBasePath() - { - if($this->_basePath===null) - { - $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; - if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) - { - $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::FALLBACK_BASEPATH; - if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) - throw new TConfigurationException('pageservice_basepath_invalid',$basePath); - } - } - return $this->_basePath; - } - - /** - * @param string root directory (in namespace form) storing pages - * @throws TInvalidOperationException if the service is initialized already or basepath is invalid - */ - public function setBasePath($value) - { - if($this->_initialized) - throw new TInvalidOperationException('pageservice_basepath_unchangeable'); - else if(($path=Prado::getPathOfNamespace($value))===null || !is_dir($path)) - throw new TConfigurationException('pageservice_basepath_invalid',$value); - $this->_basePath=realpath($path); - } - - /** - * Sets the base page class name (in namespace format). - * If a page only has a template file without page class file, - * this base page class will be instantiated. - * @param string class name - */ - public function setBasePageClass($value) - { - $this->_basePageClass=$value; - } - - /** - * @return string base page class name in namespace format. Defaults to 'TPage'. - */ - public function getBasePageClass() - { - return $this->_basePageClass; - } - - /** - * Sets the clientscript manager class (in namespace format). - * @param string class name - * @since 3.1.7 - */ - public function setClientScriptManagerClass($value) - { - $this->_clientScriptManagerClass=$value; - } - - /** - * @return string clientscript manager class in namespace format. Defaults to 'System.Web.UI.TClientScriptManager'. - * @since 3.1.7 - */ - public function getClientScriptManagerClass() - { - return $this->_clientScriptManagerClass; - } - - /** - * Runs the service. - * This will create the requested page, initializes it with the property values - * specified in the configuration, and executes the page. - */ - public function run() - { - Prado::trace("Running page service",'System.Web.Services.TPageService'); - $this->_page=$this->createPage($this->getRequestedPagePath()); - $this->runPage($this->_page,$this->_properties); - } - - /** - * Creates a page instance based on requested page path. - * @param string requested page path - * @return TPage the requested page instance - * @throws THttpException if requested page path is invalid - * @throws TConfigurationException if the page class cannot be found - */ - protected function createPage($pagePath) - { - $path=$this->getBasePath().DIRECTORY_SEPARATOR.strtr($pagePath,'.',DIRECTORY_SEPARATOR); - $hasTemplateFile=is_file($path.self::PAGE_FILE_EXT); - $hasClassFile=is_file($path.Prado::CLASS_FILE_EXT); - - if(!$hasTemplateFile && !$hasClassFile) - throw new THttpException(404,'pageservice_page_unknown',$pagePath); - - if($hasClassFile) - { - $className=basename($path); - if(!class_exists($className,false)) - include_once($path.Prado::CLASS_FILE_EXT); - } - else - { - $className=$this->getBasePageClass(); - Prado::using($className); - if(($pos=strrpos($className,'.'))!==false) - $className=substr($className,$pos+1); - } - - if(!class_exists($className,false) || ($className!=='TPage' && !is_subclass_of($className,'TPage'))) - throw new THttpException(404,'pageservice_page_unknown',$pagePath); - - $page=new $className; - $page->setPagePath($pagePath); - - if($hasTemplateFile) - $page->setTemplate($this->getTemplateManager()->getTemplateByFileName($path.self::PAGE_FILE_EXT)); - - return $page; - } - - /** - * Executes a page. - * @param TPage the page instance to be run - * @param array list of initial page properties - */ - protected function runPage($page,$properties) - { - foreach($properties as $name=>$value) - $page->setSubProperty($name,$value); - $page->run($this->getResponse()->createHtmlWriter()); - } - - /** - * Constructs a URL with specified page path and GET parameters. - * @param string page path - * @param array list of GET parameters, null if no GET parameters required - * @param boolean whether to encode the ampersand in URL, defaults to true. - * @param boolean whether to encode the GET parameters (their names and values), defaults to true. - * @return string URL for the page and GET parameters - */ - public function constructUrl($pagePath,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) - { - return $this->getRequest()->constructUrl($this->getID(),$pagePath,$getParams,$encodeAmpersand,$encodeGetItems); - } -} - - -/** - * TPageConfiguration class - * - * TPageConfiguration represents the configuration for a page. - * The page is specified by a dot-connected path. - * Configurations along this path are merged together to be provided for the page. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web.Services - * @since 3.0 - */ -class TPageConfiguration extends TComponent -{ - /** - * @var array list of application configurations - */ - private $_appConfigs=array(); - /** - * @var array list of page initial property values - */ - private $_properties=array(); - /** - * @var TAuthorizationRuleCollection list of authorization rules - */ - private $_rules=array(); - /** - * @var array list of included configurations - */ - private $_includes=array(); - /** - * @var string the currently request page in the format of Path.To.PageName - */ - private $_pagePath=''; - - /** - * Constructor. - * @param string the currently request page in the format of Path.To.PageName - */ - public function __construct($pagePath) - { - $this->_pagePath=$pagePath; - } - - /** - * @return array list of external configuration files. Each element is like $filePath=>$condition - */ - public function getExternalConfigurations() - { - return $this->_includes; - } - - /** - * Returns list of page initial property values. - * Each array element represents a single property with the key - * being the property name and the value the initial property value. - * @return array list of page initial property values - */ - public function getProperties() - { - return $this->_properties; - } - - /** - * Returns list of authorization rules. - * The authorization rules are aggregated (bottom-up) from configuration files - * along the path to the specified page. - * @return TAuthorizationRuleCollection collection of authorization rules - */ - public function getRules() - { - return $this->_rules; - } - - /** - * @return array list of application configurations specified along page path - */ - public function getApplicationConfigurations() - { - return $this->_appConfigs; - } - - /** - * Loads configuration for a page specified in a path format. - * @param string root path for pages - */ - public function loadFromFiles($basePath) - { - $paths=explode('.',$this->_pagePath); - $page=array_pop($paths); - $path=$basePath; - $configPagePath=''; - $fileName = Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP - ? TPageService::CONFIG_FILE_PHP - : TPageService::CONFIG_FILE_XML; - foreach($paths as $p) - { - $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); - $path.=DIRECTORY_SEPARATOR.$p; - if($configPagePath==='') - $configPagePath=$p; - else - $configPagePath.='.'.$p; - } - $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); - $this->_rules=new TAuthorizationRuleCollection($this->_rules); - } - - /** - * Loads a specific config file. - * @param string config file name - * @param string the page path that the config file is associated with. The page path doesn't include the page name. - */ - public function loadFromFile($fname,$configPagePath) - { - Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService'); - if(empty($fname) || !is_file($fname)) - return; - - if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - $fcontent = include $fname; - $this->loadFromPhp($fcontent,dirname($fname),$configPagePath); - } - else - { - $dom=new TXmlDocument; - if($dom->loadFromFile($fname)) - $this->loadFromXml($dom,dirname($fname),$configPagePath); - else - throw new TConfigurationException('pageserviceconf_file_invalid',$fname); - } - } - - public function loadFromPhp($config,$configPath,$configPagePath) - { - $this->loadApplicationConfigurationFromPhp($config,$configPath); - $this->loadPageConfigurationFromPhp($config,$configPath,$configPagePath); - } - - /** - * Loads a page configuration. - * The configuration includes information for both application - * and page service. - * @param TXmlElement config xml element - * @param string the directory containing this configuration - * @param string the page path that the config XML is associated with. The page path doesn't include the page name. - */ - public function loadFromXml($dom,$configPath,$configPagePath) - { - $this->loadApplicationConfigurationFromXml($dom,$configPath); - $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath); - } - - public function loadApplicationConfigurationFromPhp($config,$configPath) - { - $appConfig=new TApplicationConfiguration; - $appConfig->loadFromPhp($config,$configPath); - $this->_appConfigs[]=$appConfig; - } - - /** - * Loads the configuration specific for application part - * @param TXmlElement config xml element - * @param string base path corresponding to this xml element - */ - public function loadApplicationConfigurationFromXml($dom,$configPath) - { - $appConfig=new TApplicationConfiguration; - $appConfig->loadFromXml($dom,$configPath); - $this->_appConfigs[]=$appConfig; - } - - public function loadPageConfigurationFromPhp($config, $configPath, $configPagePath) - { - // authorization - if(isset($config['authorization']) && is_array($config['authorization'])) - { - $rules = array(); - foreach($config['authorization'] as $authorization) - { - $patterns=isset($authorization['pages'])?$authorization['pages']:''; - $ruleApplies=false; - if(empty($patterns) || trim($patterns)==='*') // null or empty string - $ruleApplies=true; - else - { - foreach(explode(',',$patterns) as $pattern) - { - if(($pattern=trim($pattern))!=='') - { - // we know $configPagePath and $this->_pagePath - if($configPagePath!=='') // prepend the pattern with ConfigPagePath - $pattern=$configPagePath.'.'.$pattern; - if(strcasecmp($pattern,$this->_pagePath)===0) - { - $ruleApplies=true; - break; - } - if($pattern[strlen($pattern)-1]==='*') // try wildcard matching - { - if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) - { - $ruleApplies=true; - break; - } - } - } - } - } - if($ruleApplies) - { - $action = isset($authorization['action'])?$authorization['action']:''; - $users = isset($authorization['users'])?$authorization['users']:''; - $roles = isset($authorization['roles'])?$authorization['roles']:''; - $verb = isset($authorization['verb'])?$authorization['verb']:''; - $ips = isset($authorization['ips'])?$authorization['ips']:''; - $rules[]=new TAuthorizationRule($action,$users,$roles,$verb,$ips); - } - } - $this->_rules=array_merge($rules,$this->_rules); - } - // pages - if(isset($config['pages']) && is_array($config['pages'])) - { - if(isset($config['pages']['properties'])) - { - $this->_properties = array_merge($this->_properties, $config['pages']['properties']); - unset($config['pages']['properties']); - } - foreach($config['pages'] as $id => $page) - { - $properties = array(); - if(isset($page['properties'])) - { - $properties=$page['properties']; - unset($page['properties']); - } - $matching=false; - $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; - if(strcasecmp($id,$this->_pagePath)===0) - $matching=true; - else if($id[strlen($id)-1]==='*') // try wildcard matching - $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; - if($matching) - $this->_properties=array_merge($this->_properties,$properties); - } - } - - // external configurations - if(isset($config['includes']) && is_array($config['includes'])) - { - foreach($config['includes'] as $include) - { - $when = isset($include['when'])?true:false; - if(!isset($include['file'])) - throw new TConfigurationException('pageserviceconf_includefile_required'); - $filePath = $include['file']; - if(isset($this->_includes[$filePath])) - $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); - else - $this->_includes[$filePath]=array($configPagePath,$when); - } - } - } - - /** - * Loads the configuration specific for page service. - * @param TXmlElement config xml element - * @param string base path corresponding to this xml element - * @param string the page path that the config XML is associated with. The page path doesn't include the page name. - */ - public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath) - { - // authorization - if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) - { - $rules=array(); - foreach($authorizationNode->getElements() as $node) - { - $patterns=$node->getAttribute('pages'); - $ruleApplies=false; - if(empty($patterns) || trim($patterns)==='*') // null or empty string - $ruleApplies=true; - else - { - foreach(explode(',',$patterns) as $pattern) - { - if(($pattern=trim($pattern))!=='') - { - // we know $configPagePath and $this->_pagePath - if($configPagePath!=='') // prepend the pattern with ConfigPagePath - $pattern=$configPagePath.'.'.$pattern; - if(strcasecmp($pattern,$this->_pagePath)===0) - { - $ruleApplies=true; - break; - } - if($pattern[strlen($pattern)-1]==='*') // try wildcard matching - { - if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) - { - $ruleApplies=true; - break; - } - } - } - } - } - if($ruleApplies) - $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ips')); - } - $this->_rules=array_merge($rules,$this->_rules); - } - - // pages - if(($pagesNode=$dom->getElementByTagName('pages'))!==null) - { - $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); - // at the page folder - foreach($pagesNode->getElementsByTagName('page') as $node) - { - $properties=$node->getAttributes(); - $id=$properties->remove('id'); - if(empty($id)) - throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); - $matching=false; - $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; - if(strcasecmp($id,$this->_pagePath)===0) - $matching=true; - else if($id[strlen($id)-1]==='*') // try wildcard matching - $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; - if($matching) - $this->_properties=array_merge($this->_properties,$properties->toArray()); - } - } - - // external configurations - foreach($dom->getElementsByTagName('include') as $node) - { - if(($when=$node->getAttribute('when'))===null) - $when=true; - if(($filePath=$node->getAttribute('file'))===null) - throw new TConfigurationException('pageserviceconf_includefile_required'); - if(isset($this->_includes[$filePath])) - $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); - else - $this->_includes[$filePath]=array($configPagePath,$when); - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.Services + */ + +/** + * Include classes to be used by page service + */ +Prado::using('System.Web.UI.TPage'); +Prado::using('System.Web.UI.TTemplateManager'); +Prado::using('System.Web.UI.TThemeManager'); + +/** + * TPageService class. + * + * TPageService implements the service for serving user page requests. + * + * Pages that are available to client users are stored under a directory specified by + * {@link setBasePath BasePath}. The directory may contain subdirectories. + * Pages serving for a similar goal are usually placed under the same directory. + * A directory may contain a configuration file config.xml whose content + * is similar to that of application configuration file. + * + * A page is requested via page path, which is a dot-connected directory names + * appended by the page name. Assume '/Users/Admin' is the directory + * containing the page 'Update'. Then the page can be requested via 'Users.Admin.Update'. + * By default, the {@link setBasePath BasePath} of the page service is the "pages" + * directory under the application base path. You may change this default + * by setting {@link setBasePath BasePath} with a different path you prefer. + * + * Page name refers to the file name (without extension) of the page template. + * In order to differentiate from the common control template files, the extension + * name of the page template files must be '.page'. If there is a PHP file with + * the same page name under the same directory as the template file, that file + * will be considered as the page class file and the file name is the page class name. + * If such a file is not found, the page class is assumed as {@link TPage}. + * + * Modules can be configured and loaded in page directory configurations. + * Configuration of a module in a subdirectory will overwrite its parent + * directory's configuration, if both configurations refer to the same module. + * + * By default, TPageService will automatically load two modules: + * - {@link TTemplateManager} : manages page and control templates + * - {@link TThemeManager} : manages themes used in a Prado application + * + * In page directory configurations, static authorization rules can also be specified, + * which governs who and which roles can access particular pages. + * Refer to {@link TAuthorizationRule} for more details about authorization rules. + * Page authorization rules can be configured within an tag in + * each page directory configuration as follows, + * + * + * + * + * + * where the 'pages' attribute may be filled with a sequence of comma-separated + * page IDs. If 'pages' attribute does not appear in a rule, the rule will be + * applied to all pages in this directory and all subdirectories (recursively). + * Application of authorization rules are in a bottom-up fashion, starting from + * the directory containing the requested page up to all parent directories. + * The first matching rule will be used. The last rule always allows all users + * accessing to any resources. + * + * @author Qiang Xue + * @author Carl G. Mathisen + * @version $Id$ + * @package System.Web.Services + * @since 3.0 + */ +class TPageService extends TService +{ + /** + * Configuration file name + */ + const CONFIG_FILE_XML='config.xml'; + /** + * Configuration file name + */ + const CONFIG_FILE_PHP='config.php'; + /** + * Default base path + */ + const DEFAULT_BASEPATH='Pages'; + /** + * Fallback base path - used to be the default up to Prado < 3.2 + */ + const FALLBACK_BASEPATH='pages'; + /** + * Prefix of ID used for storing parsed configuration in cache + */ + const CONFIG_CACHE_PREFIX='prado:pageservice:'; + /** + * Page template file extension + */ + const PAGE_FILE_EXT='.page'; + /** + * @var string root path of pages + */ + private $_basePath=null; + /** + * @var string base path class in namespace format + */ + private $_basePageClass='TPage'; + /** + * @var string clientscript manager class in namespace format + * @since 3.1.7 + */ + private $_clientScriptManagerClass='System.Web.UI.TClientScriptManager'; + /** + * @var string default page + */ + private $_defaultPage='Home'; + /** + * @var string requested page (path) + */ + private $_pagePath=null; + /** + * @var TPage the requested page + */ + private $_page=null; + /** + * @var array list of initial page property values + */ + private $_properties=array(); + /** + * @var boolean whether service is initialized + */ + private $_initialized=false; + /** + * @var TThemeManager theme manager + */ + private $_themeManager=null; + /** + * @var TTemplateManager template manager + */ + private $_templateManager=null; + + /** + * Initializes the service. + * This method is required by IService interface and is invoked by application. + * @param TXmlElement service configuration + */ + public function init($config) + { + Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); + + $pageConfig=$this->loadPageConfig($config); + + $this->initPageContext($pageConfig); + + $this->_initialized=true; + } + + /** + * Initializes page context. + * Page context includes path alias settings, namespace usages, + * parameter initialization, module loadings, page initial properties + * and authorization rules. + * @param TPageConfiguration + */ + protected function initPageContext($pageConfig) + { + $application=$this->getApplication(); + foreach($pageConfig->getApplicationConfigurations() as $appConfig) + $application->applyConfiguration($appConfig); + + $this->applyConfiguration($pageConfig); + } + + /** + * Applies a page configuration. + * @param TPageConfiguration the configuration + */ + protected function applyConfiguration($config) + { + // initial page properties (to be set when page runs) + $this->_properties=array_merge($this->_properties, $config->getProperties()); + $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules()); + $pagePath=$this->getRequestedPagePath(); + // external configurations + foreach($config->getExternalConfigurations() as $filePath=>$params) + { + list($configPagePath,$condition)=$params; + if($condition!==true) + $condition=$this->evaluateExpression($condition); + if($condition) + { + if(($path=Prado::getPathOfNamespace($filePath,Prado::getApplication()->getConfigurationFileExt()))===null || !is_file($path)) + throw new TConfigurationException('pageservice_includefile_invalid',$filePath); + $c=new TPageConfiguration($pagePath); + $c->loadFromFile($path,$configPagePath); + $this->applyConfiguration($c); + } + } + + } + + /** + * Determines the requested page path. + * @return string page path requested + */ + protected function determineRequestedPagePath() + { + $pagePath=$this->getRequest()->getServiceParameter(); + if(empty($pagePath)) + $pagePath=$this->getDefaultPage(); + return $pagePath; + } + + /** + * Collects configuration for a page. + * @param TXmlElement additional configuration specified in the application configuration + * @return TPageConfiguration + */ + protected function loadPageConfig($config) + { + $application=$this->getApplication(); + $pagePath=$this->getRequestedPagePath(); + if(($cache=$application->getCache())===null) + { + $pageConfig=new TPageConfiguration($pagePath); + if($config!==null) + { + if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); + else + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + } + $pageConfig->loadFromFiles($this->getBasePath()); + } + else + { + $configCached=true; + $currentTimestamp=array(); + $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath); + if(is_array($arr)) + { + list($pageConfig,$timestamps)=$arr; + if($application->getMode()!==TApplicationMode::Performance) + { + foreach($timestamps as $fileName=>$timestamp) + { + if($fileName===0) // application config file + { + $appConfigFile=$application->getConfigurationFile(); + $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); + if($currentTimestamp[0]>$timestamp || ($timestamp>0 && !$currentTimestamp[0])) + $configCached=false; + } + else + { + $currentTimestamp[$fileName]=@filemtime($fileName); + if($currentTimestamp[$fileName]>$timestamp || ($timestamp>0 && !$currentTimestamp[$fileName])) + $configCached=false; + } + } + } + } + else + { + $configCached=false; + $paths=explode('.',$pagePath); + $configPath=$this->getBasePath(); + $fileName = $this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP + ? self::CONFIG_FILE_PHP + : self::CONFIG_FILE_XML; + foreach($paths as $path) + { + $configFile=$configPath.DIRECTORY_SEPARATOR.$fileName; + $currentTimestamp[$configFile]=@filemtime($configFile); + $configPath.=DIRECTORY_SEPARATOR.$path; + } + $appConfigFile=$application->getConfigurationFile(); + $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); + } + if(!$configCached) + { + $pageConfig=new TPageConfiguration($pagePath); + if($config!==null) + { + if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); + else + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + } + $pageConfig->loadFromFiles($this->getBasePath()); + $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); + } + } + return $pageConfig; + } + + /** + * @return TTemplateManager template manager + */ + public function getTemplateManager() + { + if(!$this->_templateManager) + { + $this->_templateManager=new TTemplateManager; + $this->_templateManager->init(null); + } + return $this->_templateManager; + } + + /** + * @param TTemplateManager template manager + */ + public function setTemplateManager(TTemplateManager $value) + { + $this->_templateManager=$value; + } + + /** + * @return TThemeManager theme manager + */ + public function getThemeManager() + { + if(!$this->_themeManager) + { + $this->_themeManager=new TThemeManager; + $this->_themeManager->init(null); + } + return $this->_themeManager; + } + + /** + * @param TThemeManager theme manager + */ + public function setThemeManager(TThemeManager $value) + { + $this->_themeManager=$value; + } + + /** + * @return string the requested page path + */ + public function getRequestedPagePath() + { + if($this->_pagePath===null) + { + $this->_pagePath=strtr($this->determineRequestedPagePath(),'/\\','..'); + if(empty($this->_pagePath)) + throw new THttpException(404,'pageservice_page_required'); + } + return $this->_pagePath; + } + + /** + * @return TPage the requested page + */ + public function getRequestedPage() + { + return $this->_page; + } + + /** + * @return string default page path to be served if no explicit page is request. Defaults to 'Home'. + */ + public function getDefaultPage() + { + return $this->_defaultPage; + } + + /** + * @param string default page path to be served if no explicit page is request + * @throws TInvalidOperationException if the page service is initialized + */ + public function setDefaultPage($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_defaultpage_unchangeable'); + else + $this->_defaultPage=$value; + } + + /** + * @return string the URL for the default page + */ + public function getDefaultPageUrl() + { + return $this->constructUrl($this->getDefaultPage()); + } + + /** + * @return string the root directory for storing pages. Defaults to the 'pages' directory under the application base path. + */ + public function getBasePath() + { + if($this->_basePath===null) + { + $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; + if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) + { + $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::FALLBACK_BASEPATH; + if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) + throw new TConfigurationException('pageservice_basepath_invalid',$basePath); + } + } + return $this->_basePath; + } + + /** + * @param string root directory (in namespace form) storing pages + * @throws TInvalidOperationException if the service is initialized already or basepath is invalid + */ + public function setBasePath($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_basepath_unchangeable'); + else if(($path=Prado::getPathOfNamespace($value))===null || !is_dir($path)) + throw new TConfigurationException('pageservice_basepath_invalid',$value); + $this->_basePath=realpath($path); + } + + /** + * Sets the base page class name (in namespace format). + * If a page only has a template file without page class file, + * this base page class will be instantiated. + * @param string class name + */ + public function setBasePageClass($value) + { + $this->_basePageClass=$value; + } + + /** + * @return string base page class name in namespace format. Defaults to 'TPage'. + */ + public function getBasePageClass() + { + return $this->_basePageClass; + } + + /** + * Sets the clientscript manager class (in namespace format). + * @param string class name + * @since 3.1.7 + */ + public function setClientScriptManagerClass($value) + { + $this->_clientScriptManagerClass=$value; + } + + /** + * @return string clientscript manager class in namespace format. Defaults to 'System.Web.UI.TClientScriptManager'. + * @since 3.1.7 + */ + public function getClientScriptManagerClass() + { + return $this->_clientScriptManagerClass; + } + + /** + * Runs the service. + * This will create the requested page, initializes it with the property values + * specified in the configuration, and executes the page. + */ + public function run() + { + Prado::trace("Running page service",'System.Web.Services.TPageService'); + $this->_page=$this->createPage($this->getRequestedPagePath()); + $this->runPage($this->_page,$this->_properties); + } + + /** + * Creates a page instance based on requested page path. + * @param string requested page path + * @return TPage the requested page instance + * @throws THttpException if requested page path is invalid + * @throws TConfigurationException if the page class cannot be found + */ + protected function createPage($pagePath) + { + $path=$this->getBasePath().DIRECTORY_SEPARATOR.strtr($pagePath,'.',DIRECTORY_SEPARATOR); + $hasTemplateFile=is_file($path.self::PAGE_FILE_EXT); + $hasClassFile=is_file($path.Prado::CLASS_FILE_EXT); + + if(!$hasTemplateFile && !$hasClassFile) + throw new THttpException(404,'pageservice_page_unknown',$pagePath); + + if($hasClassFile) + { + $className=basename($path); + if(!class_exists($className,false)) + include_once($path.Prado::CLASS_FILE_EXT); + } + else + { + $className=$this->getBasePageClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + } + + if(!class_exists($className,false) || ($className!=='TPage' && !is_subclass_of($className,'TPage'))) + throw new THttpException(404,'pageservice_page_unknown',$pagePath); + + $page=new $className; + $page->setPagePath($pagePath); + + if($hasTemplateFile) + $page->setTemplate($this->getTemplateManager()->getTemplateByFileName($path.self::PAGE_FILE_EXT)); + + return $page; + } + + /** + * Executes a page. + * @param TPage the page instance to be run + * @param array list of initial page properties + */ + protected function runPage($page,$properties) + { + foreach($properties as $name=>$value) + $page->setSubProperty($name,$value); + $page->run($this->getResponse()->createHtmlWriter()); + } + + /** + * Constructs a URL with specified page path and GET parameters. + * @param string page path + * @param array list of GET parameters, null if no GET parameters required + * @param boolean whether to encode the ampersand in URL, defaults to true. + * @param boolean whether to encode the GET parameters (their names and values), defaults to true. + * @return string URL for the page and GET parameters + */ + public function constructUrl($pagePath,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) + { + return $this->getRequest()->constructUrl($this->getID(),$pagePath,$getParams,$encodeAmpersand,$encodeGetItems); + } +} + + +/** + * TPageConfiguration class + * + * TPageConfiguration represents the configuration for a page. + * The page is specified by a dot-connected path. + * Configurations along this path are merged together to be provided for the page. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web.Services + * @since 3.0 + */ +class TPageConfiguration extends TComponent +{ + /** + * @var array list of application configurations + */ + private $_appConfigs=array(); + /** + * @var array list of page initial property values + */ + private $_properties=array(); + /** + * @var TAuthorizationRuleCollection list of authorization rules + */ + private $_rules=array(); + /** + * @var array list of included configurations + */ + private $_includes=array(); + /** + * @var string the currently request page in the format of Path.To.PageName + */ + private $_pagePath=''; + + /** + * Constructor. + * @param string the currently request page in the format of Path.To.PageName + */ + public function __construct($pagePath) + { + $this->_pagePath=$pagePath; + } + + /** + * @return array list of external configuration files. Each element is like $filePath=>$condition + */ + public function getExternalConfigurations() + { + return $this->_includes; + } + + /** + * Returns list of page initial property values. + * Each array element represents a single property with the key + * being the property name and the value the initial property value. + * @return array list of page initial property values + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * Returns list of authorization rules. + * The authorization rules are aggregated (bottom-up) from configuration files + * along the path to the specified page. + * @return TAuthorizationRuleCollection collection of authorization rules + */ + public function getRules() + { + return $this->_rules; + } + + /** + * @return array list of application configurations specified along page path + */ + public function getApplicationConfigurations() + { + return $this->_appConfigs; + } + + /** + * Loads configuration for a page specified in a path format. + * @param string root path for pages + */ + public function loadFromFiles($basePath) + { + $paths=explode('.',$this->_pagePath); + $page=array_pop($paths); + $path=$basePath; + $configPagePath=''; + $fileName = Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP + ? TPageService::CONFIG_FILE_PHP + : TPageService::CONFIG_FILE_XML; + foreach($paths as $p) + { + $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); + $path.=DIRECTORY_SEPARATOR.$p; + if($configPagePath==='') + $configPagePath=$p; + else + $configPagePath.='.'.$p; + } + $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); + $this->_rules=new TAuthorizationRuleCollection($this->_rules); + } + + /** + * Loads a specific config file. + * @param string config file name + * @param string the page path that the config file is associated with. The page path doesn't include the page name. + */ + public function loadFromFile($fname,$configPagePath) + { + Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService'); + if(empty($fname) || !is_file($fname)) + return; + + if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $fcontent = include $fname; + $this->loadFromPhp($fcontent,dirname($fname),$configPagePath); + } + else + { + $dom=new TXmlDocument; + if($dom->loadFromFile($fname)) + $this->loadFromXml($dom,dirname($fname),$configPagePath); + else + throw new TConfigurationException('pageserviceconf_file_invalid',$fname); + } + } + + public function loadFromPhp($config,$configPath,$configPagePath) + { + $this->loadApplicationConfigurationFromPhp($config,$configPath); + $this->loadPageConfigurationFromPhp($config,$configPath,$configPagePath); + } + + /** + * Loads a page configuration. + * The configuration includes information for both application + * and page service. + * @param TXmlElement config xml element + * @param string the directory containing this configuration + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. + */ + public function loadFromXml($dom,$configPath,$configPagePath) + { + $this->loadApplicationConfigurationFromXml($dom,$configPath); + $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath); + } + + public function loadApplicationConfigurationFromPhp($config,$configPath) + { + $appConfig=new TApplicationConfiguration; + $appConfig->loadFromPhp($config,$configPath); + $this->_appConfigs[]=$appConfig; + } + + /** + * Loads the configuration specific for application part + * @param TXmlElement config xml element + * @param string base path corresponding to this xml element + */ + public function loadApplicationConfigurationFromXml($dom,$configPath) + { + $appConfig=new TApplicationConfiguration; + $appConfig->loadFromXml($dom,$configPath); + $this->_appConfigs[]=$appConfig; + } + + public function loadPageConfigurationFromPhp($config, $configPath, $configPagePath) + { + // authorization + if(isset($config['authorization']) && is_array($config['authorization'])) + { + $rules = array(); + foreach($config['authorization'] as $authorization) + { + $patterns=isset($authorization['pages'])?$authorization['pages']:''; + $ruleApplies=false; + if(empty($patterns) || trim($patterns)==='*') // null or empty string + $ruleApplies=true; + else + { + foreach(explode(',',$patterns) as $pattern) + { + if(($pattern=trim($pattern))!=='') + { + // we know $configPagePath and $this->_pagePath + if($configPagePath!=='') // prepend the pattern with ConfigPagePath + $pattern=$configPagePath.'.'.$pattern; + if(strcasecmp($pattern,$this->_pagePath)===0) + { + $ruleApplies=true; + break; + } + if($pattern[strlen($pattern)-1]==='*') // try wildcard matching + { + if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) + { + $ruleApplies=true; + break; + } + } + } + } + } + if($ruleApplies) + { + $action = isset($authorization['action'])?$authorization['action']:''; + $users = isset($authorization['users'])?$authorization['users']:''; + $roles = isset($authorization['roles'])?$authorization['roles']:''; + $verb = isset($authorization['verb'])?$authorization['verb']:''; + $ips = isset($authorization['ips'])?$authorization['ips']:''; + $rules[]=new TAuthorizationRule($action,$users,$roles,$verb,$ips); + } + } + $this->_rules=array_merge($rules,$this->_rules); + } + // pages + if(isset($config['pages']) && is_array($config['pages'])) + { + if(isset($config['pages']['properties'])) + { + $this->_properties = array_merge($this->_properties, $config['pages']['properties']); + unset($config['pages']['properties']); + } + foreach($config['pages'] as $id => $page) + { + $properties = array(); + if(isset($page['properties'])) + { + $properties=$page['properties']; + unset($page['properties']); + } + $matching=false; + $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; + if(strcasecmp($id,$this->_pagePath)===0) + $matching=true; + else if($id[strlen($id)-1]==='*') // try wildcard matching + $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; + if($matching) + $this->_properties=array_merge($this->_properties,$properties); + } + } + + // external configurations + if(isset($config['includes']) && is_array($config['includes'])) + { + foreach($config['includes'] as $include) + { + $when = isset($include['when'])?true:false; + if(!isset($include['file'])) + throw new TConfigurationException('pageserviceconf_includefile_required'); + $filePath = $include['file']; + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); + else + $this->_includes[$filePath]=array($configPagePath,$when); + } + } + } + + /** + * Loads the configuration specific for page service. + * @param TXmlElement config xml element + * @param string base path corresponding to this xml element + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. + */ + public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath) + { + // authorization + if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) + { + $rules=array(); + foreach($authorizationNode->getElements() as $node) + { + $patterns=$node->getAttribute('pages'); + $ruleApplies=false; + if(empty($patterns) || trim($patterns)==='*') // null or empty string + $ruleApplies=true; + else + { + foreach(explode(',',$patterns) as $pattern) + { + if(($pattern=trim($pattern))!=='') + { + // we know $configPagePath and $this->_pagePath + if($configPagePath!=='') // prepend the pattern with ConfigPagePath + $pattern=$configPagePath.'.'.$pattern; + if(strcasecmp($pattern,$this->_pagePath)===0) + { + $ruleApplies=true; + break; + } + if($pattern[strlen($pattern)-1]==='*') // try wildcard matching + { + if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) + { + $ruleApplies=true; + break; + } + } + } + } + } + if($ruleApplies) + $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ips')); + } + $this->_rules=array_merge($rules,$this->_rules); + } + + // pages + if(($pagesNode=$dom->getElementByTagName('pages'))!==null) + { + $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); + // at the page folder + foreach($pagesNode->getElementsByTagName('page') as $node) + { + $properties=$node->getAttributes(); + $id=$properties->remove('id'); + if(empty($id)) + throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); + $matching=false; + $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; + if(strcasecmp($id,$this->_pagePath)===0) + $matching=true; + else if($id[strlen($id)-1]==='*') // try wildcard matching + $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; + if($matching) + $this->_properties=array_merge($this->_properties,$properties->toArray()); + } + } + + // external configurations + foreach($dom->getElementsByTagName('include') as $node) + { + if(($when=$node->getAttribute('when'))===null) + $when=true; + if(($filePath=$node->getAttribute('file'))===null) + throw new TConfigurationException('pageserviceconf_includefile_required'); + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); + else + $this->_includes[$filePath]=array($configPagePath,$when); + } + } +} + diff --git a/framework/Web/TAssetManager.php b/framework/Web/TAssetManager.php index e4fcbe19..d0aa9b18 100644 --- a/framework/Web/TAssetManager.php +++ b/framework/Web/TAssetManager.php @@ -1,357 +1,357 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web - */ - -/** - * TAssetManager class - * - * TAssetManager provides a scheme to allow web clients visiting - * private files that are normally web-inaccessible. - * - * TAssetManager will copy the file to be published into a web-accessible - * directory. The default base directory for storing the file is "assets", which - * should be under the application directory. This can be changed by setting - * the {@link setBasePath BasePath} property together with the - * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path. - * - * By default, TAssetManager will not publish a file or directory if it already - * exists in the publishing directory and has an older modification time. - * If the application mode is set as 'Performance', the modification time check - * will be skipped. You can explicitly require a modification time check - * with the function {@link publishFilePath}. This is usually - * very useful during development. - * - * TAssetManager may be configured in application configuration file as follows, - * - * - * - * where {@link getBasePath BasePath} and {@link getBaseUrl BaseUrl} are - * configurable properties of TAssetManager. Make sure that BasePath is a namespace - * pointing to a valid directory writable by the Web server process. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class TAssetManager extends TModule -{ - /** - * Default web accessible base path for storing private files - */ - const DEFAULT_BASEPATH='assets'; - /** - * @var string base web accessible path for storing private files - */ - private $_basePath=null; - /** - * @var string base URL for accessing the publishing directory. - */ - private $_baseUrl=null; - /** - * @var boolean whether to use timestamp checking to ensure files are published with up-to-date versions. - */ - private $_checkTimestamp=false; - /** - * @var TApplication application instance - */ - private $_application; - /** - * @var array published assets - */ - private $_published=array(); - /** - * @var boolean whether the module is initialized - */ - private $_initialized=false; - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * @param TXmlElement module configuration - */ - public function init($config) - { - $application=$this->getApplication(); - if($this->_basePath===null) - $this->_basePath=dirname($application->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; - if(!is_writable($this->_basePath) || !is_dir($this->_basePath)) - throw new TConfigurationException('assetmanager_basepath_invalid',$this->_basePath); - if($this->_baseUrl===null) - $this->_baseUrl=rtrim(dirname($application->getRequest()->getApplicationUrl()),'/\\').'/'.self::DEFAULT_BASEPATH; - $application->setAssetManager($this); - $this->_initialized=true; - } - - /** - * @return string the root directory storing published asset files - */ - public function getBasePath() - { - return $this->_basePath; - } - - /** - * Sets the root directory storing published asset files. - * The directory must be in namespace format. - * @param string the root directory storing published asset files - * @throws TInvalidOperationException if the module is initialized already - */ - public function setBasePath($value) - { - if($this->_initialized) - throw new TInvalidOperationException('assetmanager_basepath_unchangeable'); - else - { - $this->_basePath=Prado::getPathOfNamespace($value); - if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath)) - throw new TInvalidDataValueException('assetmanager_basepath_invalid',$value); - } - } - - /** - * @return string the base url that the published asset files can be accessed - */ - public function getBaseUrl() - { - return $this->_baseUrl; - } - - /** - * @param string the base url that the published asset files can be accessed - * @throws TInvalidOperationException if the module is initialized already - */ - public function setBaseUrl($value) - { - if($this->_initialized) - throw new TInvalidOperationException('assetmanager_baseurl_unchangeable'); - else - $this->_baseUrl=rtrim($value,'/'); - } - - /** - * Publishes a file or a directory (recursively). - * This method will copy the content in a directory (recursively) to - * a web accessible directory and returns the URL for the directory. - * If the application is not in performance mode, the file modification - * time will be used to make sure the published file is latest or not. - * If not, a file copy will be performed. - * @param string the path to be published - * @param boolean If true, file modification time will be checked even if the application - * is in performance mode. - * @return string an absolute URL to the published directory - * @throws TInvalidDataValueException if the file path to be published is - * invalid - */ - public function publishFilePath($path,$checkTimestamp=false) - { - if(isset($this->_published[$path])) - return $this->_published[$path]; - else if(empty($path) || ($fullpath=realpath($path))===false) - throw new TInvalidDataValueException('assetmanager_filepath_invalid',$path); - else if(is_file($fullpath)) - { - $dir=$this->hash(dirname($fullpath)); - $fileName=basename($fullpath); - $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; - if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) - $this->copyFile($fullpath,$dst); - return $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.$fileName; - } - else - { - $dir=$this->hash($fullpath); - if(!is_dir($this->_basePath.DIRECTORY_SEPARATOR.$dir) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) - { - Prado::trace("Publishing directory $fullpath",'System.Web.UI.TAssetManager'); - $this->copyDirectory($fullpath,$this->_basePath.DIRECTORY_SEPARATOR.$dir); - } - return $this->_published[$path]=$this->_baseUrl.'/'.$dir; - } - } - - /** - * @return array List of published assets - * @since 3.1.6 - */ - public function getPublished() - { - return $this->_published; - } - - /** - * @param $values List of published assets - * @since 3.1.6 - */ - protected function setPublished($values=array()) - { - $this->_published = $values; - } - - /** - * Returns the published path of a file path. - * This method does not perform any publishing. It merely tells you - * if the file path is published, where it will go. - * @param string directory or file path being published - * @return string the published file path - */ - public function getPublishedPath($path) - { - $path=realpath($path); - if(is_file($path)) - return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash(dirname($path)).DIRECTORY_SEPARATOR.basename($path); - else - return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash($path); - } - - /** - * Returns the URL of a published file path. - * This method does not perform any publishing. It merely tells you - * if the file path is published, what the URL will be to access it. - * @param string directory or file path being published - * @return string the published URL for the file path - */ - public function getPublishedUrl($path) - { - $path=realpath($path); - if(is_file($path)) - return $this->_baseUrl.'/'.$this->hash(dirname($path)).'/'.basename($path); - else - return $this->_baseUrl.'/'.$this->hash($path); - } - - /** - * Generate a CRC32 hash for the directory path. Collisions are higher - * than MD5 but generates a much smaller hash string. - * @param string string to be hashed. - * @return string hashed string. - */ - protected function hash($dir) - { - return sprintf('%x',crc32($dir.Prado::getVersion())); - } - - /** - * Copies a file to a directory. - * Copying is done only when the destination file does not exist - * or has an older file modification time. - * @param string source file path - * @param string destination directory (if not exists, it will be created) - */ - protected function copyFile($src,$dst) - { - if(!is_dir($dst)) - { - @mkdir($dst); - @chmod($dst, PRADO_CHMOD); - } - $dstFile=$dst.DIRECTORY_SEPARATOR.basename($src); - if(@filemtime($dstFile)<@filemtime($src)) - { - Prado::trace("Publishing file $src to $dstFile",'System.Web.TAssetManager'); - @copy($src,$dstFile); - } - } - - /** - * Copies a directory recursively as another. - * If the destination directory does not exist, it will be created. - * File modification time is used to ensure the copied files are latest. - * @param string the source directory - * @param string the destination directory - * @todo a generic solution to ignore certain directories and files - */ - public function copyDirectory($src,$dst) - { - if(!is_dir($dst)) - { - @mkdir($dst); - @chmod($dst, PRADO_CHMOD); - } - if($folder=@opendir($src)) - { - while($file=@readdir($folder)) - { - if($file==='.' || $file==='..' || $file==='.svn') - continue; - else if(is_file($src.DIRECTORY_SEPARATOR.$file)) - { - if(@filemtime($dst.DIRECTORY_SEPARATOR.$file)<@filemtime($src.DIRECTORY_SEPARATOR.$file)) - { - @copy($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); - @chmod($dst.DIRECTORY_SEPARATOR.$file, PRADO_CHMOD); - } - } - else - $this->copyDirectory($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); - } - closedir($folder); - } else { - throw new TInvalidDataValueException('assetmanager_source_directory_invalid', $src); - } - } - - /** - * Publish a tar file by extracting its contents to the assets directory. - * Each tar file must be accomplished with its own MD5 check sum file. - * The MD5 file is published when the tar contents are successfully - * extracted to the assets directory. The presence of the MD5 file - * as published asset assumes that the tar file has already been extracted. - * @param string tar filename - * @param string MD5 checksum for the corresponding tar file. - * @param boolean Wether or not to check the time stamp of the file for publishing. Defaults to false. - * @return string URL path to the directory where the tar file was extracted. - */ - public function publishTarFile($tarfile, $md5sum, $checkTimestamp=false) - { - if(isset($this->_published[$md5sum])) - return $this->_published[$md5sum]; - else if(($fullpath=realpath($md5sum))===false || !is_file($fullpath)) - throw new TInvalidDataValueException('assetmanager_tarchecksum_invalid',$md5sum); - else - { - $dir=$this->hash(dirname($fullpath)); - $fileName=basename($fullpath); - $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; - if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) - { - if(@filemtime($dst.DIRECTORY_SEPARATOR.$fileName)<@filemtime($fullpath)) - { - $this->copyFile($fullpath,$dst); - $this->deployTarFile($tarfile,$dst); - } - } - return $this->_published[$md5sum]=$this->_baseUrl.'/'.$dir; - } - } - - /** - * Extracts the tar file to the destination directory. - * N.B Tar file must not be compressed. - * @param string tar file - * @param string path where the contents of tar file are to be extracted - * @return boolean true if extract successful, false otherwise. - */ - protected function deployTarFile($path,$destination) - { - if(($fullpath=realpath($path))===false || !is_file($fullpath)) - throw new TIOException('assetmanager_tarfile_invalid',$path); - else - { - Prado::using('System.IO.TTarFileExtractor'); - $tar = new TTarFileExtractor($fullpath); - return $tar->extract($destination); - } - } - -} - -?> + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * TAssetManager class + * + * TAssetManager provides a scheme to allow web clients visiting + * private files that are normally web-inaccessible. + * + * TAssetManager will copy the file to be published into a web-accessible + * directory. The default base directory for storing the file is "assets", which + * should be under the application directory. This can be changed by setting + * the {@link setBasePath BasePath} property together with the + * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path. + * + * By default, TAssetManager will not publish a file or directory if it already + * exists in the publishing directory and has an older modification time. + * If the application mode is set as 'Performance', the modification time check + * will be skipped. You can explicitly require a modification time check + * with the function {@link publishFilePath}. This is usually + * very useful during development. + * + * TAssetManager may be configured in application configuration file as follows, + * + * + * + * where {@link getBasePath BasePath} and {@link getBaseUrl BaseUrl} are + * configurable properties of TAssetManager. Make sure that BasePath is a namespace + * pointing to a valid directory writable by the Web server process. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class TAssetManager extends TModule +{ + /** + * Default web accessible base path for storing private files + */ + const DEFAULT_BASEPATH='assets'; + /** + * @var string base web accessible path for storing private files + */ + private $_basePath=null; + /** + * @var string base URL for accessing the publishing directory. + */ + private $_baseUrl=null; + /** + * @var boolean whether to use timestamp checking to ensure files are published with up-to-date versions. + */ + private $_checkTimestamp=false; + /** + * @var TApplication application instance + */ + private $_application; + /** + * @var array published assets + */ + private $_published=array(); + /** + * @var boolean whether the module is initialized + */ + private $_initialized=false; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + $application=$this->getApplication(); + if($this->_basePath===null) + $this->_basePath=dirname($application->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; + if(!is_writable($this->_basePath) || !is_dir($this->_basePath)) + throw new TConfigurationException('assetmanager_basepath_invalid',$this->_basePath); + if($this->_baseUrl===null) + $this->_baseUrl=rtrim(dirname($application->getRequest()->getApplicationUrl()),'/\\').'/'.self::DEFAULT_BASEPATH; + $application->setAssetManager($this); + $this->_initialized=true; + } + + /** + * @return string the root directory storing published asset files + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * Sets the root directory storing published asset files. + * The directory must be in namespace format. + * @param string the root directory storing published asset files + * @throws TInvalidOperationException if the module is initialized already + */ + public function setBasePath($value) + { + if($this->_initialized) + throw new TInvalidOperationException('assetmanager_basepath_unchangeable'); + else + { + $this->_basePath=Prado::getPathOfNamespace($value); + if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath)) + throw new TInvalidDataValueException('assetmanager_basepath_invalid',$value); + } + } + + /** + * @return string the base url that the published asset files can be accessed + */ + public function getBaseUrl() + { + return $this->_baseUrl; + } + + /** + * @param string the base url that the published asset files can be accessed + * @throws TInvalidOperationException if the module is initialized already + */ + public function setBaseUrl($value) + { + if($this->_initialized) + throw new TInvalidOperationException('assetmanager_baseurl_unchangeable'); + else + $this->_baseUrl=rtrim($value,'/'); + } + + /** + * Publishes a file or a directory (recursively). + * This method will copy the content in a directory (recursively) to + * a web accessible directory and returns the URL for the directory. + * If the application is not in performance mode, the file modification + * time will be used to make sure the published file is latest or not. + * If not, a file copy will be performed. + * @param string the path to be published + * @param boolean If true, file modification time will be checked even if the application + * is in performance mode. + * @return string an absolute URL to the published directory + * @throws TInvalidDataValueException if the file path to be published is + * invalid + */ + public function publishFilePath($path,$checkTimestamp=false) + { + if(isset($this->_published[$path])) + return $this->_published[$path]; + else if(empty($path) || ($fullpath=realpath($path))===false) + throw new TInvalidDataValueException('assetmanager_filepath_invalid',$path); + else if(is_file($fullpath)) + { + $dir=$this->hash(dirname($fullpath)); + $fileName=basename($fullpath); + $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; + if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + $this->copyFile($fullpath,$dst); + return $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.$fileName; + } + else + { + $dir=$this->hash($fullpath); + if(!is_dir($this->_basePath.DIRECTORY_SEPARATOR.$dir) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + { + Prado::trace("Publishing directory $fullpath",'System.Web.UI.TAssetManager'); + $this->copyDirectory($fullpath,$this->_basePath.DIRECTORY_SEPARATOR.$dir); + } + return $this->_published[$path]=$this->_baseUrl.'/'.$dir; + } + } + + /** + * @return array List of published assets + * @since 3.1.6 + */ + public function getPublished() + { + return $this->_published; + } + + /** + * @param $values List of published assets + * @since 3.1.6 + */ + protected function setPublished($values=array()) + { + $this->_published = $values; + } + + /** + * Returns the published path of a file path. + * This method does not perform any publishing. It merely tells you + * if the file path is published, where it will go. + * @param string directory or file path being published + * @return string the published file path + */ + public function getPublishedPath($path) + { + $path=realpath($path); + if(is_file($path)) + return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash(dirname($path)).DIRECTORY_SEPARATOR.basename($path); + else + return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash($path); + } + + /** + * Returns the URL of a published file path. + * This method does not perform any publishing. It merely tells you + * if the file path is published, what the URL will be to access it. + * @param string directory or file path being published + * @return string the published URL for the file path + */ + public function getPublishedUrl($path) + { + $path=realpath($path); + if(is_file($path)) + return $this->_baseUrl.'/'.$this->hash(dirname($path)).'/'.basename($path); + else + return $this->_baseUrl.'/'.$this->hash($path); + } + + /** + * Generate a CRC32 hash for the directory path. Collisions are higher + * than MD5 but generates a much smaller hash string. + * @param string string to be hashed. + * @return string hashed string. + */ + protected function hash($dir) + { + return sprintf('%x',crc32($dir.Prado::getVersion())); + } + + /** + * Copies a file to a directory. + * Copying is done only when the destination file does not exist + * or has an older file modification time. + * @param string source file path + * @param string destination directory (if not exists, it will be created) + */ + protected function copyFile($src,$dst) + { + if(!is_dir($dst)) + { + @mkdir($dst); + @chmod($dst, PRADO_CHMOD); + } + $dstFile=$dst.DIRECTORY_SEPARATOR.basename($src); + if(@filemtime($dstFile)<@filemtime($src)) + { + Prado::trace("Publishing file $src to $dstFile",'System.Web.TAssetManager'); + @copy($src,$dstFile); + } + } + + /** + * Copies a directory recursively as another. + * If the destination directory does not exist, it will be created. + * File modification time is used to ensure the copied files are latest. + * @param string the source directory + * @param string the destination directory + * @todo a generic solution to ignore certain directories and files + */ + public function copyDirectory($src,$dst) + { + if(!is_dir($dst)) + { + @mkdir($dst); + @chmod($dst, PRADO_CHMOD); + } + if($folder=@opendir($src)) + { + while($file=@readdir($folder)) + { + if($file==='.' || $file==='..' || $file==='.svn') + continue; + else if(is_file($src.DIRECTORY_SEPARATOR.$file)) + { + if(@filemtime($dst.DIRECTORY_SEPARATOR.$file)<@filemtime($src.DIRECTORY_SEPARATOR.$file)) + { + @copy($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); + @chmod($dst.DIRECTORY_SEPARATOR.$file, PRADO_CHMOD); + } + } + else + $this->copyDirectory($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); + } + closedir($folder); + } else { + throw new TInvalidDataValueException('assetmanager_source_directory_invalid', $src); + } + } + + /** + * Publish a tar file by extracting its contents to the assets directory. + * Each tar file must be accomplished with its own MD5 check sum file. + * The MD5 file is published when the tar contents are successfully + * extracted to the assets directory. The presence of the MD5 file + * as published asset assumes that the tar file has already been extracted. + * @param string tar filename + * @param string MD5 checksum for the corresponding tar file. + * @param boolean Wether or not to check the time stamp of the file for publishing. Defaults to false. + * @return string URL path to the directory where the tar file was extracted. + */ + public function publishTarFile($tarfile, $md5sum, $checkTimestamp=false) + { + if(isset($this->_published[$md5sum])) + return $this->_published[$md5sum]; + else if(($fullpath=realpath($md5sum))===false || !is_file($fullpath)) + throw new TInvalidDataValueException('assetmanager_tarchecksum_invalid',$md5sum); + else + { + $dir=$this->hash(dirname($fullpath)); + $fileName=basename($fullpath); + $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; + if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + { + if(@filemtime($dst.DIRECTORY_SEPARATOR.$fileName)<@filemtime($fullpath)) + { + $this->copyFile($fullpath,$dst); + $this->deployTarFile($tarfile,$dst); + } + } + return $this->_published[$md5sum]=$this->_baseUrl.'/'.$dir; + } + } + + /** + * Extracts the tar file to the destination directory. + * N.B Tar file must not be compressed. + * @param string tar file + * @param string path where the contents of tar file are to be extracted + * @return boolean true if extract successful, false otherwise. + */ + protected function deployTarFile($path,$destination) + { + if(($fullpath=realpath($path))===false || !is_file($fullpath)) + throw new TIOException('assetmanager_tarfile_invalid',$path); + else + { + Prado::using('System.IO.TTarFileExtractor'); + $tar = new TTarFileExtractor($fullpath); + return $tar->extract($destination); + } + } + +} + +?> diff --git a/framework/Web/THttpResponse.php b/framework/Web/THttpResponse.php index 57459362..987bf63b 100644 --- a/framework/Web/THttpResponse.php +++ b/framework/Web/THttpResponse.php @@ -1,719 +1,719 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web - */ - -/** - * Includes the THttpResponse adapter. - */ -Prado::using('System.Web.THttpResponseAdapter'); - -/** - * THttpResponse class - * - * THttpResponse implements the mechanism for sending output to client users. - * - * To output a string to client, use {@link write()}. By default, the output is - * buffered until {@link flush()} is called or the application ends. The output in - * the buffer can also be cleaned by {@link clear()}. To disable output buffering, - * set BufferOutput property to false. - * - * To send cookies to client, use {@link getCookies()}. - * To redirect client browser to a new URL, use {@link redirect()}. - * To send a file to client, use {@link writeFile()}. - * - * By default, THttpResponse is registered with {@link TApplication} as the - * response module. It can be accessed via {@link TApplication::getResponse()}. - * - * THttpResponse may be configured in application configuration file as follows - * - * - * - * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl} - * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse. - * - * THttpResponse sends charset header if either {@link setCharset() Charset} - * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set. - * - * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property. - * - * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode} - * in your application, be sure to add theses informations. - * E.g : to make an http authentication : - * - * public function clickAuth ($sender, $param) - * { - * $response=$this->getResponse(); - * $response->setStatusCode(401); - * $response->appendHeader('WWW-Authenticate: Basic realm="Test"'); - * } - * - * - * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This - * will force the browser to ask for a username and a password. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class THttpResponse extends TModule implements ITextWriter -{ - const DEFAULT_CONTENTTYPE = 'text/html'; - const DEFAULT_CHARSET = 'UTF-8'; - - /** - * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616} - */ - private static $HTTP_STATUS_CODES = array( - 100 => 'Continue', 101 => 'Switching Protocols', - 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', - 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', - 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', - 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' - ); - - /** - * @var boolean whether to buffer output - */ - private $_bufferOutput=true; - /** - * @var boolean if the application is initialized - */ - private $_initialized=false; - /** - * @var THttpCookieCollection list of cookies to return - */ - private $_cookies=null; - /** - * @var integer response status code - */ - private $_status=200; - /** - * @var string reason correspond to status code - */ - private $_reason='OK'; - /** - * @var string HTML writer type - */ - private $_htmlWriterType='System.Web.UI.THtmlWriter'; - /** - * @var string content type - */ - private $_contentType=null; - /** - * @var string|boolean character set, e.g. UTF-8 or false if no character set should be send to client - */ - private $_charset=''; - /** - * @var THttpResponseAdapter adapter. - */ - private $_adapter; - /** - * @var boolean whether http response header has been sent - */ - private $_httpHeaderSent; - /** - * @var boolean whether content-type header has been sent - */ - private $_contentTypeHeaderSent; - - /** - * Destructor. - * Flushes any existing content in buffer. - */ - public function __destruct() - { - //if($this->_bufferOutput) - // @ob_end_flush(); - } - - /** - * @param THttpResponseAdapter response adapter - */ - public function setAdapter(THttpResponseAdapter $adapter) - { - $this->_adapter=$adapter; - } - - /** - * @return THttpResponseAdapter response adapter, null if not exist. - */ - public function getAdapter() - { - return $this->_adapter; - } - - /** - * @return boolean true if adapter exists, false otherwise. - */ - public function getHasAdapter() - { - return $this->_adapter!==null; - } - - /** - * 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) - { - if($this->_bufferOutput) - ob_start(); - $this->_initialized=true; - $this->getApplication()->setResponse($this); - } - - /** - * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180. - */ - public function getCacheExpire() - { - return session_cache_expire(); - } - - /** - * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. - */ - public function setCacheExpire($value) - { - session_cache_expire(TPropertyValue::ensureInteger($value)); - } - - /** - * @return string cache control method to use for session pages - */ - public function getCacheControl() - { - return session_cache_limiter(); - } - - /** - * @param string cache control method to use for session pages. Valid values - * include none/nocache/private/private_no_expire/public - */ - public function setCacheControl($value) - { - session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); - } - - /** - * @return string content type, default is text/html - */ - public function setContentType($type) - { - if ($this->_contentTypeHeaderSent) - throw new Exception('Unable to alter content-type as it has been already sent'); - $this->_contentType = $type; - } - - /** - * @return string current content type - */ - public function getContentType() - { - return $this->_contentType; - } - - /** - * @return string|boolean output charset. - */ - public function getCharset() - { - return $this->_charset; - } - - /** - * @param string|boolean output charset. - */ - public function setCharset($charset) - { - $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset; - } - - /** - * @return boolean whether to enable output buffer - */ - public function getBufferOutput() - { - return $this->_bufferOutput; - } - - /** - * @param boolean whether to enable output buffer - * @throws TInvalidOperationException if session is started already - */ - public function setBufferOutput($value) - { - if($this->_initialized) - throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); - else - $this->_bufferOutput=TPropertyValue::ensureBoolean($value); - } - - /** - * @return integer HTTP status code, defaults to 200 - */ - public function getStatusCode() - { - return $this->_status; - } - - /** - * Set the HTTP status code for the response. - * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion}) - * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1 - * - * @param integer HTTP status code - * @param string HTTP status reason, defaults to standard HTTP reasons - */ - public function setStatusCode($status, $reason=null) - { - if ($this->_httpHeaderSent) - throw new Exception('Unable to alter response as HTTP header already sent'); - $status=TPropertyValue::ensureInteger($status); - if(isset(self::$HTTP_STATUS_CODES[$status])) { - $this->_reason=self::$HTTP_STATUS_CODES[$status]; - }else{ - if($reason===null || $reason==='') { - throw new TInvalidDataValueException("response_status_reason_missing"); - } - $reason=TPropertyValue::ensureString($reason); - if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { - throw new TInvalidDataValueException("response_status_reason_barchars"); - } - $this->_reason=$reason; - } - $this->_status=$status; - } - - /** - * @param string HTTP status reason - */ - public function getStatusReason() { - return $this->_reason; - } - - /** - * @return THttpCookieCollection list of output cookies - */ - public function getCookies() - { - if($this->_cookies===null) - $this->_cookies=new THttpCookieCollection($this); - return $this->_cookies; - } - - /** - * Outputs a string. - * It may not be sent back to user immediately if output buffer is enabled. - * @param string string to be output - */ - public function write($str) - { - // when starting output make sure we send the headers first - if (!$this->_bufferOutput and !$this->_httpHeaderSent) - $this->ensureHeadersSent(); - echo $str; - } - - /** - * Sends a file back to user. - * Make sure not to output anything else after calling this method. - * @param string file name - * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName. - * @param string mime type of the content. - * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain'). - * @param boolean force download of file, even if browser able to display inline. Defaults to 'true'. - * @param string force a specific file name on client side. Defaults to 'null' means auto-detect. - * @param integer size of file or content in bytes if already known. Defaults to 'null' means auto-detect. - * @throws TInvalidDataValueException if the file cannot be found - */ - public function writeFile($fileName,$content=null,$mimeType=null,$headers=null,$forceDownload=true,$clientFileName=null,$fileSize=null) - { - static $defaultMimeTypes=array( - 'css'=>'text/css', - 'gif'=>'image/gif', - 'png'=>'image/png', - 'jpg'=>'image/jpeg', - 'jpeg'=>'image/jpeg', - 'htm'=>'text/html', - 'html'=>'text/html', - 'js'=>'javascript/js', - 'pdf'=>'application/pdf', - 'xls'=>'application/vnd.ms-excel', - ); - - if($mimeType===null) - { - $mimeType='text/plain'; - if(function_exists('mime_content_type')) - $mimeType=mime_content_type($fileName); - else if(($ext=strrchr($fileName,'.'))!==false) - { - $ext=substr($ext,1); - if(isset($defaultMimeTypes[$ext])) - $mimeType=$defaultMimeTypes[$ext]; - } - } - - if($clientFileName===null) - $clientFileName=basename($fileName); - else - $clientFileName=basename($clientFileName); - - if($fileSize===null || $fileSize < 0) - $fileSize = ($content===null?filesize($fileName):strlen($content)); - - $this->sendHttpHeader(); - if(is_array($headers)) - { - foreach($headers as $h) - header($h); - } - else - { - header('Pragma: public'); - header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header("Content-Type: $mimeType"); - $this->_contentTypeHeaderSent = true; - } - - header('Content-Length: '.$fileSize); - header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); - header('Content-Transfer-Encoding: binary'); - if($content===null) - readfile($fileName); - else - echo $content; - } - - /** - * Redirects the browser to the specified URL. - * The current application will be terminated after this method is invoked. - * @param string URL to be redirected to. If the URL is a relative one, the base URL of - * the current request will be inserted at the beginning. - */ - public function redirect($url) - { - if($this->getHasAdapter()) - $this->_adapter->httpRedirect($url); - else - $this->httpRedirect($url); - } - - /** - * Redirect the browser to another URL and exists the current application. - * This method is used internally. Please use {@link redirect} instead. - * - * @since 3.1.5 - * You can set the set {@link setStatusCode StatusCode} to a value between 300 and 399 before - * calling this function to change the type of redirection. - * If not specified, StatusCode will be 302 (Found) by default - * - * @param string URL to be redirected to. If the URL is a relative one, the base URL of - * the current request will be inserted at the beginning. - */ - public function httpRedirect($url) - { - $this->ensureHeadersSent(); - - if($url[0]==='/') - $url=$this->getRequest()->getBaseUrl().$url; - if ($this->_status >= 300 && $this->_status < 400) - // The status code has been modified to a valid redirection status, send it - header('Location: '.str_replace('&','&',$url), true, $this->_status); - else - header('Location: '.str_replace('&','&',$url)); - - if(!$this->getApplication()->getRequestCompleted()) - $this->getApplication()->onEndRequest(); - - exit(); - } - - /** - * Reloads the current page. - * The effect of this method call is the same as user pressing the - * refresh button on his browser (without post data). - **/ - public function reload() - { - $this->redirect($this->getRequest()->getRequestUri()); - } - - /** - * Flush the response contents and headers. - */ - public function flush($continueBuffering = true) - { - if($this->getHasAdapter()) - $this->_adapter->flushContent($continueBuffering); - else - $this->flushContent($continueBuffering); - } - - /** - * Ensures that HTTP response and content-type headers are sent - */ - public function ensureHeadersSent() - { - $this->ensureHttpHeaderSent(); - $this->ensureContentTypeHeaderSent(); - } - - /** - * Outputs the buffered content, sends content-type and charset header. - * This method is used internally. Please use {@link flush} instead. - * @param boolean whether to continue buffering after flush if buffering was active - */ - public function flushContent($continueBuffering = true) - { - Prado::trace("Flushing output",'System.Web.THttpResponse'); - $this->ensureHeadersSent(); - if($this->_bufferOutput) - { - // avoid forced send of http headers (ob_flush() does that) if there's no output yet - if (ob_get_length()>0) - { - if (!$continueBuffering) - { - $this->_bufferOutput = false; - ob_end_flush(); - } - else - ob_flush(); - flush(); - } - } - else - flush(); - } - - /** - * Ensures that the HTTP header with the status code and status reason are sent - */ - protected function ensureHttpHeaderSent() - { - if (!$this->_httpHeaderSent) - $this->sendHttpHeader(); - } - - /** - * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK) - */ - protected function sendHttpHeader() - { - if (($version=$this->getRequest()->getHttpProtocolVersion())==='') - header (' ', true, $this->_status); - else - header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status); - $this->_httpHeaderSent = true; - } - - /** - * Ensures that the HTTP header with the status code and status reason are sent - */ - protected function ensureContentTypeHeaderSent() - { - if (!$this->_contentTypeHeaderSent) - $this->sendContentTypeHeader(); - } - - /** - * Sends content type header with optional charset. - */ - protected function sendContentTypeHeader() - { - $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType; - $charset=$this->getCharset(); - if($charset === false) { - $this->appendHeader('Content-Type: '.$contentType); - return; - } - - if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) - $charset=$globalization->getCharset(); - - if($charset==='') $charset = self::DEFAULT_CHARSET; - $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); - - $this->_contentTypeHeaderSent = true; - } - - /** - * Returns the content in the output buffer. - * The buffer will NOT be cleared after calling this method. - * Use {@link clear()} is you want to clear the buffer. - * @return string output that is in the buffer. - */ - public function getContents() - { - Prado::trace("Retrieving output",'System.Web.THttpResponse'); - return $this->_bufferOutput?ob_get_contents():''; - } - - /** - * Clears any existing buffered content. - */ - public function clear() - { - if($this->_bufferOutput) - ob_clean(); - Prado::trace("Clearing output",'System.Web.THttpResponse'); - } - - /** - * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default) - * @return array - */ - public function getHeaders($case=null) - { - $result = array(); - $headers = headers_list(); - foreach($headers as $header) { - $tmp = explode(':', $header); - $key = trim(array_shift($tmp)); - $value = trim(implode(':', $tmp)); - if(isset($result[$key])) - $result[$key] .= ', ' . $value; - else - $result[$key] = $value; - } - - if($case !== null) - return array_change_key_case($result, $case); - - return $result; - } - - /** - * Sends a header. - * @param string header - * @param boolean whether the header should replace a previous similar header, or add a second header of the same type - */ - public function appendHeader($value, $replace=true) - { - Prado::trace("Sending header '$value'",'System.Web.THttpResponse'); - header($value, $replace); - } - - /** - * Writes a log message into error log. - * This method is simple wrapper of PHP function error_log. - * @param string The error message that should be logged - * @param integer where the error should go - * @param string The destination. Its meaning depends on the message parameter as described above - * @param string The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does. - * @see http://us2.php.net/manual/en/function.error-log.php - */ - public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') - { - error_log($message,$messageType,$destination,$extraHeaders); - } - - /** - * Sends a cookie. - * Do not call this method directly. Operate with the result of {@link getCookies} instead. - * @param THttpCookie cook to be sent - */ - public function addCookie($cookie) - { - $request=$this->getRequest(); - if($request->getEnableCookieValidation()) - { - $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); - setcookie( - $cookie->getName(), - $value, - $cookie->getExpire(), - $cookie->getPath(), - $cookie->getDomain(), - $cookie->getSecure(), - $cookie->getHttpOnly() - ); - } - else { - setcookie( - $cookie->getName(), - $cookie->getValue(), - $cookie->getExpire(), - $cookie->getPath(), - $cookie->getDomain(), - $cookie->getSecure(), - $cookie->getHttpOnly() - ); - } - } - - /** - * Deletes a cookie. - * Do not call this method directly. Operate with the result of {@link getCookies} instead. - * @param THttpCookie cook to be deleted - */ - public function removeCookie($cookie) - { - setcookie( - $cookie->getName(), - null, - 0, - $cookie->getPath(), - $cookie->getDomain(), - $cookie->getSecure(), - $cookie->getHttpOnly() - ); - } - - /** - * @return string the type of HTML writer to be used, defaults to THtmlWriter - */ - public function getHtmlWriterType() - { - return $this->_htmlWriterType; - } - - /** - * @param string the type of HTML writer to be used, may be the class name or the namespace - */ - public function setHtmlWriterType($value) - { - $this->_htmlWriterType=$value; - } - - /** - * Creates a new instance of HTML writer. - * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed. - * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed. - */ - public function createHtmlWriter($type=null) - { - if($type===null) - $type=$this->getHtmlWriterType(); - if($this->getHasAdapter()) - return $this->_adapter->createNewHtmlWriter($type, $this); - else - return $this->createNewHtmlWriter($type, $this); - } - - /** - * Create a new html writer instance. - * This method is used internally. Please use {@link createHtmlWriter} instead. - * @param string type of HTML writer to be created. - * @param ITextWriter text writer holding the contents. - */ - public function createNewHtmlWriter($type, $writer) - { - return Prado::createComponent($type, $writer); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * Includes the THttpResponse adapter. + */ +Prado::using('System.Web.THttpResponseAdapter'); + +/** + * THttpResponse class + * + * THttpResponse implements the mechanism for sending output to client users. + * + * To output a string to client, use {@link write()}. By default, the output is + * buffered until {@link flush()} is called or the application ends. The output in + * the buffer can also be cleaned by {@link clear()}. To disable output buffering, + * set BufferOutput property to false. + * + * To send cookies to client, use {@link getCookies()}. + * To redirect client browser to a new URL, use {@link redirect()}. + * To send a file to client, use {@link writeFile()}. + * + * By default, THttpResponse is registered with {@link TApplication} as the + * response module. It can be accessed via {@link TApplication::getResponse()}. + * + * THttpResponse may be configured in application configuration file as follows + * + * + * + * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl} + * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse. + * + * THttpResponse sends charset header if either {@link setCharset() Charset} + * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set. + * + * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property. + * + * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode} + * in your application, be sure to add theses informations. + * E.g : to make an http authentication : + * + * public function clickAuth ($sender, $param) + * { + * $response=$this->getResponse(); + * $response->setStatusCode(401); + * $response->appendHeader('WWW-Authenticate: Basic realm="Test"'); + * } + * + * + * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This + * will force the browser to ask for a username and a password. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class THttpResponse extends TModule implements ITextWriter +{ + const DEFAULT_CONTENTTYPE = 'text/html'; + const DEFAULT_CHARSET = 'UTF-8'; + + /** + * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616} + */ + private static $HTTP_STATUS_CODES = array( + 100 => 'Continue', 101 => 'Switching Protocols', + 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', + 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', + 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', + 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + ); + + /** + * @var boolean whether to buffer output + */ + private $_bufferOutput=true; + /** + * @var boolean if the application is initialized + */ + private $_initialized=false; + /** + * @var THttpCookieCollection list of cookies to return + */ + private $_cookies=null; + /** + * @var integer response status code + */ + private $_status=200; + /** + * @var string reason correspond to status code + */ + private $_reason='OK'; + /** + * @var string HTML writer type + */ + private $_htmlWriterType='System.Web.UI.THtmlWriter'; + /** + * @var string content type + */ + private $_contentType=null; + /** + * @var string|boolean character set, e.g. UTF-8 or false if no character set should be send to client + */ + private $_charset=''; + /** + * @var THttpResponseAdapter adapter. + */ + private $_adapter; + /** + * @var boolean whether http response header has been sent + */ + private $_httpHeaderSent; + /** + * @var boolean whether content-type header has been sent + */ + private $_contentTypeHeaderSent; + + /** + * Destructor. + * Flushes any existing content in buffer. + */ + public function __destruct() + { + //if($this->_bufferOutput) + // @ob_end_flush(); + } + + /** + * @param THttpResponseAdapter response adapter + */ + public function setAdapter(THttpResponseAdapter $adapter) + { + $this->_adapter=$adapter; + } + + /** + * @return THttpResponseAdapter response adapter, null if not exist. + */ + public function getAdapter() + { + return $this->_adapter; + } + + /** + * @return boolean true if adapter exists, false otherwise. + */ + public function getHasAdapter() + { + return $this->_adapter!==null; + } + + /** + * 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) + { + if($this->_bufferOutput) + ob_start(); + $this->_initialized=true; + $this->getApplication()->setResponse($this); + } + + /** + * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180. + */ + public function getCacheExpire() + { + return session_cache_expire(); + } + + /** + * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. + */ + public function setCacheExpire($value) + { + session_cache_expire(TPropertyValue::ensureInteger($value)); + } + + /** + * @return string cache control method to use for session pages + */ + public function getCacheControl() + { + return session_cache_limiter(); + } + + /** + * @param string cache control method to use for session pages. Valid values + * include none/nocache/private/private_no_expire/public + */ + public function setCacheControl($value) + { + session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); + } + + /** + * @return string content type, default is text/html + */ + public function setContentType($type) + { + if ($this->_contentTypeHeaderSent) + throw new Exception('Unable to alter content-type as it has been already sent'); + $this->_contentType = $type; + } + + /** + * @return string current content type + */ + public function getContentType() + { + return $this->_contentType; + } + + /** + * @return string|boolean output charset. + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * @param string|boolean output charset. + */ + public function setCharset($charset) + { + $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset; + } + + /** + * @return boolean whether to enable output buffer + */ + public function getBufferOutput() + { + return $this->_bufferOutput; + } + + /** + * @param boolean whether to enable output buffer + * @throws TInvalidOperationException if session is started already + */ + public function setBufferOutput($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); + else + $this->_bufferOutput=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer HTTP status code, defaults to 200 + */ + public function getStatusCode() + { + return $this->_status; + } + + /** + * Set the HTTP status code for the response. + * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion}) + * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1 + * + * @param integer HTTP status code + * @param string HTTP status reason, defaults to standard HTTP reasons + */ + public function setStatusCode($status, $reason=null) + { + if ($this->_httpHeaderSent) + throw new Exception('Unable to alter response as HTTP header already sent'); + $status=TPropertyValue::ensureInteger($status); + if(isset(self::$HTTP_STATUS_CODES[$status])) { + $this->_reason=self::$HTTP_STATUS_CODES[$status]; + }else{ + if($reason===null || $reason==='') { + throw new TInvalidDataValueException("response_status_reason_missing"); + } + $reason=TPropertyValue::ensureString($reason); + if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { + throw new TInvalidDataValueException("response_status_reason_barchars"); + } + $this->_reason=$reason; + } + $this->_status=$status; + } + + /** + * @param string HTTP status reason + */ + public function getStatusReason() { + return $this->_reason; + } + + /** + * @return THttpCookieCollection list of output cookies + */ + public function getCookies() + { + if($this->_cookies===null) + $this->_cookies=new THttpCookieCollection($this); + return $this->_cookies; + } + + /** + * Outputs a string. + * It may not be sent back to user immediately if output buffer is enabled. + * @param string string to be output + */ + public function write($str) + { + // when starting output make sure we send the headers first + if (!$this->_bufferOutput and !$this->_httpHeaderSent) + $this->ensureHeadersSent(); + echo $str; + } + + /** + * Sends a file back to user. + * Make sure not to output anything else after calling this method. + * @param string file name + * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName. + * @param string mime type of the content. + * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain'). + * @param boolean force download of file, even if browser able to display inline. Defaults to 'true'. + * @param string force a specific file name on client side. Defaults to 'null' means auto-detect. + * @param integer size of file or content in bytes if already known. Defaults to 'null' means auto-detect. + * @throws TInvalidDataValueException if the file cannot be found + */ + public function writeFile($fileName,$content=null,$mimeType=null,$headers=null,$forceDownload=true,$clientFileName=null,$fileSize=null) + { + static $defaultMimeTypes=array( + 'css'=>'text/css', + 'gif'=>'image/gif', + 'png'=>'image/png', + 'jpg'=>'image/jpeg', + 'jpeg'=>'image/jpeg', + 'htm'=>'text/html', + 'html'=>'text/html', + 'js'=>'javascript/js', + 'pdf'=>'application/pdf', + 'xls'=>'application/vnd.ms-excel', + ); + + if($mimeType===null) + { + $mimeType='text/plain'; + if(function_exists('mime_content_type')) + $mimeType=mime_content_type($fileName); + else if(($ext=strrchr($fileName,'.'))!==false) + { + $ext=substr($ext,1); + if(isset($defaultMimeTypes[$ext])) + $mimeType=$defaultMimeTypes[$ext]; + } + } + + if($clientFileName===null) + $clientFileName=basename($fileName); + else + $clientFileName=basename($clientFileName); + + if($fileSize===null || $fileSize < 0) + $fileSize = ($content===null?filesize($fileName):strlen($content)); + + $this->sendHttpHeader(); + if(is_array($headers)) + { + foreach($headers as $h) + header($h); + } + else + { + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-Type: $mimeType"); + $this->_contentTypeHeaderSent = true; + } + + header('Content-Length: '.$fileSize); + header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); + header('Content-Transfer-Encoding: binary'); + if($content===null) + readfile($fileName); + else + echo $content; + } + + /** + * Redirects the browser to the specified URL. + * The current application will be terminated after this method is invoked. + * @param string URL to be redirected to. If the URL is a relative one, the base URL of + * the current request will be inserted at the beginning. + */ + public function redirect($url) + { + if($this->getHasAdapter()) + $this->_adapter->httpRedirect($url); + else + $this->httpRedirect($url); + } + + /** + * Redirect the browser to another URL and exists the current application. + * This method is used internally. Please use {@link redirect} instead. + * + * @since 3.1.5 + * You can set the set {@link setStatusCode StatusCode} to a value between 300 and 399 before + * calling this function to change the type of redirection. + * If not specified, StatusCode will be 302 (Found) by default + * + * @param string URL to be redirected to. If the URL is a relative one, the base URL of + * the current request will be inserted at the beginning. + */ + public function httpRedirect($url) + { + $this->ensureHeadersSent(); + + if($url[0]==='/') + $url=$this->getRequest()->getBaseUrl().$url; + if ($this->_status >= 300 && $this->_status < 400) + // The status code has been modified to a valid redirection status, send it + header('Location: '.str_replace('&','&',$url), true, $this->_status); + else + header('Location: '.str_replace('&','&',$url)); + + if(!$this->getApplication()->getRequestCompleted()) + $this->getApplication()->onEndRequest(); + + exit(); + } + + /** + * Reloads the current page. + * The effect of this method call is the same as user pressing the + * refresh button on his browser (without post data). + **/ + public function reload() + { + $this->redirect($this->getRequest()->getRequestUri()); + } + + /** + * Flush the response contents and headers. + */ + public function flush($continueBuffering = true) + { + if($this->getHasAdapter()) + $this->_adapter->flushContent($continueBuffering); + else + $this->flushContent($continueBuffering); + } + + /** + * Ensures that HTTP response and content-type headers are sent + */ + public function ensureHeadersSent() + { + $this->ensureHttpHeaderSent(); + $this->ensureContentTypeHeaderSent(); + } + + /** + * Outputs the buffered content, sends content-type and charset header. + * This method is used internally. Please use {@link flush} instead. + * @param boolean whether to continue buffering after flush if buffering was active + */ + public function flushContent($continueBuffering = true) + { + Prado::trace("Flushing output",'System.Web.THttpResponse'); + $this->ensureHeadersSent(); + if($this->_bufferOutput) + { + // avoid forced send of http headers (ob_flush() does that) if there's no output yet + if (ob_get_length()>0) + { + if (!$continueBuffering) + { + $this->_bufferOutput = false; + ob_end_flush(); + } + else + ob_flush(); + flush(); + } + } + else + flush(); + } + + /** + * Ensures that the HTTP header with the status code and status reason are sent + */ + protected function ensureHttpHeaderSent() + { + if (!$this->_httpHeaderSent) + $this->sendHttpHeader(); + } + + /** + * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK) + */ + protected function sendHttpHeader() + { + if (($version=$this->getRequest()->getHttpProtocolVersion())==='') + header (' ', true, $this->_status); + else + header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status); + $this->_httpHeaderSent = true; + } + + /** + * Ensures that the HTTP header with the status code and status reason are sent + */ + protected function ensureContentTypeHeaderSent() + { + if (!$this->_contentTypeHeaderSent) + $this->sendContentTypeHeader(); + } + + /** + * Sends content type header with optional charset. + */ + protected function sendContentTypeHeader() + { + $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType; + $charset=$this->getCharset(); + if($charset === false) { + $this->appendHeader('Content-Type: '.$contentType); + return; + } + + if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) + $charset=$globalization->getCharset(); + + if($charset==='') $charset = self::DEFAULT_CHARSET; + $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); + + $this->_contentTypeHeaderSent = true; + } + + /** + * Returns the content in the output buffer. + * The buffer will NOT be cleared after calling this method. + * Use {@link clear()} is you want to clear the buffer. + * @return string output that is in the buffer. + */ + public function getContents() + { + Prado::trace("Retrieving output",'System.Web.THttpResponse'); + return $this->_bufferOutput?ob_get_contents():''; + } + + /** + * Clears any existing buffered content. + */ + public function clear() + { + if($this->_bufferOutput) + ob_clean(); + Prado::trace("Clearing output",'System.Web.THttpResponse'); + } + + /** + * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default) + * @return array + */ + public function getHeaders($case=null) + { + $result = array(); + $headers = headers_list(); + foreach($headers as $header) { + $tmp = explode(':', $header); + $key = trim(array_shift($tmp)); + $value = trim(implode(':', $tmp)); + if(isset($result[$key])) + $result[$key] .= ', ' . $value; + else + $result[$key] = $value; + } + + if($case !== null) + return array_change_key_case($result, $case); + + return $result; + } + + /** + * Sends a header. + * @param string header + * @param boolean whether the header should replace a previous similar header, or add a second header of the same type + */ + public function appendHeader($value, $replace=true) + { + Prado::trace("Sending header '$value'",'System.Web.THttpResponse'); + header($value, $replace); + } + + /** + * Writes a log message into error log. + * This method is simple wrapper of PHP function error_log. + * @param string The error message that should be logged + * @param integer where the error should go + * @param string The destination. Its meaning depends on the message parameter as described above + * @param string The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does. + * @see http://us2.php.net/manual/en/function.error-log.php + */ + public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') + { + error_log($message,$messageType,$destination,$extraHeaders); + } + + /** + * Sends a cookie. + * Do not call this method directly. Operate with the result of {@link getCookies} instead. + * @param THttpCookie cook to be sent + */ + public function addCookie($cookie) + { + $request=$this->getRequest(); + if($request->getEnableCookieValidation()) + { + $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); + setcookie( + $cookie->getName(), + $value, + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + else { + setcookie( + $cookie->getName(), + $cookie->getValue(), + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + } + + /** + * Deletes a cookie. + * Do not call this method directly. Operate with the result of {@link getCookies} instead. + * @param THttpCookie cook to be deleted + */ + public function removeCookie($cookie) + { + setcookie( + $cookie->getName(), + null, + 0, + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + + /** + * @return string the type of HTML writer to be used, defaults to THtmlWriter + */ + public function getHtmlWriterType() + { + return $this->_htmlWriterType; + } + + /** + * @param string the type of HTML writer to be used, may be the class name or the namespace + */ + public function setHtmlWriterType($value) + { + $this->_htmlWriterType=$value; + } + + /** + * Creates a new instance of HTML writer. + * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed. + * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed. + */ + public function createHtmlWriter($type=null) + { + if($type===null) + $type=$this->getHtmlWriterType(); + if($this->getHasAdapter()) + return $this->_adapter->createNewHtmlWriter($type, $this); + else + return $this->createNewHtmlWriter($type, $this); + } + + /** + * Create a new html writer instance. + * This method is used internally. Please use {@link createHtmlWriter} instead. + * @param string type of HTML writer to be created. + * @param ITextWriter text writer holding the contents. + */ + public function createNewHtmlWriter($type, $writer) + { + return Prado::createComponent($type, $writer); + } +} + diff --git a/framework/Web/THttpSession.php b/framework/Web/THttpSession.php index 762a87f7..55d5f8b6 100644 --- a/framework/Web/THttpSession.php +++ b/framework/Web/THttpSession.php @@ -1,730 +1,730 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web - */ - -/** - * THttpSession class - * - * THttpSession provides session-level data management and the related configurations. - * To start the session, call {@link open}; to complete and send out session data, call {@link close}; - * to destroy the session, call {@link destroy}. If AutoStart is true, then the session - * will be started once the session module is loaded and initialized. - * - * To access data stored in session, use THttpSession like an associative array. For example, - * - * $session=new THttpSession; - * $session->open(); - * $value1=$session['name1']; // get session variable 'name1' - * $value2=$session['name2']; // get session variable 'name2' - * foreach($session as $name=>$value) // traverse all session variables - * $session['name3']=$value3; // set session variable 'name3' - * - * - * The following configurations are available for session: - * {@link setAutoStart AutoStart}, {@link setCookieMode CookieMode}, - * {@link setSavePath SavePath}, - * {@link setUseCustomStorage UseCustomStorage}, {@link setGCProbability GCProbability}, - * {@link setTimeout Timeout}. - * See the corresponding setter and getter documentation for more information. - * Note, these properties must be set before the session is started. - * - * THttpSession can be inherited with customized session storage method. - * Override {@link _open}, {@link _close}, {@link _read}, {@link _write}, {@link _destroy} and {@link _gc} - * and set {@link setUseCustomStorage UseCustomStorage} to true. - * Then, the session data will be stored using the above methods. - * - * By default, THttpSession is registered with {@link TApplication} as the - * request module. It can be accessed via {@link TApplication::getSession()}. - * - * THttpSession may be configured in application configuration file as follows, - * - * - * - * where {@link getSessionName SessionName}, {@link getSavePath SavePath}, - * {@link getCookieMode CookieMode}, {@link getUseCustomStorage - * UseCustomStorage}, {@link getAutoStart AutoStart}, {@link getGCProbability - * GCProbability}, {@link getUseTransparentSessionID UseTransparentSessionID} - * and {@link getTimeout TimeOut} are configurable properties of THttpSession. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule -{ - /** - * @var boolean whether this module has been initialized - */ - private $_initialized=false; - /** - * @var boolean whether the session has started - */ - private $_started=false; - /** - * @var boolean whether the session should be started when the module is initialized - */ - private $_autoStart=false; - /** - * @var THttpCookie cookie to be used to store session ID and other data - */ - private $_cookie=null; - /** - * @var string module id - */ - private $_id; - /** - * @var boolean - */ - private $_customStorage=false; - - /** - * @return string id of this module - */ - public function getID() - { - return $this->_id; - } - - /** - * @param string id of this module - */ - public function setID($value) - { - $this->_id=$value; - } - - /** - * Initializes the module. - * This method is required by IModule. - * If AutoStart is true, the session will be started. - * @param TXmlElement module configuration - */ - public function init($config) - { - if($this->_autoStart) - $this->open(); - $this->_initialized=true; - $this->getApplication()->setSession($this); - register_shutdown_function(array($this, "close")); - } - - /** - * Starts the session if it has not started yet. - */ - public function open() - { - if(!$this->_started) - { - if($this->_customStorage) - session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc')); - if($this->_cookie!==null) - session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure()); - if(ini_get('session.auto_start')!=='1') - session_start(); - $this->_started=true; - } - } - - /** - * Ends the current session and store session data. - */ - public function close() - { - if($this->_started) - { - session_write_close(); - $this->_started=false; - } - } - - /** - * Destroys all data registered to a session. - */ - public function destroy() - { - if($this->_started) - { - session_destroy(); - $this->_started=false; - } - } - - /** - * Update the current session id with a newly generated one - * - * @param boolean $deleteOld Whether to delete the old associated session or not. - * @return string old session id - * @link http://php.net/manual/en/function.session-regenerate-id.php - */ - public function regenerate($deleteOld=false) - { - $old = $this->getSessionID(); - session_regenerate_id($deleteOld); - return $old; - } - - /** - * @return boolean whether the session has started - */ - public function getIsStarted() - { - return $this->_started; - } - - /** - * @return string the current session ID - */ - public function getSessionID() - { - return session_id(); - } - - /** - * @param string the session ID for the current session - * @throws TInvalidOperationException if session is started already - */ - public function setSessionID($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_sessionid_unchangeable'); - else - session_id($value); - } - - /** - * @return string the current session name - */ - public function getSessionName() - { - return session_name(); - } - - /** - * @param string the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID - * @throws TInvalidOperationException if session is started already - */ - public function setSessionName($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_sessionname_unchangeable'); - else if(ctype_alnum($value)) - session_name($value); - else - throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value); - } - - /** - * @return string the current session save path, defaults to '/tmp'. - */ - public function getSavePath() - { - return session_save_path(); - } - - /** - * @param string the current session save path - * @throws TInvalidOperationException if session is started already - */ - public function setSavePath($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_savepath_unchangeable'); - else if(is_dir($value)) - session_save_path($value); - else - throw new TInvalidDataValueException('httpsession_savepath_invalid',$value); - } - - /** - * @return boolean whether to use user-specified handlers to store session data. Defaults to false. - */ - public function getUseCustomStorage() - { - return $this->_customStorage; - } - - /** - * @param boolean whether to use user-specified handlers to store session data. - * If true, make sure the methods {@link _open}, {@link _close}, {@link _read}, - * {@link _write}, {@link _destroy}, and {@link _gc} are overridden in child - * class, because they will be used as the callback handlers. - */ - public function setUseCustomStorage($value) - { - $this->_customStorage=TPropertyValue::ensureBoolean($value); - } - - /** - * @return THttpCookie cookie that will be used to store session ID - */ - public function getCookie() - { - if($this->_cookie===null) - $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID()); - return $this->_cookie; - } - - /** - * @return THttpSessionCookieMode how to use cookie to store session ID. Defaults to THttpSessionCookieMode::Allow. - */ - public function getCookieMode() - { - if(ini_get('session.use_cookies')==='0') - return THttpSessionCookieMode::None; - else if(ini_get('session.use_only_cookies')==='0') - return THttpSessionCookieMode::Allow; - else - return THttpSessionCookieMode::Only; - } - - /** - * @param THttpSessionCookieMode how to use cookie to store session ID - * @throws TInvalidOperationException if session is started already - */ - public function setCookieMode($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_cookiemode_unchangeable'); - else - { - $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode'); - if($value===THttpSessionCookieMode::None) - ini_set('session.use_cookies','0'); - else if($value===THttpSessionCookieMode::Allow) - { - ini_set('session.use_cookies','1'); - ini_set('session.use_only_cookies','0'); - } - else - { - ini_set('session.use_cookies','1'); - ini_set('session.use_only_cookies','1'); - ini_set('session.use_trans_sid', 0); - } - } - } - - /** - * @return boolean whether the session should be automatically started when the session module is initialized, defaults to false. - */ - public function getAutoStart() - { - return $this->_autoStart; - } - - /** - * @param boolean whether the session should be automatically started when the session module is initialized, defaults to false. - * @throws TInvalidOperationException if session is started already - */ - public function setAutoStart($value) - { - if($this->_initialized) - throw new TInvalidOperationException('httpsession_autostart_unchangeable'); - else - $this->_autoStart=TPropertyValue::ensureBoolean($value); - } - - /** - * @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. - */ - public function getGCProbability() - { - return TPropertyValue::ensureInteger(ini_get('session.gc_probability')); - } - - /** - * @param integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization. - * @throws TInvalidOperationException if session is started already - * @throws TInvalidDataValueException if the value is beyond [0,100]. - */ - public function setGCProbability($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_gcprobability_unchangeable'); - else - { - $value=TPropertyValue::ensureInteger($value); - if($value>=0 && $value<=100) - { - ini_set('session.gc_probability',$value); - ini_set('session.gc_divisor','100'); - } - else - throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value); - } - } - - /** - * @return boolean whether transparent sid support is enabled or not, defaults to false. - */ - public function getUseTransparentSessionID() - { - return ini_get('session.use_trans_sid')==='1'; - } - - /** - * @param boolean whether transparent sid support is enabled or not. - */ - public function setUseTransparentSessionID($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_transid_unchangeable'); - else - { - $value=TPropertyValue::ensureBoolean($value); - if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only) - throw new TInvalidOperationException('httpsession_transid_cookieonly'); - ini_set('session.use_trans_sid',$value?'1':'0'); - } - } - - /** - * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. - */ - public function getTimeout() - { - return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime')); - } - - /** - * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up - * @throws TInvalidOperationException if session is started already - */ - public function setTimeout($value) - { - if($this->_started) - throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable'); - else - ini_set('session.gc_maxlifetime',$value); - } - - /** - * Session open handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @param string session save path - * @param string session name - * @return boolean whether session is opened successfully - */ - public function _open($savePath,$sessionName) - { - return true; - } - - /** - * Session close handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @return boolean whether session is closed successfully - */ - public function _close() - { - return true; - } - - /** - * Session read handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @param string session ID - * @return string the session data - */ - public function _read($id) - { - return ''; - } - - /** - * Session write handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @param string session ID - * @param string session data - * @return boolean whether session write is successful - */ - public function _write($id,$data) - { - return true; - } - - /** - * Session destroy handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @param string session ID - * @return boolean whether session is destroyed successfully - */ - public function _destroy($id) - { - return true; - } - - /** - * Session GC (garbage collection) handler. - * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. - * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up. - * @return boolean whether session is GCed successfully - */ - public function _gc($maxLifetime) - { - return true; - } - - //------ The following methods enable THttpSession to be TMap-like ----- - - /** - * Returns an iterator for traversing the session variables. - * This method is required by the interface IteratorAggregate. - * @return TSessionIterator an iterator for traversing the session variables. - */ - public function getIterator() - { - return new TSessionIterator; - } - - /** - * @return integer the number of session variables - */ - public function getCount() - { - return count($_SESSION); - } - - /** - * Returns the number of items in the session. - * This method is required by Countable interface. - * @return integer number of items in the session. - */ - public function count() - { - return $this->getCount(); - } - - /** - * @return array the list of session variable names - */ - public function getKeys() - { - return array_keys($_SESSION); - } - - /** - * Returns the session variable value with the session variable name. - * This method is exactly the same as {@link offsetGet}. - * @param mixed the session variable name - * @return mixed the session variable value, null if no such variable exists - */ - public function itemAt($key) - { - return isset($_SESSION[$key]) ? $_SESSION[$key] : null; - } - - /** - * Adds a session variable. - * Note, if the specified name already exists, the old value will be removed first. - * @param mixed session variable name - * @param mixed session variable value - */ - public function add($key,$value) - { - $_SESSION[$key]=$value; - } - - /** - * Removes a session variable. - * @param mixed the name of the session variable to be removed - * @return mixed the removed value, null if no such session variable. - */ - public function remove($key) - { - if(isset($_SESSION[$key])) - { - $value=$_SESSION[$key]; - unset($_SESSION[$key]); - return $value; - } - else - return null; - } - - /** - * Removes all session variables - */ - public function clear() - { - foreach(array_keys($_SESSION) as $key) - unset($_SESSION[$key]); - } - - /** - * @param mixed session variable name - * @return boolean whether there is the named session variable - */ - public function contains($key) - { - return isset($_SESSION[$key]); - } - - /** - * @return array the list of all session variables in array - */ - public function toArray() - { - return $_SESSION; - } - - /** - * This method is required by the interface ArrayAccess. - * @param mixed the offset to check on - * @return boolean - */ - public function offsetExists($offset) - { - return isset($_SESSION[$offset]); - } - - /** - * This method is required by the interface ArrayAccess. - * @param integer the offset to retrieve element. - * @return mixed the element at the offset, null if no element is found at the offset - */ - public function offsetGet($offset) - { - return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; - } - - /** - * This method is required by the interface ArrayAccess. - * @param integer the offset to set element - * @param mixed the element value - */ - public function offsetSet($offset,$item) - { - $_SESSION[$offset]=$item; - } - - /** - * This method is required by the interface ArrayAccess. - * @param mixed the offset to unset element - */ - public function offsetUnset($offset) - { - unset($_SESSION[$offset]); - } -} - -/** - * TSessionIterator class - * - * TSessionIterator implements Iterator interface. - * - * TSessionIterator is used by THttpSession. It allows THttpSession to return a new iterator - * for traversing the session variables. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class TSessionIterator implements Iterator -{ - /** - * @var array list of keys in the map - */ - private $_keys; - /** - * @var mixed current key - */ - private $_key; - - /** - * Constructor. - * @param array the data to be iterated through - */ - public function __construct() - { - $this->_keys=array_keys($_SESSION); - } - - /** - * Rewinds internal array pointer. - * This method is required by the interface Iterator. - */ - public function rewind() - { - $this->_key=reset($this->_keys); - } - - /** - * Returns the key of the current array element. - * This method is required by the interface Iterator. - * @return mixed the key of the current array element - */ - public function key() - { - return $this->_key; - } - - /** - * Returns the current array element. - * This method is required by the interface Iterator. - * @return mixed the current array element - */ - public function current() - { - return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; - } - - /** - * Moves the internal pointer to the next array element. - * This method is required by the interface Iterator. - */ - public function next() - { - do - { - $this->_key=next($this->_keys); - } - while(!isset($_SESSION[$this->_key]) && $this->_key!==false); - } - - /** - * Returns whether there is an element at current position. - * This method is required by the interface Iterator. - * @return boolean - */ - public function valid() - { - return $this->_key!==false; - } -} - - -/** - * THttpSessionCookieMode class. - * THttpSessionCookieMode defines the enumerable type for the possible methods of - * using cookies to store session ID. - * - * The following enumerable values are defined: - * - None: not using cookie. - * - Allow: using cookie. - * - Only: using cookie only. - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0.4 - */ -class THttpSessionCookieMode extends TEnumerable -{ - const None='None'; - const Allow='Allow'; - const Only='Only'; -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * THttpSession class + * + * THttpSession provides session-level data management and the related configurations. + * To start the session, call {@link open}; to complete and send out session data, call {@link close}; + * to destroy the session, call {@link destroy}. If AutoStart is true, then the session + * will be started once the session module is loaded and initialized. + * + * To access data stored in session, use THttpSession like an associative array. For example, + * + * $session=new THttpSession; + * $session->open(); + * $value1=$session['name1']; // get session variable 'name1' + * $value2=$session['name2']; // get session variable 'name2' + * foreach($session as $name=>$value) // traverse all session variables + * $session['name3']=$value3; // set session variable 'name3' + * + * + * The following configurations are available for session: + * {@link setAutoStart AutoStart}, {@link setCookieMode CookieMode}, + * {@link setSavePath SavePath}, + * {@link setUseCustomStorage UseCustomStorage}, {@link setGCProbability GCProbability}, + * {@link setTimeout Timeout}. + * See the corresponding setter and getter documentation for more information. + * Note, these properties must be set before the session is started. + * + * THttpSession can be inherited with customized session storage method. + * Override {@link _open}, {@link _close}, {@link _read}, {@link _write}, {@link _destroy} and {@link _gc} + * and set {@link setUseCustomStorage UseCustomStorage} to true. + * Then, the session data will be stored using the above methods. + * + * By default, THttpSession is registered with {@link TApplication} as the + * request module. It can be accessed via {@link TApplication::getSession()}. + * + * THttpSession may be configured in application configuration file as follows, + * + * + * + * where {@link getSessionName SessionName}, {@link getSavePath SavePath}, + * {@link getCookieMode CookieMode}, {@link getUseCustomStorage + * UseCustomStorage}, {@link getAutoStart AutoStart}, {@link getGCProbability + * GCProbability}, {@link getUseTransparentSessionID UseTransparentSessionID} + * and {@link getTimeout TimeOut} are configurable properties of THttpSession. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule +{ + /** + * @var boolean whether this module has been initialized + */ + private $_initialized=false; + /** + * @var boolean whether the session has started + */ + private $_started=false; + /** + * @var boolean whether the session should be started when the module is initialized + */ + private $_autoStart=false; + /** + * @var THttpCookie cookie to be used to store session ID and other data + */ + private $_cookie=null; + /** + * @var string module id + */ + private $_id; + /** + * @var boolean + */ + private $_customStorage=false; + + /** + * @return string id of this module + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * Initializes the module. + * This method is required by IModule. + * If AutoStart is true, the session will be started. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_autoStart) + $this->open(); + $this->_initialized=true; + $this->getApplication()->setSession($this); + register_shutdown_function(array($this, "close")); + } + + /** + * Starts the session if it has not started yet. + */ + public function open() + { + if(!$this->_started) + { + if($this->_customStorage) + session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc')); + if($this->_cookie!==null) + session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure()); + if(ini_get('session.auto_start')!=='1') + session_start(); + $this->_started=true; + } + } + + /** + * Ends the current session and store session data. + */ + public function close() + { + if($this->_started) + { + session_write_close(); + $this->_started=false; + } + } + + /** + * Destroys all data registered to a session. + */ + public function destroy() + { + if($this->_started) + { + session_destroy(); + $this->_started=false; + } + } + + /** + * Update the current session id with a newly generated one + * + * @param boolean $deleteOld Whether to delete the old associated session or not. + * @return string old session id + * @link http://php.net/manual/en/function.session-regenerate-id.php + */ + public function regenerate($deleteOld=false) + { + $old = $this->getSessionID(); + session_regenerate_id($deleteOld); + return $old; + } + + /** + * @return boolean whether the session has started + */ + public function getIsStarted() + { + return $this->_started; + } + + /** + * @return string the current session ID + */ + public function getSessionID() + { + return session_id(); + } + + /** + * @param string the session ID for the current session + * @throws TInvalidOperationException if session is started already + */ + public function setSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionid_unchangeable'); + else + session_id($value); + } + + /** + * @return string the current session name + */ + public function getSessionName() + { + return session_name(); + } + + /** + * @param string the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID + * @throws TInvalidOperationException if session is started already + */ + public function setSessionName($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionname_unchangeable'); + else if(ctype_alnum($value)) + session_name($value); + else + throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value); + } + + /** + * @return string the current session save path, defaults to '/tmp'. + */ + public function getSavePath() + { + return session_save_path(); + } + + /** + * @param string the current session save path + * @throws TInvalidOperationException if session is started already + */ + public function setSavePath($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_savepath_unchangeable'); + else if(is_dir($value)) + session_save_path($value); + else + throw new TInvalidDataValueException('httpsession_savepath_invalid',$value); + } + + /** + * @return boolean whether to use user-specified handlers to store session data. Defaults to false. + */ + public function getUseCustomStorage() + { + return $this->_customStorage; + } + + /** + * @param boolean whether to use user-specified handlers to store session data. + * If true, make sure the methods {@link _open}, {@link _close}, {@link _read}, + * {@link _write}, {@link _destroy}, and {@link _gc} are overridden in child + * class, because they will be used as the callback handlers. + */ + public function setUseCustomStorage($value) + { + $this->_customStorage=TPropertyValue::ensureBoolean($value); + } + + /** + * @return THttpCookie cookie that will be used to store session ID + */ + public function getCookie() + { + if($this->_cookie===null) + $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID()); + return $this->_cookie; + } + + /** + * @return THttpSessionCookieMode how to use cookie to store session ID. Defaults to THttpSessionCookieMode::Allow. + */ + public function getCookieMode() + { + if(ini_get('session.use_cookies')==='0') + return THttpSessionCookieMode::None; + else if(ini_get('session.use_only_cookies')==='0') + return THttpSessionCookieMode::Allow; + else + return THttpSessionCookieMode::Only; + } + + /** + * @param THttpSessionCookieMode how to use cookie to store session ID + * @throws TInvalidOperationException if session is started already + */ + public function setCookieMode($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_cookiemode_unchangeable'); + else + { + $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode'); + if($value===THttpSessionCookieMode::None) + ini_set('session.use_cookies','0'); + else if($value===THttpSessionCookieMode::Allow) + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','0'); + } + else + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','1'); + ini_set('session.use_trans_sid', 0); + } + } + } + + /** + * @return boolean whether the session should be automatically started when the session module is initialized, defaults to false. + */ + public function getAutoStart() + { + return $this->_autoStart; + } + + /** + * @param boolean whether the session should be automatically started when the session module is initialized, defaults to false. + * @throws TInvalidOperationException if session is started already + */ + public function setAutoStart($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpsession_autostart_unchangeable'); + else + $this->_autoStart=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. + */ + public function getGCProbability() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_probability')); + } + + /** + * @param integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization. + * @throws TInvalidOperationException if session is started already + * @throws TInvalidDataValueException if the value is beyond [0,100]. + */ + public function setGCProbability($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_gcprobability_unchangeable'); + else + { + $value=TPropertyValue::ensureInteger($value); + if($value>=0 && $value<=100) + { + ini_set('session.gc_probability',$value); + ini_set('session.gc_divisor','100'); + } + else + throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value); + } + } + + /** + * @return boolean whether transparent sid support is enabled or not, defaults to false. + */ + public function getUseTransparentSessionID() + { + return ini_get('session.use_trans_sid')==='1'; + } + + /** + * @param boolean whether transparent sid support is enabled or not. + */ + public function setUseTransparentSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_transid_unchangeable'); + else + { + $value=TPropertyValue::ensureBoolean($value); + if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only) + throw new TInvalidOperationException('httpsession_transid_cookieonly'); + ini_set('session.use_trans_sid',$value?'1':'0'); + } + } + + /** + * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. + */ + public function getTimeout() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime')); + } + + /** + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up + * @throws TInvalidOperationException if session is started already + */ + public function setTimeout($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable'); + else + ini_set('session.gc_maxlifetime',$value); + } + + /** + * Session open handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session save path + * @param string session name + * @return boolean whether session is opened successfully + */ + public function _open($savePath,$sessionName) + { + return true; + } + + /** + * Session close handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @return boolean whether session is closed successfully + */ + public function _close() + { + return true; + } + + /** + * Session read handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @return string the session data + */ + public function _read($id) + { + return ''; + } + + /** + * Session write handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function _write($id,$data) + { + return true; + } + + /** + * Session destroy handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function _destroy($id) + { + return true; + } + + /** + * Session GC (garbage collection) handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function _gc($maxLifetime) + { + return true; + } + + //------ The following methods enable THttpSession to be TMap-like ----- + + /** + * Returns an iterator for traversing the session variables. + * This method is required by the interface IteratorAggregate. + * @return TSessionIterator an iterator for traversing the session variables. + */ + public function getIterator() + { + return new TSessionIterator; + } + + /** + * @return integer the number of session variables + */ + public function getCount() + { + return count($_SESSION); + } + + /** + * Returns the number of items in the session. + * This method is required by Countable interface. + * @return integer number of items in the session. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return array the list of session variable names + */ + public function getKeys() + { + return array_keys($_SESSION); + } + + /** + * Returns the session variable value with the session variable name. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the session variable name + * @return mixed the session variable value, null if no such variable exists + */ + public function itemAt($key) + { + return isset($_SESSION[$key]) ? $_SESSION[$key] : null; + } + + /** + * Adds a session variable. + * Note, if the specified name already exists, the old value will be removed first. + * @param mixed session variable name + * @param mixed session variable value + */ + public function add($key,$value) + { + $_SESSION[$key]=$value; + } + + /** + * Removes a session variable. + * @param mixed the name of the session variable to be removed + * @return mixed the removed value, null if no such session variable. + */ + public function remove($key) + { + if(isset($_SESSION[$key])) + { + $value=$_SESSION[$key]; + unset($_SESSION[$key]); + return $value; + } + else + return null; + } + + /** + * Removes all session variables + */ + public function clear() + { + foreach(array_keys($_SESSION) as $key) + unset($_SESSION[$key]); + } + + /** + * @param mixed session variable name + * @return boolean whether there is the named session variable + */ + public function contains($key) + { + return isset($_SESSION[$key]); + } + + /** + * @return array the list of all session variables in array + */ + public function toArray() + { + return $_SESSION; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return isset($_SESSION[$offset]); + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $_SESSION[$offset]=$item; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + unset($_SESSION[$offset]); + } +} + +/** + * TSessionIterator class + * + * TSessionIterator implements Iterator interface. + * + * TSessionIterator is used by THttpSession. It allows THttpSession to return a new iterator + * for traversing the session variables. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class TSessionIterator implements Iterator +{ + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct() + { + $this->_keys=array_keys($_SESSION); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key=reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + do + { + $this->_key=next($this->_keys); + } + while(!isset($_SESSION[$this->_key]) && $this->_key!==false); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key!==false; + } +} + + +/** + * THttpSessionCookieMode class. + * THttpSessionCookieMode defines the enumerable type for the possible methods of + * using cookies to store session ID. + * + * The following enumerable values are defined: + * - None: not using cookie. + * - Allow: using cookie. + * - Only: using cookie only. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0.4 + */ +class THttpSessionCookieMode extends TEnumerable +{ + const None='None'; + const Allow='Allow'; + const Only='Only'; +} + diff --git a/framework/Web/THttpUtility.php b/framework/Web/THttpUtility.php index 9e0bf4bb..a7a0f813 100644 --- a/framework/Web/THttpUtility.php +++ b/framework/Web/THttpUtility.php @@ -1,62 +1,62 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web - */ - -/** - * THttpUtility class - * - * @author Qiang Xue - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class THttpUtility -{ - private static $_encodeTable=array('<'=>'<','>'=>'>','"'=>'"'); - private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); - private static $_stripTable=array('<'=>'','>'=>'','"'=>''); - - /** - * HTML-encodes a string. - * This method translates the following characters to their corresponding - * HTML entities: <, >, " - * Note, unlike {@link htmlspecialchars}, & is not translated. - * @param string string to be encoded - * @return string encoded string - */ - public static function htmlEncode($s) - { - return strtr($s,self::$_encodeTable); - } - - /** - * HTML-decodes a string. - * It is the inverse of {@link htmlEncode}. - * @param string string to be decoded - * @return string decoded string - */ - public static function htmlDecode($s) - { - return strtr($s,self::$_decodeTable); - } - - /** - * This method strips the following characters from a string: - * HTML entities: <, >, " - * @param string string to be encoded - * @return string encoded string - */ - public static function htmlStrip($s) - { - return strtr($s,self::$_stripTable); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * THttpUtility class + * + * @author Qiang Xue + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class THttpUtility +{ + private static $_encodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_stripTable=array('<'=>'','>'=>'','"'=>''); + + /** + * HTML-encodes a string. + * This method translates the following characters to their corresponding + * HTML entities: <, >, " + * Note, unlike {@link htmlspecialchars}, & is not translated. + * @param string string to be encoded + * @return string encoded string + */ + public static function htmlEncode($s) + { + return strtr($s,self::$_encodeTable); + } + + /** + * HTML-decodes a string. + * It is the inverse of {@link htmlEncode}. + * @param string string to be decoded + * @return string decoded string + */ + public static function htmlDecode($s) + { + return strtr($s,self::$_decodeTable); + } + + /** + * This method strips the following characters from a string: + * HTML entities: <, >, " + * @param string string to be encoded + * @return string encoded string + */ + public static function htmlStrip($s) + { + return strtr($s,self::$_stripTable); + } +} + diff --git a/framework/Web/TUrlManager.php b/framework/Web/TUrlManager.php index f4846e28..b5a0a5be 100644 --- a/framework/Web/TUrlManager.php +++ b/framework/Web/TUrlManager.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 - */ - -/** - * TUrlManager class - * - * TUrlManager is the base class for managing URLs that can be - * recognized by PRADO applications. It provides the default implementation - * for parsing and constructing URLs. - * - * Derived classes may override {@link constructUrl} and {@link parseUrl} - * to provide customized URL schemes. - * - * By default, {@link THttpRequest} uses TUrlManager as its URL manager. - * If you want to use your customized URL manager, load your manager class - * as an application module and set {@link THttpRequest::setUrlManager THttpRequest.UrlManager} - * with the ID of your URL manager module. - * - * @author Qiang Xue - * @version $Id $ - * @package System.Web - * @since 3.0.6 - */ -class TUrlManager extends TModule -{ - /** - * Constructs a URL that can be recognized by PRADO. - * - * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. - * Override this method if you want to provide your own way of URL formatting. - * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. - * - * The URL is constructed as the following format: - * /entryscript.php?serviceID=serviceParameter&get1=value1&... - * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', - * the following format is used instead: - * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... - * @param string service ID - * @param string service parameter - * @param array GET parameters, null if not provided - * @param boolean whether to encode the ampersand in URL - * @param boolean whether to encode the GET parameters (their names and values) - * @return string URL - * @see parseUrl - */ - public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) - { - $url=$serviceID.'='.urlencode($serviceParam); - $amp=$encodeAmpersand?'&':'&'; - $request=$this->getRequest(); - if(is_array($getItems) || $getItems instanceof Traversable) - { - if($encodeGetItems) - { - foreach($getItems as $name=>$value) - { - if(is_array($value)) - { - $name=urlencode($name.'[]'); - foreach($value as $v) - $url.=$amp.$name.'='.urlencode($v); - } - else - $url.=$amp.urlencode($name).'='.urlencode($value); - } - } - else - { - foreach($getItems as $name=>$value) - { - if(is_array($value)) - { - foreach($value as $v) - $url.=$amp.$name.'[]='.$v; - } - else - $url.=$amp.$name.'='.$value; - } - } - } - if($request->getUrlFormat()===THttpRequestUrlFormat::Path) - return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); - else - return $request->getApplicationUrl().'?'.$url; - } - - /** - * Parses the request URL and returns an array of input parameters. - * This method is automatically invoked by {@link THttpRequest} when - * handling a user request. - * - * In general, this method should parse the path info part of the requesting URL - * and generate an array of name-value pairs according to some scheme. - * The current implementation deals with both 'Get' and 'Path' URL formats. - * - * You may override this method to support customized URL format. - * @return array list of input parameters, indexed by parameter names - * @see constructUrl - */ - public function parseUrl() - { - $request=$this->getRequest(); - $pathInfo=trim($request->getPathInfo(),'/'); - if($request->getUrlFormat()===THttpRequestUrlFormat::Path && $pathInfo!=='') - { - $separator=$request->getUrlParamSeparator(); - $paths=explode('/',$pathInfo); - $getVariables=array(); - foreach($paths as $path) - { - if(($path=trim($path))!=='') - { - if(($pos=strpos($path,$separator))!==false) - { - $name=substr($path,0,$pos); - $value=substr($path,$pos+1); - if(($pos=strpos($name,'[]'))!==false) - $getVariables[substr($name,0,$pos)][]=$value; - else - $getVariables[$name]=$value; - } - else - $getVariables[$path]=''; - } - } - return $getVariables; - } - else - return array(); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id $ + * @package System.Web + */ + +/** + * TUrlManager class + * + * TUrlManager is the base class for managing URLs that can be + * recognized by PRADO applications. It provides the default implementation + * for parsing and constructing URLs. + * + * Derived classes may override {@link constructUrl} and {@link parseUrl} + * to provide customized URL schemes. + * + * By default, {@link THttpRequest} uses TUrlManager as its URL manager. + * If you want to use your customized URL manager, load your manager class + * as an application module and set {@link THttpRequest::setUrlManager THttpRequest.UrlManager} + * with the ID of your URL manager module. + * + * @author Qiang Xue + * @version $Id $ + * @package System.Web + * @since 3.0.6 + */ +class TUrlManager extends TModule +{ + /** + * Constructs a URL that can be recognized by PRADO. + * + * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. + * Override this method if you want to provide your own way of URL formatting. + * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. + * + * The URL is constructed as the following format: + * /entryscript.php?serviceID=serviceParameter&get1=value1&... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', + * the following format is used instead: + * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not provided + * @param boolean whether to encode the ampersand in URL + * @param boolean whether to encode the GET parameters (their names and values) + * @return string URL + * @see parseUrl + */ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + $url=$serviceID.'='.urlencode($serviceParam); + $amp=$encodeAmpersand?'&':'&'; + $request=$this->getRequest(); + if(is_array($getItems) || $getItems instanceof Traversable) + { + if($encodeGetItems) + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + $name=urlencode($name.'[]'); + foreach($value as $v) + $url.=$amp.$name.'='.urlencode($v); + } + else + $url.=$amp.urlencode($name).'='.urlencode($value); + } + } + else + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url.=$amp.$name.'[]='.$v; + } + else + $url.=$amp.$name.'='.$value; + } + } + } + if($request->getUrlFormat()===THttpRequestUrlFormat::Path) + return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + else + return $request->getApplicationUrl().'?'.$url; + } + + /** + * Parses the request URL and returns an array of input parameters. + * This method is automatically invoked by {@link THttpRequest} when + * handling a user request. + * + * In general, this method should parse the path info part of the requesting URL + * and generate an array of name-value pairs according to some scheme. + * The current implementation deals with both 'Get' and 'Path' URL formats. + * + * You may override this method to support customized URL format. + * @return array list of input parameters, indexed by parameter names + * @see constructUrl + */ + public function parseUrl() + { + $request=$this->getRequest(); + $pathInfo=trim($request->getPathInfo(),'/'); + if($request->getUrlFormat()===THttpRequestUrlFormat::Path && $pathInfo!=='') + { + $separator=$request->getUrlParamSeparator(); + $paths=explode('/',$pathInfo); + $getVariables=array(); + foreach($paths as $path) + { + if(($path=trim($path))!=='') + { + if(($pos=strpos($path,$separator))!==false) + { + $name=substr($path,0,$pos); + $value=substr($path,$pos+1); + if(($pos=strpos($name,'[]'))!==false) + $getVariables[substr($name,0,$pos)][]=$value; + else + $getVariables[$name]=$value; + } + else + $getVariables[$path]=''; + } + } + return $getVariables; + } + else + return array(); + } +} + diff --git a/framework/Web/TUrlMapping.php b/framework/Web/TUrlMapping.php index 1cae4ee4..19891da1 100644 --- a/framework/Web/TUrlMapping.php +++ b/framework/Web/TUrlMapping.php @@ -1,434 +1,434 @@ - - * @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 - */ - -Prado::using('System.Web.TUrlManager'); -Prado::using('System.Collections.TAttributeCollection'); - -/** - * TUrlMapping Class - * - * The TUrlMapping module allows PRADO to construct and recognize URLs - * based on specific patterns. - * - * TUrlMapping consists of a list of URL patterns which are used to match - * against the currently requested URL. The first matching pattern will then - * be used to decompose the URL into request parameters (accessible through - * $this->Request['paramname']). - * - * The patterns can also be used to construct customized URLs. In this case, - * the parameters in an applied pattern will be replaced with the corresponding - * GET variable values. - * - * Since it is derived from {@link TUrlManager}, it should be configured globally - * in the application configuration like the following, - * - * - * - * - * - * - * - * - * - * In the above, each <url> element specifies a URL pattern represented - * as a {@link TUrlMappingPattern} internally. You may create your own pattern classes - * by extending {@link TUrlMappingPattern} and specifying the <class> attribute - * in the element. - * - * The patterns can be also be specified in an external file using the {@link setConfigFile ConfigFile} property. - * - * The URL mapping are evaluated in order, only the first mapping that matches - * the URL will be used. Cascaded mapping can be achieved by placing the URL mappings - * in particular order. For example, placing the most specific mappings first. - * - * Only the PATH_INFO part of the URL is used to match the available patterns. The matching - * is strict in the sense that the whole pattern must match the whole PATH_INFO of the URL. - * - * From PRADO v3.1.1, TUrlMapping also provides support for constructing URLs according to - * the specified pattern. You may enable this functionality by setting {@link setEnableCustomUrl EnableCustomUrl} to true. - * When you call THttpRequest::constructUrl() (or via TPageService::constructUrl()), - * TUrlMapping will examine the available URL mapping patterns using their {@link TUrlMappingPattern::getServiceParameter ServiceParameter} - * and {@link TUrlMappingPattern::getPattern Pattern} properties. A pattern is applied if its - * {@link TUrlMappingPattern::getServiceParameter ServiceParameter} matches the service parameter passed - * to constructUrl() and every parameter in the {@link getPattern Pattern} is found - * in the GET variables. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web - * @since 3.0.5 - */ -class TUrlMapping extends TUrlManager -{ - /** - * @var TUrlMappingPattern[] list of patterns. - */ - protected $_patterns=array(); - /** - * @var TUrlMappingPattern matched pattern. - */ - private $_matched; - /** - * @var string external configuration file - */ - private $_configFile=null; - /** - * @var boolean whether to enable custom contructUrl - */ - private $_customUrl=false; - /** - * @var array rules for constructing URLs - */ - protected $_constructRules=array(); - - private $_urlPrefix=''; - - private $_defaultMappingClass='TUrlMappingPattern'; - - /** - * Initializes this module. - * This method is required by the IModule interface. - * @param mixed configuration for this module, can be null - * @throws TConfigurationException if module is configured in the global scope. - */ - public function init($config) - { - parent::init($config); - if($this->getRequest()->getRequestResolved()) - throw new TConfigurationException('urlmapping_global_required'); - if($this->_configFile!==null) - $this->loadConfigFile(); - $this->loadUrlMappings($config); - if($this->_urlPrefix==='') - $this->_urlPrefix=$this->getRequest()->getApplicationUrl(); - $this->_urlPrefix=rtrim($this->_urlPrefix,'/'); - } - - /** - * Initialize the module from configuration file. - * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. - */ - protected function loadConfigFile() - { - if(is_file($this->_configFile)) - { - if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) - { - $config = include $this->_configFile; - $this->loadUrlMappings($dom); - } - else - { - $dom=new TXmlDocument; - $dom->loadFromFile($this->_configFile); - $this->loadUrlMappings($dom); - } - } - else - throw new TConfigurationException('urlmapping_configfile_inexistent',$this->_configFile); - } - - /** - * Returns a value indicating whether to enable custom constructUrl. - * If true, constructUrl() will make use of the URL mapping rules to - * construct valid URLs. - * @return boolean whether to enable custom constructUrl. Defaults to false. - * @since 3.1.1 - */ - public function getEnableCustomUrl() - { - return $this->_customUrl; - } - - /** - * Sets a value indicating whether to enable custom constructUrl. - * If true, constructUrl() will make use of the URL mapping rules to - * construct valid URLs. - * @param boolean whether to enable custom constructUrl. - * @since 3.1.1 - */ - public function setEnableCustomUrl($value) - { - $this->_customUrl=TPropertyValue::ensureBoolean($value); - } - - /** - * @return string the part that will be prefixed to the constructed URLs. Defaults to the requested script path (e.g. /path/to/index.php for a URL http://hostname/path/to/index.php) - * @since 3.1.1 - */ - public function getUrlPrefix() - { - return $this->_urlPrefix; - } - - /** - * @param string the part that will be prefixed to the constructed URLs. This is used by constructUrl() when EnableCustomUrl is set true. - * @see getUrlPrefix - * @since 3.1.1 - */ - public function setUrlPrefix($value) - { - $this->_urlPrefix=$value; - } - - /** - * @return string external configuration file. Defaults to null. - */ - public function getConfigFile() - { - return $this->_configFile; - } - - /** - * @param string external configuration file in namespace format. The file - * must be suffixed with '.xml'. - * @throws TInvalidDataValueException if the file is invalid. - */ - public function setConfigFile($value) - { - if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null) - throw new TConfigurationException('urlmapping_configfile_invalid',$value); - } - - /** - * @return string the default class of URL mapping patterns. Defaults to TUrlMappingPattern. - * @since 3.1.1 - */ - public function getDefaultMappingClass() - { - return $this->_defaultMappingClass; - } - - /** - * Sets the default class of URL mapping patterns. - * When a URL matching pattern does not specify "class" attribute, it will default to the class - * specified by this property. You may use either a class name or a namespace format of class (if the class needs to be included first.) - * @param string the default class of URL mapping patterns. - * @since 3.1.1 - */ - public function setDefaultMappingClass($value) - { - $this->_defaultMappingClass=$value; - } - - /** - * Load and configure each url mapping pattern. - * @param mixed configuration node - * @throws TConfigurationException if specific pattern class is invalid - */ - protected function loadUrlMappings($config) - { - $defaultClass = $this->getDefaultMappingClass(); - - if(is_array($config)) - { - if(isset($config['urls']) && is_array($config['urls'])) - { - foreach($config['urls'] as $url) - { - $class=null; - if(!isset($url['class'])) - $class=$defaultClass; - $pattern=Prado::createComponent($class,$this); - $properties = isset($url['properties'])?$url['properties']:array(); - $this->buildUrlMapping($class,$pattern,$properties,$url); - } - } - } - else - { - foreach($config->getElementsByTagName('url') as $url) - { - $properties=$url->getAttributes(); - if(($class=$properties->remove('class'))===null) - $class=$defaultClass; - $pattern=Prado::createComponent($class,$this); - $this->buildUrlMapping($class,$pattern,$properties,$url); - } - } - } - - private function buildUrlMapping($class, $pattern, $properties, $url) - { - $pattern=Prado::createComponent($class,$this); - if(!($pattern instanceof TUrlMappingPattern)) - throw new TConfigurationException('urlmapping_urlmappingpattern_required'); - foreach($properties as $name=>$value) - $pattern->setSubproperty($name,$value); - - if($url instanceof TXmlElement) { - $text = $url -> getValue(); - if($text) { - $text = preg_replace('/(\s+)/S', '', $text); - if(($regExp = $pattern->getRegularExpression()) !== '') - trigger_error(sPrintF('%s.RegularExpression property value "%s" for ServiceID="%s" and ServiceParameter="%s" was replaced by node value "%s"', - get_class($pattern), - $regExp, - $pattern->getServiceID(), - $pattern->getServiceParameter(), - $text), - E_USER_NOTICE); - $pattern->setRegularExpression($text); - } - } - - $this->_patterns[]=$pattern; - $pattern->init($url); - - $key=$pattern->getServiceID().':'.$pattern->getServiceParameter(); - $this->_constructRules[$key][]=$pattern; - } - - /** - * Parses the request URL and returns an array of input parameters. - * This method overrides the parent implementation. - * The input parameters do not include GET and POST variables. - * This method uses the request URL path to find the first matching pattern. If found - * the matched pattern parameters are used to return as the input parameters. - * @return array list of input parameters - */ - public function parseUrl() - { - $request=$this->getRequest(); - foreach($this->_patterns as $pattern) - { - $matches=$pattern->getPatternMatches($request); - if(count($matches)>0) - { - $this->_matched=$pattern; - $params=array(); - foreach($matches as $key=>$value) - { - if(is_string($key)) - $params[$key]=$value; - } - if (!$pattern->getIsWildCardPattern()) - $params[$pattern->getServiceID()]=$pattern->getServiceParameter(); - return $params; - } - } - return parent::parseUrl(); - } - - /** - * Constructs a URL that can be recognized by PRADO. - * - * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. - * Override this method if you want to provide your own way of URL formatting. - * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. - * - * The URL is constructed as the following format: - * /entryscript.php?serviceID=serviceParameter&get1=value1&... - * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', - * the following format is used instead: - * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... - * @param string service ID - * @param string service parameter - * @param array GET parameters, null if not provided - * @param boolean whether to encode the ampersand in URL - * @param boolean whether to encode the GET parameters (their names and values) - * @return string URL - * @see parseUrl - * @since 3.1.1 - */ - public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) - { - if($this->_customUrl) - { - if(!(is_array($getItems) || ($getItems instanceof Traversable))) - $getItems=array(); - $key=$serviceID.':'.$serviceParam; + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +Prado::using('System.Web.TUrlManager'); +Prado::using('System.Collections.TAttributeCollection'); + +/** + * TUrlMapping Class + * + * The TUrlMapping module allows PRADO to construct and recognize URLs + * based on specific patterns. + * + * TUrlMapping consists of a list of URL patterns which are used to match + * against the currently requested URL. The first matching pattern will then + * be used to decompose the URL into request parameters (accessible through + * $this->Request['paramname']). + * + * The patterns can also be used to construct customized URLs. In this case, + * the parameters in an applied pattern will be replaced with the corresponding + * GET variable values. + * + * Since it is derived from {@link TUrlManager}, it should be configured globally + * in the application configuration like the following, + * + * + * + * + * + * + * + * + * + * In the above, each <url> element specifies a URL pattern represented + * as a {@link TUrlMappingPattern} internally. You may create your own pattern classes + * by extending {@link TUrlMappingPattern} and specifying the <class> attribute + * in the element. + * + * The patterns can be also be specified in an external file using the {@link setConfigFile ConfigFile} property. + * + * The URL mapping are evaluated in order, only the first mapping that matches + * the URL will be used. Cascaded mapping can be achieved by placing the URL mappings + * in particular order. For example, placing the most specific mappings first. + * + * Only the PATH_INFO part of the URL is used to match the available patterns. The matching + * is strict in the sense that the whole pattern must match the whole PATH_INFO of the URL. + * + * From PRADO v3.1.1, TUrlMapping also provides support for constructing URLs according to + * the specified pattern. You may enable this functionality by setting {@link setEnableCustomUrl EnableCustomUrl} to true. + * When you call THttpRequest::constructUrl() (or via TPageService::constructUrl()), + * TUrlMapping will examine the available URL mapping patterns using their {@link TUrlMappingPattern::getServiceParameter ServiceParameter} + * and {@link TUrlMappingPattern::getPattern Pattern} properties. A pattern is applied if its + * {@link TUrlMappingPattern::getServiceParameter ServiceParameter} matches the service parameter passed + * to constructUrl() and every parameter in the {@link getPattern Pattern} is found + * in the GET variables. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMapping extends TUrlManager +{ + /** + * @var TUrlMappingPattern[] list of patterns. + */ + protected $_patterns=array(); + /** + * @var TUrlMappingPattern matched pattern. + */ + private $_matched; + /** + * @var string external configuration file + */ + private $_configFile=null; + /** + * @var boolean whether to enable custom contructUrl + */ + private $_customUrl=false; + /** + * @var array rules for constructing URLs + */ + protected $_constructRules=array(); + + private $_urlPrefix=''; + + private $_defaultMappingClass='TUrlMappingPattern'; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + * @throws TConfigurationException if module is configured in the global scope. + */ + public function init($config) + { + parent::init($config); + if($this->getRequest()->getRequestResolved()) + throw new TConfigurationException('urlmapping_global_required'); + if($this->_configFile!==null) + $this->loadConfigFile(); + $this->loadUrlMappings($config); + if($this->_urlPrefix==='') + $this->_urlPrefix=$this->getRequest()->getApplicationUrl(); + $this->_urlPrefix=rtrim($this->_urlPrefix,'/'); + } + + /** + * Initialize the module from configuration file. + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + protected function loadConfigFile() + { + if(is_file($this->_configFile)) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $config = include $this->_configFile; + $this->loadUrlMappings($dom); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadUrlMappings($dom); + } + } + else + throw new TConfigurationException('urlmapping_configfile_inexistent',$this->_configFile); + } + + /** + * Returns a value indicating whether to enable custom constructUrl. + * If true, constructUrl() will make use of the URL mapping rules to + * construct valid URLs. + * @return boolean whether to enable custom constructUrl. Defaults to false. + * @since 3.1.1 + */ + public function getEnableCustomUrl() + { + return $this->_customUrl; + } + + /** + * Sets a value indicating whether to enable custom constructUrl. + * If true, constructUrl() will make use of the URL mapping rules to + * construct valid URLs. + * @param boolean whether to enable custom constructUrl. + * @since 3.1.1 + */ + public function setEnableCustomUrl($value) + { + $this->_customUrl=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the part that will be prefixed to the constructed URLs. Defaults to the requested script path (e.g. /path/to/index.php for a URL http://hostname/path/to/index.php) + * @since 3.1.1 + */ + public function getUrlPrefix() + { + return $this->_urlPrefix; + } + + /** + * @param string the part that will be prefixed to the constructed URLs. This is used by constructUrl() when EnableCustomUrl is set true. + * @see getUrlPrefix + * @since 3.1.1 + */ + public function setUrlPrefix($value) + { + $this->_urlPrefix=$value; + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null) + throw new TConfigurationException('urlmapping_configfile_invalid',$value); + } + + /** + * @return string the default class of URL mapping patterns. Defaults to TUrlMappingPattern. + * @since 3.1.1 + */ + public function getDefaultMappingClass() + { + return $this->_defaultMappingClass; + } + + /** + * Sets the default class of URL mapping patterns. + * When a URL matching pattern does not specify "class" attribute, it will default to the class + * specified by this property. You may use either a class name or a namespace format of class (if the class needs to be included first.) + * @param string the default class of URL mapping patterns. + * @since 3.1.1 + */ + public function setDefaultMappingClass($value) + { + $this->_defaultMappingClass=$value; + } + + /** + * Load and configure each url mapping pattern. + * @param mixed configuration node + * @throws TConfigurationException if specific pattern class is invalid + */ + protected function loadUrlMappings($config) + { + $defaultClass = $this->getDefaultMappingClass(); + + if(is_array($config)) + { + if(isset($config['urls']) && is_array($config['urls'])) + { + foreach($config['urls'] as $url) + { + $class=null; + if(!isset($url['class'])) + $class=$defaultClass; + $pattern=Prado::createComponent($class,$this); + $properties = isset($url['properties'])?$url['properties']:array(); + $this->buildUrlMapping($class,$pattern,$properties,$url); + } + } + } + else + { + foreach($config->getElementsByTagName('url') as $url) + { + $properties=$url->getAttributes(); + if(($class=$properties->remove('class'))===null) + $class=$defaultClass; + $pattern=Prado::createComponent($class,$this); + $this->buildUrlMapping($class,$pattern,$properties,$url); + } + } + } + + private function buildUrlMapping($class, $pattern, $properties, $url) + { + $pattern=Prado::createComponent($class,$this); + if(!($pattern instanceof TUrlMappingPattern)) + throw new TConfigurationException('urlmapping_urlmappingpattern_required'); + foreach($properties as $name=>$value) + $pattern->setSubproperty($name,$value); + + if($url instanceof TXmlElement) { + $text = $url -> getValue(); + if($text) { + $text = preg_replace('/(\s+)/S', '', $text); + if(($regExp = $pattern->getRegularExpression()) !== '') + trigger_error(sPrintF('%s.RegularExpression property value "%s" for ServiceID="%s" and ServiceParameter="%s" was replaced by node value "%s"', + get_class($pattern), + $regExp, + $pattern->getServiceID(), + $pattern->getServiceParameter(), + $text), + E_USER_NOTICE); + $pattern->setRegularExpression($text); + } + } + + $this->_patterns[]=$pattern; + $pattern->init($url); + + $key=$pattern->getServiceID().':'.$pattern->getServiceParameter(); + $this->_constructRules[$key][]=$pattern; + } + + /** + * Parses the request URL and returns an array of input parameters. + * This method overrides the parent implementation. + * The input parameters do not include GET and POST variables. + * This method uses the request URL path to find the first matching pattern. If found + * the matched pattern parameters are used to return as the input parameters. + * @return array list of input parameters + */ + public function parseUrl() + { + $request=$this->getRequest(); + foreach($this->_patterns as $pattern) + { + $matches=$pattern->getPatternMatches($request); + if(count($matches)>0) + { + $this->_matched=$pattern; + $params=array(); + foreach($matches as $key=>$value) + { + if(is_string($key)) + $params[$key]=$value; + } + if (!$pattern->getIsWildCardPattern()) + $params[$pattern->getServiceID()]=$pattern->getServiceParameter(); + return $params; + } + } + return parent::parseUrl(); + } + + /** + * Constructs a URL that can be recognized by PRADO. + * + * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. + * Override this method if you want to provide your own way of URL formatting. + * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. + * + * The URL is constructed as the following format: + * /entryscript.php?serviceID=serviceParameter&get1=value1&... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', + * the following format is used instead: + * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not provided + * @param boolean whether to encode the ampersand in URL + * @param boolean whether to encode the GET parameters (their names and values) + * @return string URL + * @see parseUrl + * @since 3.1.1 + */ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + if($this->_customUrl) + { + if(!(is_array($getItems) || ($getItems instanceof Traversable))) + $getItems=array(); + $key=$serviceID.':'.$serviceParam; $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ? $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*'; - if(isset($this->_constructRules[$key])) - { - foreach($this->_constructRules[$key] as $rule) - { - if($rule->supportCustomUrl($getItems)) - return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); - } + if(isset($this->_constructRules[$key])) + { + foreach($this->_constructRules[$key] as $rule) + { + if($rule->supportCustomUrl($getItems)) + return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); + } } elseif(isset($this->_constructRules[$wildCardKey])) { - foreach($this->_constructRules[$wildCardKey] as $rule) - { - if($rule->supportCustomUrl($getItems)) + foreach($this->_constructRules[$wildCardKey] as $rule) + { + if($rule->supportCustomUrl($getItems)) { $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam; - return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); + return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); } - } - } - } - return parent::constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); - } - - /** - * @return TUrlMappingPattern the matched pattern, null if not found. - */ - public function getMatchingPattern() - { - return $this->_matched; - } -} - -/** - * TUrlMappingPattern class. - * - * TUrlMappingPattern represents a pattern used to parse and construct URLs. - * If the currently requested URL matches the pattern, it will alter - * the THttpRequest parameters. If a constructUrl() call matches the pattern - * parameters, the pattern will generate a valid URL. In both case, only the PATH_INFO - * part of a URL is parsed/constructed using the pattern. - * - * To specify the pattern, set the {@link setPattern Pattern} property. - * {@link setPattern Pattern} takes a string expression with - * parameter names enclosed between a left brace '{' and a right brace '}'. - * The patterns for each parameter can be set using {@link getParameters Parameters} - * attribute collection. For example - * - * - * - * - * In the above example, the pattern contains 3 parameters named "year", - * "month" and "day". The pattern for these parameters are, respectively, - * "\d{4}" (4 digits), "\d{2}" (2 digits) and "\d+" (1 or more digits). - * Essentially, the Parameters attribute name and values are used - * as substrings in replacing the placeholders in the Pattern string - * to form a complete regular expression string. - * - * For more complicated patterns, one may specify the pattern using a regular expression - * by {@link setRegularExpression RegularExpression}. For example, the above pattern - * is equivalent to the following regular expression-based pattern: - * - * #^articles/(?P\d{4})/(?P\d{2})\/(?P\d+)$#u - * - * The above regular expression used the "named group" feature available in PHP. - * If you intended to use the RegularExpression property or - * regular expressions in CDATA sections, notice that you need to escape the slash, - * if you are using the slash as regular expressions delimiter. - * - * Thus, only an url that matches the pattern will be valid. For example, - * a URL http://example.com/index.php/articles/2006/07/21 will match the above pattern, - * while http://example.com/index.php/articles/2006/07/hello will not - * since the "day" parameter pattern is not satisfied. - * - * The parameter values are available through the THttpRequest instance (e.g. - * $this->Request['year']). - * - * The {@link setServiceParameter ServiceParameter} and {@link setServiceID ServiceID} - * (the default ID is 'page') set the service parameter and service id respectively. - * + } + } + } + return parent::constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); + } + + /** + * @return TUrlMappingPattern the matched pattern, null if not found. + */ + public function getMatchingPattern() + { + return $this->_matched; + } +} + +/** + * TUrlMappingPattern class. + * + * TUrlMappingPattern represents a pattern used to parse and construct URLs. + * If the currently requested URL matches the pattern, it will alter + * the THttpRequest parameters. If a constructUrl() call matches the pattern + * parameters, the pattern will generate a valid URL. In both case, only the PATH_INFO + * part of a URL is parsed/constructed using the pattern. + * + * To specify the pattern, set the {@link setPattern Pattern} property. + * {@link setPattern Pattern} takes a string expression with + * parameter names enclosed between a left brace '{' and a right brace '}'. + * The patterns for each parameter can be set using {@link getParameters Parameters} + * attribute collection. For example + * + * + * + * + * In the above example, the pattern contains 3 parameters named "year", + * "month" and "day". The pattern for these parameters are, respectively, + * "\d{4}" (4 digits), "\d{2}" (2 digits) and "\d+" (1 or more digits). + * Essentially, the Parameters attribute name and values are used + * as substrings in replacing the placeholders in the Pattern string + * to form a complete regular expression string. + * + * For more complicated patterns, one may specify the pattern using a regular expression + * by {@link setRegularExpression RegularExpression}. For example, the above pattern + * is equivalent to the following regular expression-based pattern: + * + * #^articles/(?P\d{4})/(?P\d{2})\/(?P\d+)$#u + * + * The above regular expression used the "named group" feature available in PHP. + * If you intended to use the RegularExpression property or + * regular expressions in CDATA sections, notice that you need to escape the slash, + * if you are using the slash as regular expressions delimiter. + * + * Thus, only an url that matches the pattern will be valid. For example, + * a URL http://example.com/index.php/articles/2006/07/21 will match the above pattern, + * while http://example.com/index.php/articles/2006/07/hello will not + * since the "day" parameter pattern is not satisfied. + * + * The parameter values are available through the THttpRequest instance (e.g. + * $this->Request['year']). + * + * The {@link setServiceParameter ServiceParameter} and {@link setServiceID ServiceID} + * (the default ID is 'page') set the service parameter and service id respectively. + * * Since 3.1.4 you can also use simplyfied wildcard patterns to match multiple * ServiceParameters with a single rule. The pattern must contain the placeholder * {*} for the ServiceParameter. For example @@ -465,225 +465,225 @@ class TUrlMapping extends TUrlManager * * .../index.php/admin/listuser/param1-value1/param2-value2. * - * @author Wei Zhuo - * @version $Id$ - * @package System.Web - * @since 3.0.5 - */ -class TUrlMappingPattern extends TComponent -{ - /** - * @var string service parameter such as Page class name. - */ - private $_serviceParameter; - /** - * @var string service ID, default is 'page'. - */ - private $_serviceID='page'; - /** - * @var string url pattern to match. - */ - private $_pattern; - /** - * @var TMap parameter regular expressions. - */ - private $_parameters; - /** - * @var string regular expression pattern. - */ - private $_regexp=''; - - private $_customUrl=true; - - private $_manager; - - private $_caseSensitive=true; - - private $_isWildCardPattern=false; - - private $_urlFormat=THttpRequestUrlFormat::Get; - - private $_separator='/'; - - /** - * @var TUrlMappingPatternSecureConnection - * @since 3.2 - */ - private $_secureConnection = TUrlMappingPatternSecureConnection::Automatic; - - /** - * Constructor. - * @param TUrlManager the URL manager instance - */ - public function __construct(TUrlManager $manager) - { - $this->_manager=$manager; - $this->_parameters=new TAttributeCollection; - $this->_parameters->setCaseSensitive(true); - } - - /** - * @return TUrlManager the URL manager instance - */ - public function getManager() - { - return $this->_manager; - } - - /** - * Initializes the pattern. - * @param TXmlElement configuration for this module. - * @throws TConfigurationException if service parameter is not specified - */ - public function init($config) - { - if($this->_serviceParameter===null) - throw new TConfigurationException('urlmappingpattern_serviceparameter_required', $this->getPattern()); + * @author Wei Zhuo + * @version $Id$ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMappingPattern extends TComponent +{ + /** + * @var string service parameter such as Page class name. + */ + private $_serviceParameter; + /** + * @var string service ID, default is 'page'. + */ + private $_serviceID='page'; + /** + * @var string url pattern to match. + */ + private $_pattern; + /** + * @var TMap parameter regular expressions. + */ + private $_parameters; + /** + * @var string regular expression pattern. + */ + private $_regexp=''; + + private $_customUrl=true; + + private $_manager; + + private $_caseSensitive=true; + + private $_isWildCardPattern=false; + + private $_urlFormat=THttpRequestUrlFormat::Get; + + private $_separator='/'; + + /** + * @var TUrlMappingPatternSecureConnection + * @since 3.2 + */ + private $_secureConnection = TUrlMappingPatternSecureConnection::Automatic; + + /** + * Constructor. + * @param TUrlManager the URL manager instance + */ + public function __construct(TUrlManager $manager) + { + $this->_manager=$manager; + $this->_parameters=new TAttributeCollection; + $this->_parameters->setCaseSensitive(true); + } + + /** + * @return TUrlManager the URL manager instance + */ + public function getManager() + { + return $this->_manager; + } + + /** + * Initializes the pattern. + * @param TXmlElement configuration for this module. + * @throws TConfigurationException if service parameter is not specified + */ + public function init($config) + { + if($this->_serviceParameter===null) + throw new TConfigurationException('urlmappingpattern_serviceparameter_required', $this->getPattern()); if(strpos($this->_serviceParameter,'*')!==false) $this->_isWildCardPattern=true; - } - - /** - * Substitute the parameter key value pairs as named groupings - * in the regular expression matching pattern. - * @return string regular expression pattern with parameter subsitution - */ - protected function getParameterizedPattern() - { - $params=array(); - $values=array(); - foreach($this->_parameters as $key=>$value) - { - $params[]='{'.$key.'}'; - $values[]='(?P<'.$key.'>'.$value.')'; - } + } + + /** + * Substitute the parameter key value pairs as named groupings + * in the regular expression matching pattern. + * @return string regular expression pattern with parameter subsitution + */ + protected function getParameterizedPattern() + { + $params=array(); + $values=array(); + foreach($this->_parameters as $key=>$value) + { + $params[]='{'.$key.'}'; + $values[]='(?P<'.$key.'>'.$value.')'; + } if ($this->getIsWildCardPattern()) { $params[]='{*}'; // service parameter must not contain '=' and '/' $values[]='(?P<'.$this->getServiceID().'>[^=/]+)'; } - $params[]='/'; - $values[]='\\/'; - $regexp=str_replace($params,$values,trim($this->getPattern(),'/').'/'); + $params[]='/'; + $values[]='\\/'; + $regexp=str_replace($params,$values,trim($this->getPattern(),'/').'/'); if ($this->_urlFormat===THttpRequestUrlFormat::Get) - $regexp='/^'.$regexp.'$/u'; + $regexp='/^'.$regexp.'$/u'; + else + $regexp='/^'.$regexp.'(?P.*)$/u'; + + if(!$this->getCaseSensitive()) + $regexp.='i'; + return $regexp; + } + + /** + * @return string full regular expression mapping pattern + */ + public function getRegularExpression() + { + return $this->_regexp; + } + + /** + * @param string full regular expression mapping pattern. + */ + public function setRegularExpression($value) + { + $this->_regexp=$value; + } + + /** + * @return boolean whether the {@link getPattern Pattern} should be treated as case sensititve. Defaults to true. + */ + public function getCaseSensitive() + { + return $this->_caseSensitive; + } + + /** + * @param boolean whether the {@link getPattern Pattern} should be treated as case sensititve. + */ + public function setCaseSensitive($value) + { + $this->_caseSensitive=TPropertyValue::ensureBoolean($value); + } + + /** + * @param string service parameter, such as page class name. + */ + public function setServiceParameter($value) + { + $this->_serviceParameter=$value; + } + + /** + * @return string service parameter, such as page class name. + */ + public function getServiceParameter() + { + return $this->_serviceParameter; + } + + /** + * @param string service id to handle. + */ + public function setServiceID($value) + { + $this->_serviceID=$value; + } + + /** + * @return string service id. + */ + public function getServiceID() + { + return $this->_serviceID; + } + + /** + * @return string url pattern to match. Defaults to ''. + */ + public function getPattern() + { + return $this->_pattern; + } + + /** + * @param string url pattern to match. + */ + public function setPattern($value) + { + $this->_pattern = $value; + } + + /** + * @return TAttributeCollection parameter key value pairs. + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @param TAttributeCollection new parameter key value pairs. + */ + public function setParameters($value) + { + $this->_parameters=$value; + } + + /** + * Uses URL pattern (or full regular expression if available) to + * match the given url path. + * @param THttpRequest the request module + * @return array matched parameters, empty if no matches. + */ + public function getPatternMatches($request) + { + $matches=array(); + if(($pattern=$this->getRegularExpression())!=='') + preg_match($pattern,$request->getPathInfo(),$matches); else - $regexp='/^'.$regexp.'(?P.*)$/u'; - - if(!$this->getCaseSensitive()) - $regexp.='i'; - return $regexp; - } - - /** - * @return string full regular expression mapping pattern - */ - public function getRegularExpression() - { - return $this->_regexp; - } - - /** - * @param string full regular expression mapping pattern. - */ - public function setRegularExpression($value) - { - $this->_regexp=$value; - } - - /** - * @return boolean whether the {@link getPattern Pattern} should be treated as case sensititve. Defaults to true. - */ - public function getCaseSensitive() - { - return $this->_caseSensitive; - } - - /** - * @param boolean whether the {@link getPattern Pattern} should be treated as case sensititve. - */ - public function setCaseSensitive($value) - { - $this->_caseSensitive=TPropertyValue::ensureBoolean($value); - } - - /** - * @param string service parameter, such as page class name. - */ - public function setServiceParameter($value) - { - $this->_serviceParameter=$value; - } - - /** - * @return string service parameter, such as page class name. - */ - public function getServiceParameter() - { - return $this->_serviceParameter; - } - - /** - * @param string service id to handle. - */ - public function setServiceID($value) - { - $this->_serviceID=$value; - } - - /** - * @return string service id. - */ - public function getServiceID() - { - return $this->_serviceID; - } - - /** - * @return string url pattern to match. Defaults to ''. - */ - public function getPattern() - { - return $this->_pattern; - } - - /** - * @param string url pattern to match. - */ - public function setPattern($value) - { - $this->_pattern = $value; - } - - /** - * @return TAttributeCollection parameter key value pairs. - */ - public function getParameters() - { - return $this->_parameters; - } - - /** - * @param TAttributeCollection new parameter key value pairs. - */ - public function setParameters($value) - { - $this->_parameters=$value; - } - - /** - * Uses URL pattern (or full regular expression if available) to - * match the given url path. - * @param THttpRequest the request module - * @return array matched parameters, empty if no matches. - */ - public function getPatternMatches($request) - { - $matches=array(); - if(($pattern=$this->getRegularExpression())!=='') - preg_match($pattern,$request->getPathInfo(),$matches); - else - preg_match($this->getParameterizedPattern(),trim($request->getPathInfo(),'/').'/',$matches); + preg_match($this->getParameterizedPattern(),trim($request->getPathInfo(),'/').'/',$matches); if($this->getIsWildCardPattern() && isset($matches[$this->_serviceID])) $matches[$this->_serviceID]=str_replace('*',$matches[$this->_serviceID],$this->_serviceParameter); @@ -708,261 +708,261 @@ class TUrlMappingPattern extends TComponent unset($matches['urlparams']); } - return $matches; - } - - /** - * Returns a value indicating whether to use this pattern to construct URL. - * @return boolean whether to enable custom constructUrl. Defaults to true. - * @since 3.1.1 - */ - public function getEnableCustomUrl() - { - return $this->_customUrl; - } - - /** - * Sets a value indicating whether to enable custom constructUrl using this pattern - * @param boolean whether to enable custom constructUrl. - */ - public function setEnableCustomUrl($value) - { - $this->_customUrl=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether this pattern is a wildcard pattern - * @since 3.1.4 - */ - public function getIsWildCardPattern() { - return $this->_isWildCardPattern; - } - - /** - * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. - */ - public function getUrlFormat() - { - return $this->_urlFormat; - } - - /** - * Sets the format of URLs constructed and interpreted by this pattern. - * A Get URL format is like index.php?name1=value1&name2=value2 - * while a Path URL format is like index.php/name1/value1/name2/value. - * The separating character between name and value can be configured with - * {@link setUrlParamSeparator} and defaults to '/'. - * Changing the UrlFormat will affect {@link constructUrl} and how GET variables - * are parsed. - * @param THttpRequestUrlFormat the format of URLs. - * @param since 3.1.4 - */ - public function setUrlFormat($value) - { - $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); - } - - /** - * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'. - */ - public function getUrlParamSeparator() - { - return $this->_separator; - } - - /** - * @param string separator used to separate GET variable name and value when URL format is Path. - * @throws TInvalidDataValueException if the separator is not a single character - */ - public function setUrlParamSeparator($value) - { - if(strlen($value)===1) - $this->_separator=$value; - else - throw new TInvalidDataValueException('httprequest_separator_invalid'); - } - - /** - * @return TUrlMappingPatternSecureConnection the SecureConnection behavior. Defaults to {@link TUrlMappingPatternSecureConnection::Automatic Automatic} - * @since 3.2 - */ - public function getSecureConnection() - { - return $this->_secureConnection; - } - - /** - * @param TUrlMappingPatternSecureConnection the SecureConnection behavior. - * @since 3.2 - */ - public function setSecureConnection($value) - { - $this->_secureConnection = TPropertyValue::ensureEnum($value, 'TUrlMappingPatternSecureConnection'); - } - - /** - * @param array list of GET items to be put in the constructed URL - * @return boolean whether this pattern IS the one for constructing the URL with the specified GET items. - * @since 3.1.1 - */ - public function supportCustomUrl($getItems) - { - if(!$this->_customUrl || $this->getPattern()===null) - return false; - foreach($this->_parameters as $key=>$value) - { - if(!isset($getItems[$key])) - return false; - } - return true; - } - - /** - * Constructs a URL using this pattern. - * @param array list of GET variables - * @param boolean whether the ampersand should be encoded in the constructed URL - * @param boolean whether the GET variables should be encoded in the constructed URL - * @return string the constructed URL - * @since 3.1.1 - */ - public function constructUrl($getItems,$encodeAmpersand,$encodeGetItems) - { - $extra=array(); - $replace=array(); - // for the GET variables matching the pattern, put them in the URL path - foreach($getItems as $key=>$value) - { - if($this->_parameters->contains($key) || $key==='*' && $this->getIsWildCardPattern()) - $replace['{'.$key.'}']=$encodeGetItems ? rawurlencode($value) : $value; - else - $extra[$key]=$value; - } - - $url=$this->_manager->getUrlPrefix().'/'.ltrim(strtr($this->getPattern(),$replace),'/'); - - // for the rest of the GET variables, put them in the query string - if(count($extra)>0) - { + return $matches; + } + + /** + * Returns a value indicating whether to use this pattern to construct URL. + * @return boolean whether to enable custom constructUrl. Defaults to true. + * @since 3.1.1 + */ + public function getEnableCustomUrl() + { + return $this->_customUrl; + } + + /** + * Sets a value indicating whether to enable custom constructUrl using this pattern + * @param boolean whether to enable custom constructUrl. + */ + public function setEnableCustomUrl($value) + { + $this->_customUrl=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether this pattern is a wildcard pattern + * @since 3.1.4 + */ + public function getIsWildCardPattern() { + return $this->_isWildCardPattern; + } + + /** + * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. + */ + public function getUrlFormat() + { + return $this->_urlFormat; + } + + /** + * Sets the format of URLs constructed and interpreted by this pattern. + * A Get URL format is like index.php?name1=value1&name2=value2 + * while a Path URL format is like index.php/name1/value1/name2/value. + * The separating character between name and value can be configured with + * {@link setUrlParamSeparator} and defaults to '/'. + * Changing the UrlFormat will affect {@link constructUrl} and how GET variables + * are parsed. + * @param THttpRequestUrlFormat the format of URLs. + * @param since 3.1.4 + */ + public function setUrlFormat($value) + { + $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); + } + + /** + * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'. + */ + public function getUrlParamSeparator() + { + return $this->_separator; + } + + /** + * @param string separator used to separate GET variable name and value when URL format is Path. + * @throws TInvalidDataValueException if the separator is not a single character + */ + public function setUrlParamSeparator($value) + { + if(strlen($value)===1) + $this->_separator=$value; + else + throw new TInvalidDataValueException('httprequest_separator_invalid'); + } + + /** + * @return TUrlMappingPatternSecureConnection the SecureConnection behavior. Defaults to {@link TUrlMappingPatternSecureConnection::Automatic Automatic} + * @since 3.2 + */ + public function getSecureConnection() + { + return $this->_secureConnection; + } + + /** + * @param TUrlMappingPatternSecureConnection the SecureConnection behavior. + * @since 3.2 + */ + public function setSecureConnection($value) + { + $this->_secureConnection = TPropertyValue::ensureEnum($value, 'TUrlMappingPatternSecureConnection'); + } + + /** + * @param array list of GET items to be put in the constructed URL + * @return boolean whether this pattern IS the one for constructing the URL with the specified GET items. + * @since 3.1.1 + */ + public function supportCustomUrl($getItems) + { + if(!$this->_customUrl || $this->getPattern()===null) + return false; + foreach($this->_parameters as $key=>$value) + { + if(!isset($getItems[$key])) + return false; + } + return true; + } + + /** + * Constructs a URL using this pattern. + * @param array list of GET variables + * @param boolean whether the ampersand should be encoded in the constructed URL + * @param boolean whether the GET variables should be encoded in the constructed URL + * @return string the constructed URL + * @since 3.1.1 + */ + public function constructUrl($getItems,$encodeAmpersand,$encodeGetItems) + { + $extra=array(); + $replace=array(); + // for the GET variables matching the pattern, put them in the URL path + foreach($getItems as $key=>$value) + { + if($this->_parameters->contains($key) || $key==='*' && $this->getIsWildCardPattern()) + $replace['{'.$key.'}']=$encodeGetItems ? rawurlencode($value) : $value; + else + $extra[$key]=$value; + } + + $url=$this->_manager->getUrlPrefix().'/'.ltrim(strtr($this->getPattern(),$replace),'/'); + + // for the rest of the GET variables, put them in the query string + if(count($extra)>0) + { if ($this->_urlFormat===THttpRequestUrlFormat::Path && $this->getIsWildCardPattern()) { foreach ($extra as $name=>$value) $url.='/'.$name.$this->_separator.($encodeGetItems?rawurlencode($value):$value); return $url; } - $url2=''; - $amp=$encodeAmpersand?'&':'&'; - if($encodeGetItems) - { - foreach($extra as $name=>$value) - { - if(is_array($value)) - { - $name=rawurlencode($name.'[]'); - foreach($value as $v) - $url2.=$amp.$name.'='.rawurlencode($v); - } - else - $url2.=$amp.rawurlencode($name).'='.rawurlencode($value); - } - } - else - { - foreach($extra as $name=>$value) - { - if(is_array($value)) - { - foreach($value as $v) - $url2.=$amp.$name.'[]='.$v; - } - else - $url2.=$amp.$name.'='.$value; - } - } - $url=$url.'?'.substr($url2,strlen($amp)); - } - return $this -> applySecureConnectionPrefix($url); - } - - /** - * Apply behavior of {@link SecureConnection} property by conditionaly prefixing - * URL with {@link THttpRequest::getBaseUrl()} - * - * @param string $url - * @return string - * @since 3.2 - */ - protected function applySecureConnectionPrefix($url) - { - static $request; - if($request === null) $request = Prado::getApplication() -> getRequest(); - - static $isSecureConnection; - if($isSecureConnection === null) $isSecureConnection = $request -> getIsSecureConnection(); - - switch($this -> getSecureConnection()) - { - case TUrlMappingPatternSecureConnection::EnableIfNotSecure: - if($isSecureConnection) return $url; - return $request -> getBaseUrl(true) . $url; - break; - case TUrlMappingPatternSecureConnection::DisableIfSecure: - if(!$isSecureConnection) return $url; - return $request -> getBaseUrl(false) . $url; - break; - case TUrlMappingPatternSecureConnection::Enable: - return $request -> getBaseUrl(true) . $url; - break; - case TUrlMappingPatternSecureConnection::Disable: - return $request -> getBaseUrl(false) . $url; - break; - case TUrlMappingPatternSecureConnection::Automatic: - default: - return $url; - break; - } - } -} - -/** - * TUrlMappingPatternSecureConnection class - * - * TUrlMappingPatternSecureConnection defines the enumerable type for the possible SecureConnection - * URL prefix behavior that can be used by {@link TUrlMappingPattern::constructUrl()}. - * - * @author Yves Berkholz - * @version $Id$ - * @package System.Web - * @since 3.2 - */ -class TUrlMappingPatternSecureConnection extends TEnumerable -{ - /** - * Keep current SecureConnection status - * means no prefixing - */ - const Automatic = 'Automatic'; - - /** - * Force use secured connection - * always prefixing with https://example.com/path/to/app - */ - const Enable = 'Enable'; - - /** - * Force use unsecured connection - * always prefixing with http://example.com/path/to/app - */ - const Disable = 'Disable'; - - /** - * Force use secured connection, if in unsecured mode - * prefixing with https://example.com/path/to/app - */ - const EnableIfNotSecure = 'EnableIfNotSecure'; - - /** - * Force use unsecured connection, if in secured mode - * prefixing with https://example.com/path/to/app - */ - const DisableIfSecure = 'DisableIfSecure'; -} + $url2=''; + $amp=$encodeAmpersand?'&':'&'; + if($encodeGetItems) + { + foreach($extra as $name=>$value) + { + if(is_array($value)) + { + $name=rawurlencode($name.'[]'); + foreach($value as $v) + $url2.=$amp.$name.'='.rawurlencode($v); + } + else + $url2.=$amp.rawurlencode($name).'='.rawurlencode($value); + } + } + else + { + foreach($extra as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url2.=$amp.$name.'[]='.$v; + } + else + $url2.=$amp.$name.'='.$value; + } + } + $url=$url.'?'.substr($url2,strlen($amp)); + } + return $this -> applySecureConnectionPrefix($url); + } + + /** + * Apply behavior of {@link SecureConnection} property by conditionaly prefixing + * URL with {@link THttpRequest::getBaseUrl()} + * + * @param string $url + * @return string + * @since 3.2 + */ + protected function applySecureConnectionPrefix($url) + { + static $request; + if($request === null) $request = Prado::getApplication() -> getRequest(); + + static $isSecureConnection; + if($isSecureConnection === null) $isSecureConnection = $request -> getIsSecureConnection(); + + switch($this -> getSecureConnection()) + { + case TUrlMappingPatternSecureConnection::EnableIfNotSecure: + if($isSecureConnection) return $url; + return $request -> getBaseUrl(true) . $url; + break; + case TUrlMappingPatternSecureConnection::DisableIfSecure: + if(!$isSecureConnection) return $url; + return $request -> getBaseUrl(false) . $url; + break; + case TUrlMappingPatternSecureConnection::Enable: + return $request -> getBaseUrl(true) . $url; + break; + case TUrlMappingPatternSecureConnection::Disable: + return $request -> getBaseUrl(false) . $url; + break; + case TUrlMappingPatternSecureConnection::Automatic: + default: + return $url; + break; + } + } +} + +/** + * TUrlMappingPatternSecureConnection class + * + * TUrlMappingPatternSecureConnection defines the enumerable type for the possible SecureConnection + * URL prefix behavior that can be used by {@link TUrlMappingPattern::constructUrl()}. + * + * @author Yves Berkholz + * @version $Id$ + * @package System.Web + * @since 3.2 + */ +class TUrlMappingPatternSecureConnection extends TEnumerable +{ + /** + * Keep current SecureConnection status + * means no prefixing + */ + const Automatic = 'Automatic'; + + /** + * Force use secured connection + * always prefixing with https://example.com/path/to/app + */ + const Enable = 'Enable'; + + /** + * Force use unsecured connection + * always prefixing with http://example.com/path/to/app + */ + const Disable = 'Disable'; + + /** + * Force use secured connection, if in unsecured mode + * prefixing with https://example.com/path/to/app + */ + const EnableIfNotSecure = 'EnableIfNotSecure'; + + /** + * Force use unsecured connection, if in secured mode + * prefixing with https://example.com/path/to/app + */ + const DisableIfSecure = 'DisableIfSecure'; +} 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