summaryrefslogtreecommitdiff
path: root/lib/prado/framework/Web/UI/WebControls
diff options
context:
space:
mode:
Diffstat (limited to 'lib/prado/framework/Web/UI/WebControls')
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TAccordion.php740
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TBaseDataList.php188
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TBaseValidator.php747
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TBoundColumn.php247
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TBulletedList.php487
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TButton.php404
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TButtonColumn.php277
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCaptcha.php493
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCaptchaValidator.php125
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCheckBox.php536
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCheckBoxColumn.php121
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCheckBoxList.php509
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TClientScript.php155
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TColorPicker.php286
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCompareValidator.php262
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TConditional.php141
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TContent.php45
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TContentPlaceHolder.php46
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TCustomValidator.php204
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataBoundControl.php582
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataGrid.php2244
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataGridColumn.php564
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataGridItemRenderer.php28
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataGridPagerStyle.php253
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataList.php1760
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataListItemRenderer.php170
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataRenderer.php50
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataSourceControl.php114
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataSourceView.php202
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDataTypeValidator.php139
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDatePicker.php978
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDropDownList.php152
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TDropDownListColumn.php319
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TEditCommandColumn.php263
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TEmailAddressValidator.php98
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TExpression.php60
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TFileUpload.php279
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TFlushOutput.php83
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TFont.php316
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THead.php372
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader1.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader2.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader3.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader4.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader5.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THeader6.php34
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THiddenField.php222
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THtmlArea.php535
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THtmlArea4.php479
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THtmlElement.php66
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THyperLink.php250
-rw-r--r--lib/prado/framework/Web/UI/WebControls/THyperLinkColumn.php271
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TImage.php155
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TImageButton.php438
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TImageMap.php828
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TInlineFrame.php314
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TItemDataRenderer.php81
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TJavascriptLogger.php91
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TKeyboard.php187
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TLabel.php152
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TLinkButton.php332
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TListBox.php259
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TListControl.php923
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TListControlValidator.php223
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TListItem.php182
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TLiteral.php110
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TLiteralColumn.php147
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TMarkdown.php72
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TMultiView.php374
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TOutputCache.php616
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TPager.php810
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TPanel.php239
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TPanelStyle.php274
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TPlaceHolder.php26
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRadioButton.php304
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRadioButtonList.php107
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRangeValidator.php357
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRatingList.php359
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TReCaptcha.php271
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TReCaptchaValidator.php135
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRegularExpressionValidator.php142
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRepeatInfo.php555
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRepeater.php1019
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRepeaterItemRenderer.php48
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TRequiredFieldValidator.php152
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TSafeHtml.php83
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TSlider.php570
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TStatements.php61
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TStyle.php885
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TStyleSheet.php89
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTabPanel.php728
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTable.php406
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTableCell.php220
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTableFooterRow.php45
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTableHeaderCell.php121
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTableHeaderRow.php45
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTableRow.php204
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTemplateColumn.php254
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTextBox.php648
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTextHighlighter.php210
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TTextProcessor.php84
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TValidationSummary.php531
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TWebControl.php498
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TWebControlAdapter.php69
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TWebControlDecorator.php382
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TWizard.php2145
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php154
-rw-r--r--lib/prado/framework/Web/UI/WebControls/TXmlTransform.php193
-rwxr-xr-xlib/prado/framework/Web/UI/WebControls/assets/TSlider/TSlider.css96
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.pngbin0 -> 286 bytes
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.pngbin0 -> 249 bytes
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/accordion.css28
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/captcha.php223
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/keyboard.css80
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/tabpanel.css71
-rw-r--r--lib/prado/framework/Web/UI/WebControls/assets/verase.ttfbin0 -> 60280 bytes
116 files changed, 36471 insertions, 0 deletions
diff --git a/lib/prado/framework/Web/UI/WebControls/TAccordion.php b/lib/prado/framework/Web/UI/WebControls/TAccordion.php
new file mode 100644
index 0000000..d193613
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TAccordion.php
@@ -0,0 +1,740 @@
+<?php
+/**
+ * TAccordion class file.
+ *
+ * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+/**
+ * Class TAccordion.
+ *
+ * TAccordion displays an accordion control. Users can click on the view headers to switch among
+ * different accordion views. Each accordion view is an independent panel that can contain arbitrary content.
+ *
+ * A TAccordion control consists of one or several {@link TAccordionView} controls representing the possible
+ * accordion views. At any time, only one accordion view is visible (active), which is specified by any of
+ * the following properties:
+ * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection.
+ * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view.
+ * - {@link setActiveView ActiveView} - the visible view instance.
+ * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID}
+ * are set, the latter takes precedence.
+ *
+ * TAccordion uses CSS to specify the appearance of the accordion headers and panel. By default,
+ * an embedded CSS file will be published which contains the default CSS for TTabPanel.
+ * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property.
+ * The following properties specify the CSS classes used for elements in a TAccordion:
+ * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'accordion');
+ * - {@link setHeaderCssClass HeaderCssClass} - the CSS class name for nonactive accordion div elements (defaults to 'accordion-header');
+ * - {@link setActiveHeaderCssClass ActiveHeaderCssClass} - the CSS class name for the active accordion div element (defaults to 'accordion-header-active');
+ * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'accordion-view');
+ *
+ * When the user clicks on a view header, the switch between the old visible view and the clicked one is animated.
+ * You can use the {@link setAnimationDuration AnimationDuration} property to set the animation length in seconds;
+ * it defaults to 1 second, and when set to 0 it will produce an immediate switch with no animation.
+ *
+ * The TAccordion auto-sizes itself to the largest of all views, so it can encompass all of them without scrolling.
+ * If you want to specify a fixed height (in pixels), use the {@link setViewHeight ViewHeight} property.
+ * When a TAccordion is nested inside another, it's adviced to manually specify a {@link setViewHeight ViewHeight} for the internal TAccordion
+ *
+ * To use TAccordion, write a template like following:
+ * <code>
+ * <com:TAccordion>
+ * <com:TAccordionView Caption="View 1">
+ * content for view 1
+ * </com:TAccordionView>
+ * <com:TAccordionView Caption="View 2">
+ * content for view 2
+ * </com:TAccordionView>
+ * <com:TAccordionView Caption="View 3">
+ * content for view 3
+ * </com:TAccordionView>
+ * </com:TAccordion>
+ * </code>
+ *
+ * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+class TAccordion extends TWebControl implements IPostBackDataHandler
+{
+ private $_dataChanged=false;
+
+ /**
+ * @return string tag name for the control
+ */
+ protected function getTagName()
+ {
+ return 'div';
+ }
+
+ /**
+ * Adds object parsed from template to the control.
+ * This method adds only {@link TAccordionView} objects into the {@link getViews Views} collection.
+ * All other objects are ignored.
+ * @param mixed object parsed from template
+ */
+ public function addParsedObject($object)
+ {
+ if($object instanceof TAccordionView)
+ $this->getControls()->add($object);
+ }
+
+ /**
+ * Returns the index of the active accordion view.
+ * Note, this property may not return the correct index.
+ * To ensure the correctness, call {@link getActiveView()} first.
+ * @return integer the zero-based index of the active accordion view. If -1, it means no active accordion view. Default is 0 (the first view is active).
+ */
+ public function getActiveViewIndex()
+ {
+ return $this->getViewState('ActiveViewIndex',0);
+ }
+
+ /**
+ * @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)
+ {
+ $this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0);
+ $this->setActiveViewID('');
+ }
+
+ /**
+ * Returns the ID of the active accordion view.
+ * Note, this property may not return the correct ID.
+ * To ensure the correctness, call {@link getActiveView()} first.
+ * @return string The ID of the active accordion view. Defaults to '', meaning not set.
+ */
+ public function getActiveViewID()
+ {
+ return $this->getViewState('ActiveViewID','');
+ }
+
+ /**
+ * @param string The ID of the active accordion view.
+ */
+ public function setActiveViewID($value)
+ {
+ $this->setViewState('ActiveViewID',$value,'');
+ }
+
+ /**
+ * Returns the currently active view.
+ * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to
+ * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly.
+ * @return TAccordionView the currently active view, null if no active view
+ * @throws TInvalidDataValueException if the active view ID or index set previously is invalid
+ */
+ public function getActiveView()
+ {
+ $activeView=null;
+ $views=$this->getViews();
+ if(($id=$this->getActiveViewID())!=='')
+ {
+ if(($index=$views->findIndexByID($id))>=0)
+ $activeView=$views->itemAt($index);
+ else
+ throw new TInvalidDataValueException('accordion_activeviewid_invalid',$id);
+ }
+ else if(($index=$this->getActiveViewIndex())>=0)
+ {
+ if($index<$views->getCount())
+ $activeView=$views->itemAt($index);
+ else
+ throw new TInvalidDataValueException('accordion_activeviewindex_invalid',$index);
+ }
+ else
+ {
+ foreach($views as $index=>$view)
+ {
+ if($view->getActive())
+ {
+ $activeView=$view;
+ break;
+ }
+ }
+ }
+ if($activeView!==null)
+ $this->activateView($activeView);
+ return $activeView;
+ }
+
+ /**
+ * @param TAccordionView the view to be activated
+ * @throws TInvalidOperationException if the view is not in the view collection
+ */
+ public function setActiveView($view)
+ {
+ if($this->getViews()->indexOf($view)>=0)
+ $this->activateView($view);
+ else
+ throw new TInvalidOperationException('accordion_view_inexistent');
+ }
+
+ /**
+ * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''.
+ */
+ public function getCssUrl()
+ {
+ return $this->getViewState('CssUrl','default');
+ }
+
+ /**
+ * @param string URL for the CSS file including all relevant CSS class definitions.
+ */
+ public function setCssUrl($value)
+ {
+ $this->setViewState('CssUrl',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string CSS class for the whole accordion control div.
+ */
+ public function getCssClass()
+ {
+ $cssClass=parent::getCssClass();
+ return $cssClass===''?'accordion':$cssClass;
+ }
+
+ /**
+ * @return string CSS class for the currently displayed view div. Defaults to 'accordion-view'.
+ */
+ public function getViewCssClass()
+ {
+ return $this->getViewStyle()->getCssClass();
+ }
+
+ /**
+ * @param string CSS class for the currently displayed view div.
+ */
+ public function setViewCssClass($value)
+ {
+ $this->getViewStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return string CSS class for the currently displayed view div. Defaults to 'accordion-view'.
+ */
+ public function getAnimationDuration()
+ {
+ return $this->getViewState('AnimationDuration','1');
+ }
+
+ /**
+ * @param string CSS class for the currently displayed view div.
+ */
+ public function setAnimationDuration($value)
+ {
+ $this->setViewState('AnimationDuration',$value);
+ }
+
+ /**
+ * @return TStyle the style for all the view div
+ */
+ public function getViewStyle()
+ {
+ if(($style=$this->getViewState('ViewStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('accordion-view');
+ $this->setViewState('ViewStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * @return string CSS class for view headers. Defaults to 'accordion-header'.
+ */
+ public function getHeaderCssClass()
+ {
+ return $this->getHeaderStyle()->getCssClass();
+ }
+
+ /**
+ * @param string CSS class for view headers.
+ */
+ public function setHeaderCssClass($value)
+ {
+ $this->getHeaderStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return TStyle the style for all the inactive header div
+ */
+ public function getHeaderStyle()
+ {
+ if(($style=$this->getViewState('HeaderStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('accordion-header');
+ $this->setViewState('HeaderStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * @return string Extra CSS class for the active header. Defaults to 'accordion-header-active'.
+ */
+ public function getActiveHeaderCssClass()
+ {
+ return $this->getActiveHeaderStyle()->getCssClass();
+ }
+
+ /**
+ * @param string Extra CSS class for the active header. Will be added to the normal header specified by HeaderCssClass.
+ */
+ public function setActiveHeaderCssClass($value)
+ {
+ $this->getActiveHeaderStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return TStyle the style for the active header div
+ */
+ public function getActiveHeaderStyle()
+ {
+ if(($style=$this->getViewState('ActiveHeaderStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('accordion-header-active');
+ $this->setViewState('ActiveHeaderStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * @return integer Maximum height for the accordion views. If non specified, the accordion will auto-sized to the largest of all views, so it can encompass all of them without scrolling
+ */
+ public function getViewHeight()
+ {
+ return TPropertyValue::ensureInteger($this->getViewState('ViewHeight'));
+ }
+
+ /**
+ * @param integer Maximum height for the accordion views. If any of the accordion's views' content is larger, those views will be made scrollable when activated
+ */
+ public function setViewHeight($value)
+ {
+ $this->setViewState('ViewHeight', TPropertyValue::ensureInteger($value));
+ }
+
+ /**
+ * Activates the specified view.
+ * If there is any other view currently active, it will be deactivated.
+ * @param TAccordionView the view to be activated. If null, all views will be deactivated.
+ */
+ protected function activateView($view)
+ {
+ $this->setActiveViewIndex(-1);
+ $this->setActiveViewID('');
+ foreach($this->getViews() as $index=>$v)
+ {
+ if($view===$v)
+ {
+ $this->setActiveViewIndex($index);
+ $this->setActiveViewID($view->getID(false));
+ $view->setActive(true);
+ }
+ else
+ $v->setActive(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(($index=$values[$this->getClientID().'_1'])!==null)
+ {
+ $index=(int)$index;
+ $currentIndex=$this->getActiveViewIndex();
+ if($currentIndex!==$index)
+ {
+ $this->setActiveViewID(''); // clear up view ID
+ $this->setActiveViewIndex($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 getActiveViewIndex ActiveViewIndex} property
+ * is changed on postback.
+ * This method is primarly used by framework developers.
+ */
+ public function raisePostDataChangedEvent()
+ {
+ // do nothing
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Adds attributes to renderer.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ $writer->addAttribute('id',$this->getClientID());
+ $this->setCssClass($this->getCssClass());
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
+ * 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->getActiveView(); // determine the active view
+ $this->registerStyleSheet();
+ }
+
+ /**
+ * Registers the CSS relevant to the TAccordion.
+ * 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()
+ {
+ $url = $this->getCssUrl();
+
+ if($url === '') {
+ return;
+ }
+
+ if($url === 'default') {
+ $url = $this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'accordion.css');
+ }
+
+ if($url !== '') {
+ $this->getPage()->getClientScript()->registerStyleSheetFile($url, $url);
+ }
+ }
+
+ /**
+ * Registers the relevant JavaScript.
+ */
+ protected function registerClientScript()
+ {
+ $id=$this->getClientID();
+ $options=TJavaScript::encode($this->getClientOptions());
+ $className=$this->getClientClassName();
+ $page=$this->getPage();
+ $cs=$page->getClientScript();
+ $cs->registerPradoScript('accordion');
+ $code="new $className($options);";
+ $cs->registerEndScript("prado:$id", $code);
+ // ensure an item is always active and visible
+ $index = $this->getActiveViewIndex();
+ if(!$this->getViews()->itemAt($index)->Visible)
+ $index=0;
+ $cs->registerHiddenField($id.'_1', $index);
+ $page->registerRequiresPostData($this);
+ $page->registerRequiresPostData($id."_1");
+ }
+
+ /**
+ * 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.TAccordion';
+ }
+
+ /**
+ * @return array the options for JavaScript
+ */
+ protected function getClientOptions()
+ {
+ $options['ID'] = $this->getClientID();
+ $options['ActiveHeaderCssClass'] = $this->getActiveHeaderCssClass();
+ $options['HeaderCssClass'] = $this->getHeaderCssClass();
+ $options['Duration'] = $this->getAnimationDuration();
+
+ if (($viewheight = $this->getViewHeight())>0)
+ $options['maxHeight'] = $viewheight;
+ $views = array();
+ foreach($this->getViews() as $view)
+ $views[$view->getClientID()] = $view->getVisible() ? '1': '0';
+ $options['Views'] = $views;
+
+ return $options;
+ }
+
+ /**
+ * Creates a control collection object that is to be used to hold child controls
+ * @return TAccordionViewCollection control collection
+ */
+ protected function createControlCollection()
+ {
+ return new TAccordionViewCollection($this);
+ }
+
+ /**
+ * @return TAccordionViewCollection list of {@link TAccordionView} controls
+ */
+ public function getViews()
+ {
+ return $this->getControls();
+ }
+
+ public function render($writer)
+ {
+ $this->registerClientScript();
+ parent::render($writer);
+ }
+
+ /**
+ * Renders body contents of the accordion control.
+ * @param THtmlWriter the writer used for the rendering purpose.
+ */
+ public function renderContents($writer)
+ {
+ $views=$this->getViews();
+ if($views->getCount()>0)
+ {
+ $writer->writeLine();
+ foreach($views as $view)
+ {
+ $view->renderHeader($writer);
+ $view->renderControl($writer);
+ $writer->writeLine();
+ }
+ }
+ }
+
+}
+
+/**
+ * Class TAccordionView.
+ *
+ * TAccordionView represents a single view in a {@link TAccordion}.
+ *
+ * TAccordionView is represented inside the {@link TAccordion} with an header label whose text is defined by
+ * the {@link setCaption Caption} property; optionally the label can be an hyperlink: use the
+ * {@link setNavigateUrl NavigateUrl} property to define the destination url.
+ *
+ * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+class TAccordionView extends TWebControl
+{
+ private $_active=false;
+
+ /**
+ * @return the tag name for the view element
+ */
+ protected function getTagName()
+ {
+ return 'div';
+ }
+
+ /**
+ * Adds attributes to renderer.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ if(!$this->getActive() && $this->getPage()->getClientSupportsJavaScript())
+ $this->getStyle()->setStyleField('display','none');
+
+ $this->getStyle()->mergeWith($this->getParent()->getViewStyle());
+
+ parent::addAttributesToRender($writer);
+
+ $writer->addAttribute('id',$this->getClientID());
+ }
+
+ /**
+ * @return string the caption displayed on this header. Defaults to ''.
+ */
+ public function getCaption()
+ {
+ return $this->getViewState('Caption','');
+ }
+
+ /**
+ * @param string the caption displayed on this header
+ */
+ public function setCaption($value)
+ {
+ $this->setViewState('Caption',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string the URL of the target page. Defaults to ''.
+ */
+ public function getNavigateUrl()
+ {
+ return $this->getViewState('NavigateUrl','');
+ }
+
+ /**
+ * Sets the URL of the target page.
+ * If not empty, clicking on this header will redirect the browser to the specified URL.
+ * @param string the URL of the target page.
+ */
+ public function setNavigateUrl($value)
+ {
+ $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string the text content displayed on this view. Defaults to ''.
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * Sets the text content to be displayed on this view.
+ * If this is not empty, the child content of the view will be ignored.
+ * @param string the text content displayed on this view
+ */
+ public function setText($value)
+ {
+ $this->setViewState('Text',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return boolean whether this accordion view is active. Defaults to false.
+ */
+ public function getActive()
+ {
+ return $this->_active;
+ }
+
+ /**
+ * @param boolean whether this accordion view is active.
+ */
+ public function setActive($value)
+ {
+ $this->_active=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * Renders body contents of the accordion view.
+ * @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);
+ }
+
+ /**
+ * Renders the header associated with the accordion view.
+ * @param THtmlWriter the writer for rendering purpose.
+ */
+ public function renderHeader($writer)
+ {
+ if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript())
+ {
+ $writer->addAttribute('id',$this->getClientID().'_0');
+
+ $style=$this->getActive()?$this->getParent()->getActiveHeaderStyle():$this->getParent()->getHeaderStyle();
+
+ $style->addAttributesToRender($writer);
+
+ $writer->renderBeginTag($this->getTagName());
+
+ $this->renderHeaderContent($writer);
+
+ $writer->renderEndTag();
+ }
+ }
+
+ /**
+ * Renders the content in the header.
+ * By default, a hyperlink is displayed.
+ * @param THtmlWriter the HTML writer
+ */
+ protected function renderHeaderContent($writer)
+ {
+ $url = $this->getNavigateUrl();
+ if(($caption=$this->getCaption())==='')
+ $caption='&nbsp;';
+
+ if ($url!='')
+ $writer->write("<a href=\"{$url}\">");
+ $writer->write("{$caption}");
+ if ($url!='')
+ $writer->write("</a>");
+ }
+}
+
+/**
+ * Class TAccordionViewCollection.
+ *
+ * TAccordionViewCollection is a collection of {@link TAccordionView} to be used inside a {@link TAccordion}.
+ *
+ * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+class TAccordionViewCollection extends TControlCollection
+{
+ /**
+ * Inserts an item at the specified position.
+ * This overrides the parent implementation by performing sanity check on the type of new item.
+ * @param integer the speicified position.
+ * @param mixed new item
+ * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TAccordionView} object.
+ */
+ public function insertAt($index,$item)
+ {
+ if($item instanceof TAccordionView)
+ parent::insertAt($index,$item);
+ else
+ throw new TInvalidDataTypeException('tabviewcollection_tabview_required');
+ }
+
+ /**
+ * Finds the index of the accordion view whose ID is the same as the one being looked for.
+ * @param string the explicit ID of the accordion view to be looked for
+ * @return integer the index of the accordion view found, -1 if not found.
+ */
+ public function findIndexByID($id)
+ {
+ foreach($this as $index=>$view)
+ {
+ if($view->getID(false)===$id)
+ return $index;
+ }
+ return -1;
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TBaseDataList.php b/lib/prado/framework/Web/UI/WebControls/TBaseDataList.php
new file mode 100644
index 0000000..4501073
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TBaseDataList.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * TBaseDataList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TDataBoundControl and TDataFieldAccessor classes
+ */
+Prado::using('System.Web.UI.WebControls.TDataBoundControl');
+Prado::using('System.Util.TDataFieldAccessor');
+
+/**
+ * TBaseDataList class
+ *
+ * TBaseDataList is the base class for data listing controls, including
+ * {@link TDataList} and {@link TDataGrid}.
+ *
+ * The key field in the data source is specified by {@link setKeyField KeyField},
+ * while {@link getKeyValues KeyValues} stores the key values of each record in
+ * a data listing control. You may use the list item index to obtain the corresponding
+ * database key value.
+ *
+ * TBaseDataList also implements a few properties used for presentation based
+ * on tabular layout. The {@link setCaption Caption}, whose alignment is
+ * specified via {@link setCaptionAlign CaptionAlign}, is rendered as the table caption.
+ * The table cellpadding and cellspacing are specified by
+ * {@link setCellPadding CellPadding} and {@link setCellSpacing CellSpacing}
+ * properties, respectively. The {@link setGridLines GridLines} specifies how
+ * the table should display its borders, and the horizontal alignment of the table
+ * content can be specified via {@link setHorizontalAlign HorizontalAlign}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+abstract class TBaseDataList extends TDataBoundControl
+{
+ /**
+ * Creates a style object for the control.
+ * This method creates a {@link TTableStyle} to be used by the data list control.
+ * @return TTableStyle control style to be used
+ */
+ protected function createStyle()
+ {
+ return new TTableStyle;
+ }
+
+ /**
+ * @return integer the cellspacing for the table layout. 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 layout.
+ */
+ public function setCellSpacing($value)
+ {
+ $this->getStyle()->setCellSpacing($value);
+ }
+
+ /**
+ * @return integer the cellpadding for the table layout. 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 layout
+ */
+ 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 layout. Defaults to TTableGridLines::None.
+ */
+ public function getGridLines()
+ {
+ if($this->getHasStyle())
+ return $this->getStyle()->getGridLines();
+ else
+ return TTableGridLines::None;
+ }
+
+ /**
+ * Sets the grid line style of the table layout.
+ * @param TTableGridLines the grid line setting of the table
+ */
+ public function setGridLines($value)
+ {
+ $this->getStyle()->setGridLines($value);
+ }
+
+
+ /**
+ * @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;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Raises OnSelectedIndexChanged event.
+ * This method is invoked when a different item is selected
+ * in a data listing control between posts to the server.
+ * @param mixed event parameter
+ */
+ public function onSelectedIndexChanged($param)
+ {
+ $this->raiseEvent('OnSelectedIndexChanged',$this,$param);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TBaseValidator.php b/lib/prado/framework/Web/UI/WebControls/TBaseValidator.php
new file mode 100644
index 0000000..a74d4bc
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TBaseValidator.php
@@ -0,0 +1,747 @@
+<?php
+/**
+ * TBaseValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TLabel class
+ */
+Prado::using('System.Web.UI.WebControls.TLabel');
+
+/**
+ * TBaseValidator class
+ *
+ * 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 <b>CausesValidation</b> property is true.
+ * You can also manually perform validation by calling {@link TPage::validate()}.
+ * The input control to be validated is specified by {@link setControlToValidate ControlToValidate}.
+ *
+ * Validator controls always validate the associated input control on the serve side.
+ * In addition, if {@link getEnableClientScript EnableClientScript} is true,
+ * validation will also be performed on the client-side using javascript.
+ * Client-side validation will validate user input before it is sent to the server.
+ * The form data will not be submitted if any error is detected. This avoids
+ * the round-trip of information necessary for server-side validation.
+ *
+ * You can use multiple validator controls to validate a single input control,
+ * each responsible for validating against a different criteria.
+ * For example, on a user registration form, you may want to make sure the user
+ * enters a value in the username text box, and the input must consist of only word
+ * characters. You can use a {@link TRequiredFieldValidator} to ensure the input
+ * of username and a {@link TRegularExpressionValidator} to ensure the proper input.
+ *
+ * If an input control fails validation, the text specified by the {@link setErrorMessage ErrorMessage}
+ * property is displayed in the validation control. However, if the {@link setText Text}
+ * property is set, it will be displayed instead. If both {@link setErrorMessage ErrorMessage}
+ * and {@link setText Text} are empty, the body content of the validator will
+ * be displayed. Error display is controlled by {@link setDisplay Display} property.
+ *
+ * You can also customized the client-side behaviour by adding javascript
+ * code to the subproperties of the {@link getClientSide ClientSide}
+ * property. See quickstart documentation for further details.
+ *
+ * You can also place a {@link TValidationSummary} control on a page to display error messages
+ * from the validators together. In this case, only the {@link setErrorMessage ErrorMessage}
+ * property of the validators will be displayed in the {@link TValidationSummary} control.
+ *
+ * Validators can be partitioned into validation groups by setting their
+ * {@link setValidationGroup ValidationGroup} property. If the control causing the
+ * validation also sets its ValidationGroup property, only those validators having
+ * the same ValidationGroup value will do input validation.
+ *
+ * Note, the {@link TPage::getIsValid IsValid} property of the current {@link TPage}
+ * instance will be automatically updated by the validation process which occurs
+ * after {@link TPage::onLoad onLoad} of {@link TPage} and before the postback events.
+ * Therefore, if you use the {@link TPage::getIsValid()} property in
+ * the {@link TPage::onLoad()} method, you must first explicitly call
+ * the {@link TPage::validate()} method.
+ *
+ * <b>Notes to Inheritors</b> When you inherit from TBaseValidator, you must
+ * override the method {@link evaluateIsValid}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+abstract class TBaseValidator extends TLabel implements IValidator
+{
+ /**
+ * @var boolean whether the validation succeeds
+ */
+ private $_isValid=true;
+ /**
+ * @var boolean whether the validator has been registered with the page
+ */
+ private $_registered=false;
+ /**
+ * @var TValidatorClientSide validator client-script options.
+ */
+ private $_clientSide;
+ /**
+ * Controls for which the client-side validation3.js file needs to handle
+ * them specially.
+ * @var array list of control class names
+ */
+ private static $_clientClass = array('THtmlArea', 'THtmlArea4', 'TDatePicker', 'TListBox', 'TCheckBoxList');
+
+ /**
+ * Constructor.
+ * This method sets the foreground color to red.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->setForeColor('red');
+ }
+
+ /**
+ * Registers the validator with page.
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ $this->getPage()->getValidators()->add($this);
+ $this->_registered=true;
+ }
+
+ /**
+ * Unregisters the validator from page.
+ * @param mixed event parameter
+ */
+ public function onUnload($param)
+ {
+ if($this->_registered && ($page=$this->getPage())!==null)
+ $page->getValidators()->remove($this);
+ $this->_registered=false;
+ parent::onUnload($param);
+ }
+
+ /**
+ * Adds attributes to renderer. Calls parent implementation and renders the
+ * client control scripts.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ $display=$this->getDisplay();
+ $visible=$this->getEnabled(true) && !$this->getIsValid();
+ if($display===TValidatorDisplayStyle::None || (!$visible && $display===TValidatorDisplayStyle::Dynamic))
+ $writer->addStyleAttribute('display','none');
+ else if(!$visible)
+ $writer->addStyleAttribute('visibility','hidden');
+ $writer->addAttribute('id',$this->getClientID());
+ parent::addAttributesToRender($writer);
+ $this->renderClientControlScript($writer);
+ }
+
+ /**
+ * Returns an array of javascript validator options.
+ * @return array javascript validator options.
+ */
+ protected function getClientScriptOptions()
+ {
+ $control = $this->getValidationTarget();
+ $options['ID'] = $this->getClientID();
+ $options['FormID'] = $this->getPage()->getForm()->getClientID();
+ $options['Display'] = $this->getDisplay();
+ $options['ErrorMessage'] = $this->getErrorMessage();
+ if($this->getFocusOnError())
+ {
+ $options['FocusOnError'] = $this->getFocusOnError();
+ $options['FocusElementID'] = $this->getFocusElementID();
+ }
+ $options['ValidationGroup'] = $this->getValidationGroup();
+ if($control)
+ $options['ControlToValidate'] = $control->getClientID();
+ $options['ControlCssClass'] = $this->getControlCssClass();
+
+ $options['ControlType'] = $this->getClientControlClass($control);
+ $options['Enabled'] = $this->getEnabled(true);
+
+ //get date format from date picker target control
+ if($control instanceof TDatePicker)
+ $options['DateFormat'] = $control->getDateFormat();
+
+ $options = array_merge($options,$this->getClientSide()->getOptions()->toArray());
+
+ return $options;
+ }
+
+ /**
+ * Gets the Control type for client-side validation. If new cases exists in
+ * TBaseValidator::$_clientClass, be sure to update the corresponding
+ * "Javascript/validation3.js" file as well.
+ * @param TControl control to validate.
+ * @return string control type for client-side validation.
+ */
+ private function getClientControlClass($control)
+ {
+ foreach(self::$_clientClass as $type)
+ if($control instanceof $type)
+ return $type;
+ return get_class($control);
+ }
+
+ /**
+ * Gets the TValidatorClientSide that allows modification of the client-
+ * side validator events.
+ *
+ * The client-side validator supports the following events.
+ * # <tt>OnValidate</tt> -- raised before client-side validation is
+ * executed.
+ * # <tt>OnValidationSuccess</tt> -- raised after client-side validation is completed
+ * and is successfull, overrides default validator error messages updates.
+ * # <tt>OnValidationError</tt> -- raised after client-side validation is completed
+ * and failed, overrides default validator error message updates.
+ *
+ * You can attach custom javascript code to each of these events
+ *
+ * @return TValidatorClientSide javascript validator event options.
+ */
+ public function getClientSide()
+ {
+ if($this->_clientSide===null)
+ $this->_clientSide = $this->createClientSide();
+ return $this->_clientSide;
+ }
+
+ /**
+ * @return TValidatorClientSide javascript validator event options.
+ */
+ protected function createClientSide()
+ {
+ return new TValidatorClientSide;
+ }
+
+ /**
+ * Renders the javascript code to the end script.
+ * If you override this method, be sure to call the parent implementation
+ * so that the event handlers can be invoked.
+ * @param THtmlWriter the renderer
+ */
+ public function renderClientControlScript($writer)
+ {
+ $scripts = $this->getPage()->getClientScript();
+ if ($this->getEnableClientScript())
+ $scripts->registerPradoScript('validator');
+ $formID=$this->getPage()->getForm()->getClientID();
+ $scriptKey = "TBaseValidator:$formID";
+ if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey))
+ {
+ $manager['FormID'] = $formID;
+ $options = TJavaScript::encode($manager);
+ $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
+ }
+ if($this->getEnableClientScript())
+ $this->registerClientScriptValidator();
+ }
+
+ /**
+ * Override parent implementation to update the control CSS Class before
+ * the validated control is rendered
+ */
+ public function onPreRender ($param)
+ {
+ parent::onPreRender($param);
+ $this->updateControlCssClass();
+ }
+
+ /**
+ * Update the ControlToValidate component's css class depending
+ * if the ControlCssClass property is set, and whether this is valid.
+ * @return boolean true if change, false otherwise.
+ */
+ protected function updateControlCssClass()
+ {
+ if(($cssClass=$this->getControlCssClass())!=='')
+ {
+ $control=$this->getValidationTarget();
+ if($control instanceof TWebControl)
+ {
+ $class = preg_replace ('/ '.preg_quote($cssClass).'/', '',$control->getCssClass());
+ if(!$this->getIsValid())
+ {
+ $class .= ' '.$cssClass;
+ $control->setCssClass($class);
+ } elseif ($control->getIsValid())
+ $control->setCssClass($class);
+ }
+ }
+ }
+
+ /**
+ * Registers the individual validator client-side javascript code.
+ */
+ protected function registerClientScriptValidator()
+ {
+ $key = 'prado:'.$this->getClientID();
+ if(!$this->getPage()->getClientScript()->isEndScriptRegistered($key))
+ {
+ $options = TJavaScript::encode($this->getClientScriptOptions());
+ $script = 'new '.$this->getClientClassName().'('.$options.');';
+ $this->getPage()->getClientScript()->registerEndScript($key, $script);
+ }
+ }
+
+ /**
+ * 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
+ */
+ abstract protected function getClientClassName();
+
+ /**
+ * This method overrides the parent implementation to forbid setting ForControl.
+ * @param string the associated control ID
+ * @throws TNotSupportedException whenever this method is called
+ */
+ public function setForControl($value)
+ {
+ throw new TNotSupportedException('basevalidator_forcontrol_unsupported',get_class($this));
+ }
+
+ /**
+ * This method overrides parent's implementation by setting {@link setIsValid IsValid} to true if disabled.
+ * @param boolean whether the validator is enabled.
+ */
+ public function setEnabled($value)
+ {
+ $value=TPropertyValue::ensureBoolean($value);
+ parent::setEnabled($value);
+ if(!$value)
+ $this->_isValid=true;
+ }
+
+ /**
+ * @return TValidatorDisplayStyle the style of displaying the error message. Defaults to TValidatorDisplayStyle::Fixed.
+ */
+ public function getDisplay()
+ {
+ return $this->getViewState('Display',TValidatorDisplayStyle::Fixed);
+ }
+
+ /**
+ * @param TValidatorDisplayStyle the style of displaying the error message
+ */
+ public function setDisplay($value)
+ {
+ $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidatorDisplayStyle'),TValidatorDisplayStyle::Fixed);
+ }
+
+ /**
+ * @return boolean whether client-side validation is enabled.
+ */
+ public function getEnableClientScript()
+ {
+ return $this->getViewState('EnableClientScript',true);
+ }
+
+ /**
+ * @param boolean whether client-side validation is enabled.
+ */
+ public function setEnableClientScript($value)
+ {
+ $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @return string the text for the error message.
+ */
+ public function getErrorMessage()
+ {
+ return $this->getViewState('ErrorMessage','');
+ }
+
+ /**
+ * Sets the text for the error message.
+ * @param string the error message
+ */
+ public function setErrorMessage($value)
+ {
+ $this->setViewState('ErrorMessage',$value,'');
+ }
+
+ /**
+ * @return string the ID path of the input control to validate
+ */
+ public function getControlToValidate()
+ {
+ return $this->getViewState('ControlToValidate','');
+ }
+
+ /**
+ * Sets the ID path of the input control to validate.
+ * The ID path is the dot-connected IDs of the controls reaching from
+ * the validator's naming container to the target control.
+ * @param string the ID path
+ */
+ public function setControlToValidate($value)
+ {
+ $this->setViewState('ControlToValidate',$value,'');
+ }
+
+ /**
+ * @return boolean whether to set focus at the validating place if the validation fails. Defaults to false.
+ */
+ public function getFocusOnError()
+ {
+ return $this->getViewState('FocusOnError',false);
+ }
+
+ /**
+ * @param boolean whether to set focus at the validating place if the validation fails
+ */
+ public function setFocusOnError($value)
+ {
+ $this->setViewState('FocusOnError',TPropertyValue::ensureBoolean($value),false);
+ }
+
+ /**
+ * Gets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
+ * Defaults to the client ID of the {@link getControlToValidate ControlToValidate}.
+ * @return string the ID of the HTML element to receive focus
+ */
+ public function getFocusElementID()
+ {
+ if(($id=$this->getViewState('FocusElementID',''))==='')
+ {
+ $target=$this->getValidationTarget();
+ /* Workaround: TCheckBoxList and TRadioButtonList nests the actual
+ * inputs inside a table; we ensure the first input gets focused
+ */
+ if($target instanceof TCheckBoxList && $target->getItemCount()>0)
+ {
+ $id=$target->getClientID().'_c0';
+ } else {
+ $id=$target->getClientID();
+ }
+ }
+ return $id;
+ }
+
+ /**
+ * Sets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
+ * @param string the ID of the HTML element to receive focus
+ */
+ public function setFocusElementID($value)
+ {
+ $this->setViewState('FocusElementID', $value, '');
+ }
+
+ /**
+ * @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,'');
+ }
+
+ /**
+ * @return boolean whether the validation succeeds
+ */
+ public function getIsValid()
+ {
+ return $this->_isValid;
+ }
+
+ /**
+ * Sets the value indicating whether the validation succeeds
+ * @param boolean whether the validation succeeds
+ */
+ public function setIsValid($value)
+ {
+ $this->_isValid=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return TControl control to be validated. Null if no control is found.
+ * @throws TConfigurationException if {@link getControlToValidate
+ * ControlToValidate} is empty or does not point to a valid control
+ */
+ public function getValidationTarget()
+ {
+ if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null)
+ return $control;
+ else
+ throw new TConfigurationException('basevalidator_controltovalidate_invalid',get_class($this));
+ }
+
+ /**
+ * Retrieves the property value of the control being validated.
+ * @param TControl control being validated
+ * @return string property value to be validated
+ * @throws TInvalidDataTypeException if the control to be validated does not implement {@link IValidatable}.
+ */
+ protected function getValidationValue($control)
+ {
+ if($control instanceof IValidatable)
+ return $control->getValidationPropertyValue();
+ else
+ throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this));
+ }
+
+ /**
+ * Validates the specified control.
+ * Do not override this method. Override {@link evaluateIsValid} instead.
+ * @return boolean whether the validation succeeds
+ */
+ public function validate()
+ {
+ $this->onValidate();
+ if($this->getVisible(true) && $this->getEnabled(true))
+ {
+ $target=$this->getValidationTarget();
+ // if the target is not a disabled web control
+ if($target===null ||
+ ($target!==null &&
+ !($target instanceof TWebControl && !$target->getEnabled(true))))
+ {
+ if($this->evaluateIsValid())
+ {
+ $this->setIsValid(true);
+ $this->onValidationSuccess();
+ }
+ else
+ {
+ if($target)
+ $target->setIsValid(false);
+ $this->setIsValid(false);
+ $this->onValidationError();
+ }
+ }
+ else
+ {
+ $this->evaluateIsValid();
+ $this->setIsValid(true);
+ $this->onValidationSuccess();
+ }
+ } else {
+ $this->setIsValid(true);
+ }
+ return $this->getIsValid();
+ }
+
+ /**
+ * @return string the css class that is applied to the control being validated in case the validation fails
+ */
+ public function getControlCssClass()
+ {
+ return $this->getViewState('ControlCssClass','');
+ }
+
+ /**
+ * @param string the css class that is applied to the control being validated in case the validation fails
+ */
+ public function setControlCssClass($value)
+ {
+ $this->setViewState('ControlCssClass',$value,'');
+ }
+
+ /**
+ * This is the major method for validation.
+ * Derived classes should implement this method to provide customized validation.
+ * @return boolean whether the validation succeeds
+ */
+ abstract protected function evaluateIsValid();
+
+ /**
+ * This event is raised when the validator succeeds in validation.
+ */
+ public function onValidationSuccess()
+ {
+ $this->raiseEvent('OnValidationSuccess',$this,null);
+ }
+
+ /**
+ * This event is raised when the validator fails in validation.
+ */
+ public function onValidationError()
+ {
+ $this->raiseEvent('OnValidationError',$this,null);
+ }
+
+ /**
+ * This event is raised right before the validator starts to perform validation.
+ * You may use this event to change the behavior of validation.
+ * For example, you may disable the validator if certain condition is satisfied.
+ * Note, the event will NOT be raised if the validator is invisible.
+ */
+ public function onValidate()
+ {
+ $this->raiseEvent('OnValidate',$this,null);
+ }
+
+ /**
+ * Renders the validator control.
+ * @param THtmlWriter writer for the rendering purpose
+ */
+ public function renderContents($writer)
+ {
+ if(($text=$this->getText())!=='')
+ $writer->write($text);
+ else if(($text=$this->getErrorMessage())!=='')
+ $writer->write($text);
+ else
+ parent::renderContents($writer);
+ }
+}
+
+/**
+ * TValidatorClientSide class.
+ *
+ * Client-side validator events can be modified through the {@link
+ * TBaseValidator::getClientSide ClientSide} property of a validator. The
+ * subproperties of ClientSide are those of the TValidatorClientSide
+ * properties. The client-side validator supports the following events.
+ *
+ * The <tt>OnValidate</tt> event is raise before the validator validation
+ * functions are called.
+ *
+ * The <tt>OnValidationSuccess</tt> event is raised after the validator has successfully
+ * validate the control.
+ *
+ * The <tt>OnValidationError</tt> event is raised after the validator fails validation.
+ *
+ * See the quickstart documentation for further details.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TValidatorClientSide extends TClientSideOptions
+{
+ /**
+ * @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;
+ }
+}
+
+
+/**
+ * TValidatorDisplayStyle class.
+ * TValidatorDisplayStyle defines the enumerable type for the possible styles
+ * that a validator control can display the error message.
+ *
+ * The following enumerable values are defined:
+ * - None: the error message is not displayed
+ * - Dynamic: the error message dynamically appears when the validator fails validation
+ * - Fixed: Similar to Dynamic except that the error message physically occupies the page layout (even though it may not be visible)
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TValidatorDisplayStyle extends TEnumerable
+{
+ const None='None';
+ const Dynamic='Dynamic';
+ const Fixed='Fixed';
+}
+
+/**
+ * TValidationDataType class.
+ * TValidationDataType defines the enumerable type for the possible data types that
+ * a comparison validator can validate upon.
+ *
+ * The following enumerable values are defined:
+ * - Integer
+ * - Float
+ * - Date
+ * - String
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TValidationDataType extends TEnumerable
+{
+ const Integer='Integer';
+ const Float='Float';
+ const Date='Date';
+ const String='String';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TBoundColumn.php b/lib/prado/framework/Web/UI/WebControls/TBoundColumn.php
new file mode 100644
index 0000000..95697bd
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TBoundColumn.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * TBoundColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TDataGridColumn class file
+ */
+Prado::using('System.Web.UI.WebControls.TDataGridColumn');
+
+/**
+ * TBoundColumn class
+ *
+ * TBoundColumn 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 setDataField DataField}. You can customize the display by
+ * setting {@link setDataFormatString DataFormatString}.
+ *
+ * If {@link setReadOnly ReadOnly} is false, TBoundColumn will display cells in edit mode
+ * with textboxes. Otherwise, a static text is displayed.
+ *
+ * When a datagrid row is in edit mode, the textbox control in the TBoundColumn
+ * can be accessed by one of the following two methods:
+ * <code>
+ * $datagridItem->BoundColumnID->TextBox
+ * $datagridItem->BoundColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the textbox control created within the
+ * datagrid cell is the first child.
+ *
+ * Since v3.1.0, TBoundColumn 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}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TBoundColumn extends TDataGridColumn
+{
+ /**
+ * @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 <b>Data</b> property
+ * will be set as the data associated with the datagrid cell during databinding.
+ * The data can be either the whole data row or a field of the row if
+ * {@link getDataField DataField} is not empty. If {@link getDataFormatString DataFormatString}
+ * is not empty, the data will be formatted first before passing to the renderer.
+ *
+ * @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 <b>Data</b> property
+ * will be set as the data associated with the datagrid cell during databinding.
+ * The data can be either the whole data row or a field of the row if
+ * {@link getDataField DataField} is not empty. If {@link getDataFormatString DataFormatString}
+ * is not empty, the data will be formatted first before passing to the renderer.
+ *
+ * @param string the renderer class name in namespace format.
+ * @since 3.1.0
+ */
+ public function setEditItemRenderer($value)
+ {
+ $this->setViewState('EditItemRenderer',$value,'');
+ }
+
+ /**
+ * @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 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);
+ }
+
+ /**
+ * 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)
+ {
+ $item=$cell->getParent();
+ switch($itemType)
+ {
+ case TListItemType::Item:
+ case TListItemType::AlternatingItem:
+ case TListItemType::SelectedItem:
+ if(($classPath=$this->getItemRenderer())!=='')
+ {
+ $control=Prado::createComponent($classPath);
+ if($control instanceof IItemDataRenderer)
+ {
+ $control->setItemIndex($item->getItemIndex());
+ $control->setItemType($item->getItemType());
+ }
+ $cell->getControls()->add($control);
+ }
+ else
+ $control=$cell;
+ $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+ break;
+ case TListItemType::EditItem:
+ if(!$this->getReadOnly())
+ {
+ if(($classPath=$this->getEditItemRenderer())!=='')
+ {
+ $control=Prado::createComponent($classPath);
+ if($control instanceof IItemDataRenderer)
+ {
+ $control->setItemIndex($item->getItemIndex());
+ $control->setItemType($item->getItemType());
+ }
+ $cell->getControls()->add($control);
+ $cell->registerObject('EditControl',$control);
+ }
+ else
+ {
+ $control=Prado::createComponent('System.Web.UI.WebControls.TTextBox');
+ $cell->getControls()->add($control);
+ $cell->registerObject('TextBox',$control);
+ }
+ }
+ else
+ {
+ if(($classPath=$this->getItemRenderer())!=='')
+ {
+ $control=Prado::createComponent($classPath);
+ if($control instanceof IItemDataRenderer)
+ {
+ $control->setItemIndex($item->getItemIndex());
+ $control->setItemType($item->getItemType());
+ }
+ $cell->getControls()->add($control);
+ }
+ else
+ $control=$cell;
+ }
+ $control->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();
+ $formatString=$this->getDataFormatString();
+ if(($field=$this->getDataField())!=='')
+ $value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field));
+ else
+ $value=$this->formatDataValue($formatString,$data);
+ $sender->setData($value);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TBulletedList.php b/lib/prado/framework/Web/UI/WebControls/TBulletedList.php
new file mode 100644
index 0000000..5d4167e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TBulletedList.php
@@ -0,0 +1,487 @@
+<?php
+/**
+ * TBulletedList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TListControl class
+ */
+Prado::using('System.Web.UI.WebControls.TListControl');
+
+/**
+ * TBulletedList class
+ *
+ * TBulletedList displays items in a bullet format.
+ * The bullet style is specified by {@link setBulletStyle BulletStyle}. When
+ * the style is 'CustomImage', the {@link setBackImageUrl BulletImageUrl}
+ * specifies the image used as bullets.
+ *
+ * TBulletedList displays the item texts in three different modes, specified
+ * via {@link setDisplayMode DisplayMode}. When the mode is Text, the item texts
+ * are displayed as static texts; When the mode is 'HyperLink', each item
+ * is displayed as a hyperlink whose URL is given by the item value, and the
+ * {@link setTarget Target} property can be used to specify the target browser window;
+ * When the mode is 'LinkButton', each item is displayed as a link button which
+ * posts back to the page if a user clicks on that and the event {@link onClick OnClick}
+ * will be raised under such a circumstance.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TBulletedList extends TListControl implements IPostBackEventHandler
+{
+ /**
+ * @var boolean cached property value of Enabled
+ */
+ private $_isEnabled;
+ /**
+ * @var TPostBackOptions postback options
+ */
+ private $_postBackOptions;
+
+ private $_currentRenderItemIndex;
+
+ /**
+ * 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} 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 TBulletedListEventParameter((int)$param));
+ }
+
+ /**
+ * @return string tag name of the bulleted list
+ */
+ protected function getTagName()
+ {
+ switch($this->getBulletStyle())
+ {
+ case TBulletStyle::Numbered:
+ case TBulletStyle::LowerAlpha:
+ case TBulletStyle::UpperAlpha:
+ case TBulletStyle::LowerRoman:
+ case TBulletStyle::UpperRoman:
+ return 'ol';
+ }
+ return 'ul';
+ }
+
+ /**
+ * 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.TBulletedList';
+ }
+
+ /**
+ * Adds attribute name-value pairs to renderer.
+ * This overrides the parent implementation with additional bulleted list specific attributes.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function addAttributesToRender($writer)
+ {
+ $needStart=false;
+ switch($this->getBulletStyle())
+ {
+ case TBulletStyle::None:
+ $writer->addStyleAttribute('list-style-type','none');
+ $needStart=true;
+ break;
+ case TBulletStyle::Numbered:
+ $writer->addStyleAttribute('list-style-type','decimal');
+ $needStart=true;
+ break;
+ case TBulletStyle::LowerAlpha:
+ $writer->addStyleAttribute('list-style-type','lower-alpha');
+ $needStart=true;
+ break;
+ case TBulletStyle::UpperAlpha:
+ $writer->addStyleAttribute('list-style-type','upper-alpha');
+ $needStart=true;
+ break;
+ case TBulletStyle::LowerRoman:
+ $writer->addStyleAttribute('list-style-type','lower-roman');
+ $needStart=true;
+ break;
+ case TBulletStyle::UpperRoman:
+ $writer->addStyleAttribute('list-style-type','upper-roman');
+ $needStart=true;
+ break;
+ case TBulletStyle::Disc:
+ $writer->addStyleAttribute('list-style-type','disc');
+ break;
+ case TBulletStyle::Circle:
+ $writer->addStyleAttribute('list-style-type','circle');
+ break;
+ case TBulletStyle::Square:
+ $writer->addStyleAttribute('list-style-type','square');
+ break;
+ case TBulletStyle::CustomImage:
+ $url=$this->getBulletImageUrl();
+ $writer->addStyleAttribute('list-style-image',"url($url)");
+ break;
+ }
+ if($needStart && ($start=$this->getFirstBulletNumber())!=1)
+ $writer->addAttribute('start',"$start");
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
+ * @return string image URL used for bullets when {@link getBulletStyle BulletStyle} is 'CustomImage'.
+ */
+ public function getBulletImageUrl()
+ {
+ return $this->getViewState('BulletImageUrl','');
+ }
+
+ /**
+ * @param string image URL used for bullets when {@link getBulletStyle BulletStyle} is 'CustomImage'.
+ */
+ public function setBulletImageUrl($value)
+ {
+ $this->setViewState('BulletImageUrl',$value,'');
+ }
+
+ /**
+ * @return TBulletStyle style of bullets. Defaults to TBulletStyle::NotSet.
+ */
+ public function getBulletStyle()
+ {
+ return $this->getViewState('BulletStyle',TBulletStyle::NotSet);
+ }
+
+ /**
+ * @param TBulletStyle style of bullets.
+ */
+ public function setBulletStyle($value)
+ {
+ $this->setViewState('BulletStyle',TPropertyValue::ensureEnum($value,'TBulletStyle'),TBulletStyle::NotSet);
+ }
+
+ /**
+ * @return TBulletedListDisplayMode display mode of the list. Defaults to TBulletedListDisplayMode::Text.
+ */
+ public function getDisplayMode()
+ {
+ return $this->getViewState('DisplayMode',TBulletedListDisplayMode::Text);
+ }
+
+ /**
+ * @return TBulletedListDisplayMode display mode of the list.
+ */
+ public function setDisplayMode($value)
+ {
+ $this->setViewState('DisplayMode',TPropertyValue::ensureEnum($value,'TBulletedListDisplayMode'),TBulletedListDisplayMode::Text);
+ }
+
+ /**
+ * @return integer starting index when {@link getBulletStyle BulletStyle} is one of
+ * the following: 'Numbered', 'LowerAlpha', 'UpperAlpha', 'LowerRoman', 'UpperRoman'.
+ * Defaults to 1.
+ */
+ public function getFirstBulletNumber()
+ {
+ return $this->getViewState('FirstBulletNumber',1);
+ }
+
+ /**
+ * @param integer starting index when {@link getBulletStyle BulletStyle} is one of
+ * the following: 'Numbered', 'LowerAlpha', 'UpperAlpha', 'LowerRoman', 'UpperRoman'.
+ */
+ public function setFirstBulletNumber($value)
+ {
+ $this->setViewState('FirstBulletNumber',TPropertyValue::ensureInteger($value),1);
+ }
+
+ /**
+ * Raises 'OnClick' event.
+ * This method is invoked when the {@link getDisplayMode DisplayMode} is 'LinkButton'
+ * and end-users click on one of the buttons.
+ * @param TBulletedListEventParameter event parameter.
+ */
+ public function onClick($param)
+ {
+ $this->raiseEvent('OnClick',$this,$param);
+ }
+
+ /**
+ * @return string the target window or frame to display the Web page content
+ * linked to when {@link getDisplayMode DisplayMode} is 'HyperLink' and one of
+ * the hyperlinks is clicked.
+ */
+ public function getTarget()
+ {
+ return $this->getViewState('Target','');
+ }
+
+ /**
+ * @param string the target window or frame to display the Web page content
+ * linked to when {@link getDisplayMode DisplayMode} is 'HyperLink' and one of
+ * the hyperlinks is clicked.
+ */
+ public function setTarget($value)
+ {
+ $this->setViewState('Target',$value,'');
+ }
+
+ /**
+ * Renders the control.
+ * @param THtmlWriter the writer for the rendering purpose.
+ */
+ public function render($writer)
+ {
+ if($this->getHasItems())
+ parent::render($writer);
+ }
+
+ /**
+ * Renders the body contents.
+ * @param THtmlWriter the writer for the rendering purpose.
+ */
+ public function renderContents($writer)
+ {
+ $this->_isEnabled=$this->getEnabled(true);
+ $this->_postBackOptions=$this->getPostBackOptions();
+ $writer->writeLine();
+ foreach($this->getItems() as $index=>$item)
+ {
+ if($item->getHasAttributes())
+ $writer->addAttributes($item->getAttributes());
+ $writer->renderBeginTag('li');
+ $this->renderBulletText($writer,$item,$index);
+ $writer->renderEndTag();
+ $writer->writeLine();
+ }
+ }
+
+ /**
+ * Renders each item
+ * @param THtmlWriter writer for the rendering purpose
+ * @param TListItem item to be rendered
+ * @param integer index of the item being rendered
+ */
+ protected function renderBulletText($writer,$item,$index)
+ {
+ switch($this->getDisplayMode())
+ {
+ case TBulletedListDisplayMode::Text:
+ $this->renderTextItem($writer, $item, $index);
+ break;
+ case TBulletedListDisplayMode::HyperLink:
+ $this->renderHyperLinkItem($writer, $item, $index);
+ break;
+ case TBulletedListDisplayMode::LinkButton:
+ $this->renderLinkButtonItem($writer, $item, $index);
+ break;
+ }
+ }
+
+ protected function renderTextItem($writer, $item, $index)
+ {
+ if($item->getEnabled())
+ $writer->write(THttpUtility::htmlEncode($item->getText()));
+ else
+ {
+ $writer->addAttribute('disabled','disabled');
+ $writer->renderBeginTag('span');
+ $writer->write(THttpUtility::htmlEncode($item->getText()));
+ $writer->renderEndTag();
+ }
+ }
+
+ protected function renderHyperLinkItem($writer, $item, $index)
+ {
+ if(!$this->_isEnabled || !$item->getEnabled())
+ $writer->addAttribute('disabled','disabled');
+ else
+ {
+ $writer->addAttribute('href',$item->getValue());
+ if(($target=$this->getTarget())!=='')
+ $writer->addAttribute('target',$target);
+ }
+ if(($accesskey=$this->getAccessKey())!=='')
+ $writer->addAttribute('accesskey',$accesskey);
+ $writer->renderBeginTag('a');
+ $writer->write(THttpUtility::htmlEncode($item->getText()));
+ $writer->renderEndTag();
+ }
+
+ protected function renderLinkButtonItem($writer, $item, $index)
+ {
+ if(!$this->_isEnabled || !$item->getEnabled())
+ $writer->addAttribute('disabled','disabled');
+ else
+ {
+ $this->_currentRenderItemIndex = $index;
+ $writer->addAttribute('id', $this->getClientID().$index);
+ $writer->addAttribute('href', "javascript:;//".$this->getClientID().$index);
+ $cs = $this->getPage()->getClientScript();
+ $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions());
+ }
+ if(($accesskey=$this->getAccessKey())!=='')
+ $writer->addAttribute('accesskey',$accesskey);
+ $writer->renderBeginTag('a');
+ $writer->write(THttpUtility::htmlEncode($item->getText()));
+ $writer->renderEndTag();
+ }
+
+ /**
+ * @return array postback options used for linkbuttons.
+ */
+ protected function getPostBackOptions()
+ {
+ $options['ValidationGroup'] = $this->getValidationGroup();
+ $options['CausesValidation'] = $this->getCausesValidation();
+ $options['EventTarget'] = $this->getUniqueID();
+ $options['EventParameter'] = $this->_currentRenderItemIndex;
+ $options['ID'] = $this->getClientID().$this->_currentRenderItemIndex;
+ $options['StopEvent'] = true;
+ return $options;
+ }
+
+ protected function canCauseValidation()
+ {
+ $group = $this->getValidationGroup();
+ $hasValidators = $this->getPage()->getValidators($group)->getCount()>0;
+ return $this->getCausesValidation() && $hasValidators;
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setAutoPostBack($value)
+ {
+ throw new TNotSupportedException('bulletedlist_autopostback_unsupported');
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setSelectedIndex($index)
+ {
+ throw new TNotSupportedException('bulletedlist_selectedindex_unsupported');
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setSelectedIndices($indices)
+ {
+ throw new TNotSupportedException('bulletedlist_selectedindices_unsupported');
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setSelectedValue($value)
+ {
+ throw new TNotSupportedException('bulletedlist_selectedvalue_unsupported');
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setSelectedValues($values)
+ {
+ throw new TNotSupportedException('bulletedlist_selectedvalue_unsupported');
+ }
+}
+
+/**
+ * TBulletedListEventParameter
+ * Event parameter for {@link TBulletedList::onClick Click} event of the
+ * bulleted list. The {@link getIndex Index} gives the zero-based index
+ * of the item that is currently being clicked.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TBulletedListEventParameter extends TEventParameter
+{
+ /**
+ * @var integer index of the item clicked
+ */
+ private $_index;
+
+ /**
+ * Constructor.
+ * @param integer index of the item clicked
+ */
+ public function __construct($index)
+ {
+ $this->_index=$index;
+ }
+
+ /**
+ * @return integer zero-based index of the item (rendered as a link button) that is clicked
+ */
+ public function getIndex()
+ {
+ return $this->_index;
+ }
+}
+
+/**
+ * TBulletStyle class.
+ * TBulletStyle defines the enumerable type for the possible bullet styles that may be used
+ * for a {@link TBulletedList} control.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TBulletStyle extends TEnumerable
+{
+ const NotSet='NotSet';
+ const None='None';
+ const Numbered='Numbered';
+ const LowerAlpha='LowerAlpha';
+ const UpperAlpha='UpperAlpha';
+ const LowerRoman='LowerRoman';
+ const UpperRoman='UpperRoman';
+ const Disc='Disc';
+ const Circle='Circle';
+ const Square='Square';
+ const CustomImage='CustomImage';
+}
+
+/**
+ * TBulletedListDisplayMode class.
+ * TBulletedListDisplayMode defines the enumerable type for the possible display mode
+ * of a {@link TBulletedList} control.
+ *
+ * The following enumerable values are defined:
+ * - Text: the bulleted list items are displayed as plain texts
+ * - HyperLink: the bulleted list items are displayed as hyperlinks
+ * - LinkButton: the bulleted list items are displayed as link buttons that can cause postbacks
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TBulletedListDisplayMode extends TEnumerable
+{
+ const Text='Text';
+ const HyperLink='HyperLink';
+ const LinkButton='LinkButton';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TButton.php b/lib/prado/framework/Web/UI/WebControls/TButton.php
new file mode 100644
index 0000000..aed518a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TButton.php
@@ -0,0 +1,404 @@
+<?php
+/**
+ * TButton class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TButton class
+ *
+ * TButton creates a click button on the page. It is mainly used to submit data to a page.
+ *
+ * TButton raises two server-side events, {@link onClick OnClick} and {@link onCommand OnCommand},
+ * when it is clicked on the client-side. The difference between these two events
+ * is that the event {@link onCommand OnCommand} is bubbled up to the button's ancestor controls.
+ * And within the event parameter for {@link onCommand OnCommand} contains the reference
+ * to the {@link setCommandName CommandName} and {@link setCommandParameter CommandParameter}
+ * property values that are set for the button object. This allows you to create multiple TButton
+ * components on a Web page and programmatically determine which one is clicked
+ * with what parameter.
+ *
+ * Clicking on button can also trigger form validation, if
+ * {@link setCausesValidation CausesValidation} is true.
+ * 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.
+ *
+ * TButton displays the {@link setText Text} property as the button caption.
+ *
+ * TButton can be one of three {@link setButtonType ButtonType}: Submit, Button and Reset.
+ * By default, it is a Submit button and the form submission uses the browser's
+ * default submission capability. If it is Button or Reset, postback may occur
+ * if one of the following conditions is met:
+ * - an event handler is attached to {@link onClick OnClick} event;
+ * - an event handler is attached to {@link onCommand OnCommand} event;
+ * - the button is in a non-empty validation group.
+ * In addition, clicking on a Reset button will clear up all input fields
+ * if the button does not cause a postback.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TButton extends TWebControl implements IPostBackEventHandler, IButtonControl, IDataRenderer
+{
+ /**
+ * @return string tag name of the button
+ */
+ protected function getTagName()
+ {
+ return strtolower($this->getButtonTag());
+ }
+
+ /**
+ * @return TButtonTag the tag name of the button. Defaults to TButtonType::Input.
+ */
+ public function getButtonTag()
+ {
+ return $this->getViewState('ButtonTag',TButtonTag::Input);
+ }
+
+ /**
+ * @param TButtonTag the tag name of the button.
+ */
+ public function setButtonTag($value)
+ {
+ $this->setViewState('ButtonTag',TPropertyValue::ensureEnum($value,'TButtonTag'),TButtonTag::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',strtolower($this->getButtonType()));
+ if(($uniqueID=$this->getUniqueID())!=='')
+ $writer->addAttribute('name',$uniqueID);
+ if($this->getButtonTag()===TButtonTag::Button)
+ $this->addParsedObject($this->getText());
+ else
+ $writer->addAttribute('value',$this->getText());
+ 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());
+ $this->getPage()->getClientScript()->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.TButton';
+ }
+
+ /**
+ * @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->getButtonType()!==TButtonType::Submit &&
+ ($this->hasEventHandler('OnClick') || $this->hasEventHandler('OnCommand')))
+ || $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['EventTarget'] = $this->getUniqueID();
+ $options['ValidationGroup']=$this->getValidationGroup();
+
+ return $options;
+ }
+
+ /**
+ * Renders the body content enclosed between the control tag.
+ * This overrides the parent implementation with nothing to be rendered for input tags,
+ * button tags are rendered normally.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ public function renderContents($writer)
+ {
+ if($this->getButtonTag()===TButtonTag::Button)
+ parent::renderContents($writer);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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()));
+ }
+
+ /**
+ * @return string caption of the button
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * @param string caption of the button
+ */
+ 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 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 TButtonType the type of the button. Defaults to TButtonType::Submit.
+ */
+ public function getButtonType()
+ {
+ return $this->getViewState('ButtonType',TButtonType::Submit);
+ }
+
+ /**
+ * @param TButtonType the type of the button.
+ */
+ public function setButtonType($value)
+ {
+ $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TButtonType'),TButtonType::Submit);
+ }
+}
+
+/**
+ * TButtonType class.
+ * TButtonType defines the enumerable type for the possible types that a {@link TButton} can take.
+ *
+ * The following enumerable values are defined:
+ * - Submit: a normal submit button
+ * - Reset: a reset button
+ * - Button: a client button (normally does not perform form submission)
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TButtonType extends TEnumerable
+{
+ const Submit='Submit';
+ const Reset='Reset';
+ const Button='Button';
+}
+
+/**
+ * TButtonTag class.
+ * TButtonTag defines the enumerable type for the possible tag names that a {@link TButton} can use for rendering.
+ *
+ * The following enumerable values are defined:
+ * - Input: an input tag is rendered
+ * - Button: a button tag is rendered
+ *
+ * @author LANDWEHR Computer und Software GmbH <programmierung@landwehr-software.de>
+ * @package System.Web.UI.WebControls
+ */
+class TButtonTag extends TEnumerable
+{
+ const Input='Input';
+ const Button='Button';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TButtonColumn.php b/lib/prado/framework/Web/UI/WebControls/TButtonColumn.php
new file mode 100644
index 0000000..d2d0ec5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TButtonColumn.php
@@ -0,0 +1,277 @@
+<?php
+/**
+ * TButtonColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TDataGridColumn class file
+ */
+Prado::using('System.Web.UI.WebControls.TDataGridColumn');
+Prado::using('System.Web.UI.WebControls.TButton');
+Prado::using('System.Web.UI.WebControls.TLinkButton');
+Prado::using('System.Web.UI.WebControls.TImageButton');
+
+/**
+ * TButtonColumn class
+ *
+ * TButtonColumn contains a user-defined command button, such as Add or Remove,
+ * that corresponds with each row in the column.
+ *
+ * The caption of the buttons in the column is determined by {@link setText Text}
+ * and {@link setDataTextField DataTextField} properties. If both are present,
+ * the latter takes precedence. The {@link setDataTextField DataTextField} property
+ * refers to the name of the field in datasource whose value will be used as the button caption.
+ * If {@link setDataTextFormatString DataTextFormatString} is not empty,
+ * the value will be formatted before rendering.
+ *
+ * The buttons in the column can be set to display as hyperlinks, push buttons or images
+ * by setting the {@link setButtonType ButtonType} property.
+ * The {@link setCommandName CommandName} will assign its value to
+ * all button's <b>CommandName</b> property. The datagrid will capture
+ * the command event where you can write event handlers based on different command names.
+ * The buttons' <b>CausesValidation</b> and <b>ValidationGroup</b> property values
+ * are determined by the column's corresponding properties.
+ *
+ * The buttons in the column can be accessed by one of the following two methods:
+ * <code>
+ * $datagridItem->ButtonColumnID->Button
+ * $datagridItem->ButtonColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the button control created within the
+ * datagrid cell is the first child.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TButtonColumn extends TDataGridColumn
+{
+ /**
+ * @return string the text caption of the button
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * Sets the text caption of the button.
+ * @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 button caption
+ */
+ public function getDataTextField()
+ {
+ return $this->getViewState('DataTextField','');
+ }
+
+ /**
+ * @param string the field name from the data source to bind to the button caption
+ */
+ public function setDataTextField($value)
+ {
+ $this->setViewState('DataTextField',$value,'');
+ }
+
+ /**
+ * @return string the formatting string used to control how the button caption will be displayed.
+ */
+ public function getDataTextFormatString()
+ {
+ return $this->getViewState('DataTextFormatString','');
+ }
+
+ /**
+ * @param string the formatting string used to control how the button caption will be displayed.
+ */
+ public function setDataTextFormatString($value)
+ {
+ $this->setViewState('DataTextFormatString',$value,'');
+ }
+
+ /**
+ * @return string the URL of the image file for image buttons
+ */
+ public function getImageUrl()
+ {
+ return $this->getViewState('ImageUrl','');
+ }
+
+ /**
+ * @param string the URL of the image file for image buttons
+ */
+ public function setImageUrl($value)
+ {
+ $this->setViewState('ImageUrl',$value,'');
+ }
+
+ /**
+ * @return string the field name from the data source to bind to the button image url
+ */
+ public function getDataImageUrlField()
+ {
+ return $this->getViewState('DataImageUrlField','');
+ }
+
+ /**
+ * @param string the field name from the data source to bind to the button image url
+ */
+ public function setDataImageUrlField($value)
+ {
+ $this->setViewState('DataImageUrlField',$value,'');
+ }
+
+ /**
+ * @return string the formatting string used to control how the button image url will be displayed.
+ */
+ public function getDataImageUrlFormatString()
+ {
+ return $this->getViewState('DataImageUrlFormatString','');
+ }
+
+ /**
+ * @param string the formatting string used to control how the button image url will be displayed.
+ */
+ public function setDataImageUrlFormatString($value)
+ {
+ $this->setViewState('DataImageUrlFormatString',$value,'');
+ }
+
+ /**
+ * @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 command name associated with the <b>OnCommand</b> event.
+ */
+ public function getCommandName()
+ {
+ return $this->getViewState('CommandName','');
+ }
+
+ /**
+ * Sets the command name associated with the <b>Command</b> event.
+ * @param string the text caption to be set
+ */
+ public function setCommandName($value)
+ {
+ $this->setViewState('CommandName',$value,'');
+ }
+
+ /**
+ * @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 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,'');
+ }
+
+ /**
+ * Initializes the specified cell to its initial values.
+ * This method overrides the parent implementation.
+ * It creates a command button 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)
+ {
+ $buttonType=$this->getButtonType();
+ if($buttonType===TButtonColumnType::LinkButton)
+ $button=new TLinkButton;
+ else if($buttonType===TButtonColumnType::PushButton)
+ $button=new TButton;
+ else // image button
+ {
+ $button=new TImageButton;
+ $button->setImageUrl($this->getImageUrl());
+ $button->setToolTip($this->getText());
+ }
+ $button->setText($this->getText());
+ $button->setCommandName($this->getCommandName());
+ $button->setCausesValidation($this->getCausesValidation());
+ $button->setValidationGroup($this->getValidationGroup());
+ if($this->getDataTextField()!=='' || ($buttonType===TButtonColumnType::ImageButton && $this->getDataImageUrlField()!==''))
+ $button->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+ $cell->getControls()->add($button);
+ $cell->registerObject('Button',$button);
+ }
+ 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)
+ {
+ if($sender instanceof IButtonControl)
+ {
+ if(($field=$this->getDataTextField())!=='')
+ {
+ $value=$this->getDataFieldValue($sender->getNamingContainer()->getData(),$field);
+ $text=$this->formatDataValue($this->getDataTextFormatString(),$value);
+ $sender->setText($text);
+ }
+ if(($sender instanceof TImageButton) && ($field=$this->getDataImageUrlField())!=='')
+ {
+ $value=$this->getDataFieldValue($sender->getNamingContainer()->getData(),$field);
+ $url=$this->formatDataValue($this->getDataImageUrlFormatString(),$value);
+ $sender->setImageUrl($url);
+ }
+ }
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCaptcha.php b/lib/prado/framework/Web/UI/WebControls/TCaptcha.php
new file mode 100644
index 0000000..cf9cc8e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCaptcha.php
@@ -0,0 +1,493 @@
+<?php
+/**
+ * TCaptcha class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+Prado::using('System.Web.UI.WebControls.TImage');
+
+/**
+ * TCaptcha class.
+ *
+ * Notice: while this class is easy to use and implement, it does not provide full security.
+ * In fact, it's easy to bypass the checks reusing old, already-validated tokens (reply attack).
+ * A better alternative is provided by {@link TReCaptcha}.
+ *
+ * TCaptcha displays a CAPTCHA (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.
+ *
+ * Unlike other CAPTCHA scripts, TCaptcha does not need session or cookie.
+ *
+ * The token (a string consisting of alphanumeric characters) displayed is automatically
+ * generated and can be configured in several ways. To specify the length of characters
+ * in the token, set {@link setMinTokenLength MinTokenLength} and {@link setMaxTokenLength MaxTokenLength}.
+ * To use case-insensitive comparison and generate upper-case-only token, set {@link setCaseSensitive CaseSensitive}
+ * to false. Advanced users can try to set {@link setTokenAlphabet TokenAlphabet}, which
+ * specifies what characters can appear in tokens.
+ *
+ * The validation of the token is related with two properties: {@link setTestLimit TestLimit}
+ * and {@link setTokenExpiry TokenExpiry}. The former specifies how many times a token can
+ * be tested with on the server side, and the latter says when a generated token will expire.
+ *
+ * To specify the appearance of the generated token image, set {@link setTokenImageTheme TokenImageTheme}
+ * to be an integer between 0 and 63. And to adjust the generated image size, set {@link setTokenFontSize TokenFontSize}
+ * (you may also set {@link TWebControl::setWidth Width}, but the scaled image may not look good.)
+ * By setting {@link setChangingTokenBackground ChangingTokenBackground} to true, the image background
+ * of the token will be variating even though the token is the same during postbacks.
+ *
+ * Upon postback, user input can be validated by calling {@link validate()}.
+ * The {@link TCaptchaValidator} control can also be used to do validation, which provides
+ * client-side validation besides the server-side validation. By default, the token will
+ * remain the same during multiple postbacks. A new one can be generated by calling
+ * {@link regenerateToken()} manually.
+ *
+ * The following template shows a typical use of TCaptcha control:
+ * <code>
+ * <com:TCaptcha ID="Captcha" />
+ * <com:TTextBox ID="Input" />
+ * <com:TCaptchaValidator CaptchaControl="Captcha"
+ * ControlToValidate="Input"
+ * ErrorMessage="You are challenged!" />
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TCaptcha extends TImage
+{
+ const MIN_TOKEN_LENGTH=2;
+ const MAX_TOKEN_LENGTH=40;
+ private $_privateKey;
+ private $_validated=false;
+
+ /**
+ * @return integer the theme of the token image. Defaults to 0.
+ */
+ public function getTokenImageTheme()
+ {
+ return $this->getViewState('TokenImageTheme',0);
+ }
+
+ /**
+ * Sets the theme of the token image.
+ * You may test each theme to find out the one you like the most.
+ * Below is the explanation of the theme value:
+ * It is treated as a 5-bit integer. Each bit toggles a specific feature of the image.
+ * Bit 0 (the least significant): whether the image is opaque (1) or transparent (0).
+ * Bit 1: whether we should add white noise to the image (1) or not (0).
+ * Bit 2: whether we should add a grid to the image (1) or not (0).
+ * Bit 3: whether we should add some scribbles to the image (1) or not (0).
+ * Bit 4: whether the image background should be morphed (1) or not (0).
+ * Bit 5: whether the token text should cast a shadow (1) or not (0).
+ * @param integer the theme of the token image. It must be an integer between 0 and 63.
+ */
+ public function setTokenImageTheme($value)
+ {
+ $value=TPropertyValue::ensureInteger($value);
+ if($value>=0 && $value<=63)
+ $this->setViewState('TokenImageTheme',$value,0);
+ else
+ throw new TConfigurationException('captcha_tokenimagetheme_invalid',0,63);
+ }
+
+ /**
+ * @return integer the font size used for displaying the token in an image. Defaults to 30.
+ */
+ public function getTokenFontSize()
+ {
+ return $this->getViewState('TokenFontSize',30);
+ }
+
+ /**
+ * Sets the font size used for displaying the token in an image.
+ * This property affects the generated token image size.
+ * The image width is proportional to this font size.
+ * @param integer the font size used for displaying the token in an image. It must be an integer between 20 and 100.
+ */
+ public function setTokenFontSize($value)
+ {
+ $value=TPropertyValue::ensureInteger($value);
+ if($value>=20 && $value<=100)
+ $this->setViewState('TokenFontSize',$value,30);
+ else
+ throw new TConfigurationException('captcha_tokenfontsize_invalid',20,100);
+ }
+
+ /**
+ * @return integer the minimum length of the token. Defaults to 4.
+ */
+ public function getMinTokenLength()
+ {
+ return $this->getViewState('MinTokenLength',4);
+ }
+
+ /**
+ * @param integer the minimum length of the token. It must be between 2 and 40.
+ */
+ public function setMinTokenLength($value)
+ {
+ $length=TPropertyValue::ensureInteger($value);
+ if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH)
+ $this->setViewState('MinTokenLength',$length,4);
+ else
+ throw new TConfigurationException('captcha_mintokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH);
+ }
+
+ /**
+ * @return integer the maximum length of the token. Defaults to 6.
+ */
+ public function getMaxTokenLength()
+ {
+ return $this->getViewState('MaxTokenLength',6);
+ }
+
+ /**
+ * @param integer the maximum length of the token. It must be between 2 and 40.
+ */
+ public function setMaxTokenLength($value)
+ {
+ $length=TPropertyValue::ensureInteger($value);
+ if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH)
+ $this->setViewState('MaxTokenLength',$length,6);
+ else
+ throw new TConfigurationException('captcha_maxtokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH);
+ }
+
+ /**
+ * @return boolean whether the token should be treated as case-sensitive. Defaults to true.
+ */
+ public function getCaseSensitive()
+ {
+ return $this->getViewState('CaseSensitive',true);
+ }
+
+ /**
+ * @param boolean whether the token should be treated as case-sensitive. If false, only upper-case letters will appear in the token.
+ */
+ public function setCaseSensitive($value)
+ {
+ $this->setViewState('CaseSensitive',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @return string the characters that may appear in the token. Defaults to '234578adefhijmnrtABDEFGHJLMNRT'.
+ */
+ public function getTokenAlphabet()
+ {
+ return $this->getViewState('TokenAlphabet','234578adefhijmnrtABDEFGHJLMNRT');
+ }
+
+ /**
+ * @param string the characters that may appear in the token. At least 2 characters must be specified.
+ */
+ public function setTokenAlphabet($value)
+ {
+ if(strlen($value)<2)
+ throw new TConfigurationException('captcha_tokenalphabet_invalid');
+ $this->setViewState('TokenAlphabet',$value,'234578adefhijmnrtABDEFGHJLMNRT');
+ }
+
+ /**
+ * @return integer the number of seconds that a generated token will remain valid. Defaults to 600 seconds (10 minutes).
+ */
+ public function getTokenExpiry()
+ {
+ return $this->getViewState('TokenExpiry',600);
+ }
+
+ /**
+ * @param integer the number of seconds that a generated token will remain valid. A value smaller than 1 means the token will not expire.
+ */
+ public function setTokenExpiry($value)
+ {
+ $this->setViewState('TokenExpiry',TPropertyValue::ensureInteger($value),600);
+ }
+
+ /**
+ * @return boolean whether the background of the token image should be variated during postbacks. Defaults to false.
+ */
+ public function getChangingTokenBackground()
+ {
+ return $this->getViewState('ChangingTokenBackground',false);
+ }
+
+ /**
+ * @param boolean whether the background of the token image should be variated during postbacks.
+ */
+ public function setChangingTokenBackground($value)
+ {
+ $this->setViewState('ChangingTokenBackground',TPropertyValue::ensureBoolean($value),false);
+ }
+
+ /**
+ * @return integer how many times a generated token can be tested. Defaults to 5.
+ */
+ public function getTestLimit()
+ {
+ return $this->getViewState('TestLimit',5);
+ }
+
+ /**
+ * @param integer how many times a generated token can be tested. For unlimited tests, set it to 0.
+ */
+ public function setTestLimit($value)
+ {
+ $this->setViewState('TestLimit',TPropertyValue::ensureInteger($value),5);
+ }
+
+ /**
+ * @return boolean whether the currently generated token has expired.
+ */
+ public function getIsTokenExpired()
+ {
+ if(($expiry=$this->getTokenExpiry())>0 && ($start=$this->getViewState('TokenGenerated',0))>0)
+ return $expiry+$start<time();
+ else
+ return false;
+ }
+
+ /**
+ * @return string the public key used for generating the token. A random one will be generated and returned if this is not set.
+ */
+ public function getPublicKey()
+ {
+ if(($publicKey=$this->getViewState('PublicKey',''))==='')
+ {
+ $publicKey=$this->generateRandomKey();
+ $this->setPublicKey($publicKey);
+ }
+ return $publicKey;
+ }
+
+ /**
+ * @param string the public key used for generating the token. A random one will be generated if this is not set.
+ */
+ public function setPublicKey($value)
+ {
+ $this->setViewState('PublicKey',$value,'');
+ }
+
+ /**
+ * @return string the token that will be displayed
+ */
+ public function getToken()
+ {
+ return $this->generateToken($this->getPublicKey(),$this->getPrivateKey(),$this->getTokenAlphabet(),$this->getTokenLength(),$this->getCaseSensitive());
+ }
+
+ /**
+ * @return integer the length of the token to be generated.
+ */
+ protected function getTokenLength()
+ {
+ if(($tokenLength=$this->getViewState('TokenLength'))===null)
+ {
+ $minLength=$this->getMinTokenLength();
+ $maxLength=$this->getMaxTokenLength();
+ if($minLength>$maxLength)
+ $tokenLength=rand($maxLength,$minLength);
+ else if($minLength<$maxLength)
+ $tokenLength=rand($minLength,$maxLength);
+ else
+ $tokenLength=$minLength;
+ $this->setViewState('TokenLength',$tokenLength);
+ }
+ return $tokenLength;
+ }
+
+ /**
+ * @return string the private key used for generating the token. This is randomly generated and kept in a file for persistency.
+ */
+ public function getPrivateKey()
+ {
+ if($this->_privateKey===null)
+ {
+ $fileName=$this->generatePrivateKeyFile();
+ $content=file_get_contents($fileName);
+ $matches=array();
+ if(preg_match("/privateKey='(.*?)'/ms",$content,$matches)>0)
+ $this->_privateKey=$matches[1];
+ else
+ throw new TConfigurationException('captcha_privatekey_unknown');
+ }
+ return $this->_privateKey;
+ }
+
+ /**
+ * Validates a user input with the token.
+ * @param string user input
+ * @return boolean if the user input is not the same as the token.
+ */
+ public function validate($input)
+ {
+ $number=$this->getViewState('TestNumber',0);
+ if(!$this->_validated)
+ {
+ $this->setViewState('TestNumber',++$number);
+ $this->_validated=true;
+ }
+ if($this->getIsTokenExpired() || (($limit=$this->getTestLimit())>0 && $number>$limit))
+ {
+ $this->regenerateToken();
+ return false;
+ }
+ return ($this->getToken()===($this->getCaseSensitive()?$input:strtoupper($input)));
+ }
+
+ /**
+ * Regenerates the token to be displayed.
+ * By default, a token, once generated, will remain the same during the following page postbacks.
+ * Calling this method will generate a new token.
+ */
+ public function regenerateToken()
+ {
+ $this->clearViewState('TokenLength');
+ $this->setPublicKey('');
+ $this->clearViewState('TokenGenerated');
+ $this->clearViewState('RandomSeed');
+ $this->clearViewState('TestNumber',0);
+ }
+
+ /**
+ * Configures the image URL that shows the token.
+ * @param mixed event parameter
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ if(!self::checkRequirements())
+ throw new TConfigurationException('captcha_imagettftext_required');
+ if(!$this->getViewState('TokenGenerated',0))
+ {
+ $manager=$this->getApplication()->getAssetManager();
+ $manager->publishFilePath($this->getFontFile());
+ $url=$manager->publishFilePath($this->getCaptchaScriptFile());
+ $url.='?options='.urlencode($this->getTokenImageOptions());
+ $this->setImageUrl($url);
+
+ $this->setViewState('TokenGenerated',time());
+ }
+ }
+
+ /**
+ * @return string the options to be passed to the token image generator
+ */
+ protected function getTokenImageOptions()
+ {
+ $privateKey=$this->getPrivateKey(); // call this method to ensure private key is generated
+ $token=$this->getToken();
+ $options=array();
+ $options['publicKey']=$this->getPublicKey();
+ $options['tokenLength']=strlen($token);
+ $options['caseSensitive']=$this->getCaseSensitive();
+ $options['alphabet']=$this->getTokenAlphabet();
+ $options['fontSize']=$this->getTokenFontSize();
+ $options['theme']=$this->getTokenImageTheme();
+ if(($randomSeed=$this->getViewState('RandomSeed',0))===0)
+ {
+ $randomSeed=(int)(microtime()*1000000);
+ $this->setViewState('RandomSeed',$randomSeed);
+ }
+ $options['randomSeed']=$this->getChangingTokenBackground()?0:$randomSeed;
+ $str=serialize($options);
+ return base64_encode(md5($privateKey.$str).$str);
+ }
+
+ /**
+ * @return string the file path of the PHP script generating the token image
+ */
+ protected function getCaptchaScriptFile()
+ {
+ return dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'captcha.php';
+ }
+
+ protected function getFontFile()
+ {
+ return dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'verase.ttf';
+ }
+
+ /**
+ * Generates a file with a randomly generated private key.
+ * @return string the path of the file keeping the private key
+ */
+ protected function generatePrivateKeyFile()
+ {
+ $captchaScript=$this->getCaptchaScriptFile();
+ $path=dirname($this->getApplication()->getAssetManager()->getPublishedPath($captchaScript));
+ $fileName=$path.DIRECTORY_SEPARATOR.'captcha_key.php';
+ if(!is_file($fileName))
+ {
+ @mkdir($path);
+ $key=$this->generateRandomKey();
+ $content="<?php
+\$privateKey='$key';
+?>";
+ file_put_contents($fileName,$content);
+ }
+ return $fileName;
+ }
+
+ /**
+ * @return string a randomly generated key
+ */
+ protected function generateRandomKey()
+ {
+ return md5(rand().rand().rand().rand());
+ }
+
+ /**
+ * Generates the token.
+ * @param string public key
+ * @param string private key
+ * @param integer the length of the token
+ * @param boolean whether the token is case sensitive
+ * @return string the token generated.
+ */
+ protected function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive)
+ {
+ $token=substr($this->hash2string(md5($publicKey.$privateKey),$alphabet).$this->hash2string(md5($privateKey.$publicKey),$alphabet),0,$tokenLength);
+ return $caseSensitive?$token:strtoupper($token);
+ }
+
+ /**
+ * Converts a hash string into a string with characters consisting of alphanumeric characters.
+ * @param string the hexadecimal representation of the hash string
+ * @param string the alphabet used to represent the converted string. If empty, it means '234578adefhijmnrtwyABDEFGHIJLMNQRTWY', which excludes those confusing characters.
+ * @return string the converted string
+ */
+ protected function hash2string($hex,$alphabet='')
+ {
+ if(strlen($alphabet)<2)
+ $alphabet='234578adefhijmnrtABDEFGHJLMNQRT';
+ $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;
+ }
+
+ /**
+ * Checks the requirements needed for generating CAPTCHA images.
+ * TCaptach requires GD2 with TrueType font support and PNG image support.
+ * @return boolean whether the requirements are satisfied.
+ */
+ public static function checkRequirements()
+ {
+ return extension_loaded('gd') && function_exists('imagettftext') && function_exists('imagepng');
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCaptchaValidator.php b/lib/prado/framework/Web/UI/WebControls/TCaptchaValidator.php
new file mode 100644
index 0000000..1a08430
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCaptchaValidator.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * TCaptchaValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+Prado::using('System.Web.UI.WebControls.TBaseValidator');
+Prado::using('System.Web.UI.WebControls.TCaptcha');
+
+/**
+ * TCaptchaValidator class
+ *
+ * Notice: while this class is easy to use and implement, it does not provide full security.
+ * In fact, it's easy to bypass the checks reusing old, already-validated tokens (reply attack).
+ * A better alternative is provided by {@link TReCaptchaValidator}.
+ *
+ * TCaptchaValidator validates user input against a CAPTCHA represented by
+ * a {@link TCaptcha} control. The input control fails validation if its value
+ * is not the same as the token displayed in CAPTCHA. Note, if the user does
+ * not enter any thing, it is still considered as failing the validation.
+ *
+ * To use TCaptchaValidator, specify the {@link setControlToValidate ControlToValidate}
+ * to be the ID path of the input control (usually a {@link TTextBox} control}.
+ * Also specify the {@link setCaptchaControl CaptchaControl} to be the ID path of
+ * the CAPTCHA control that the user input should be compared with.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TCaptchaValidator 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.TCaptchaValidator';
+ }
+
+ /**
+ * @return string the ID path of the CAPTCHA control to validate
+ */
+ public function getCaptchaControl()
+ {
+ return $this->getViewState('CaptchaControl','');
+ }
+
+ /**
+ * Sets the ID path of the CAPTCHA control to validate.
+ * The ID path is the dot-connected IDs of the controls reaching from
+ * the validator's naming container to the target control.
+ * @param string the ID path
+ */
+ public function setCaptchaControl($value)
+ {
+ $this->setViewState('CaptchaControl',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * 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 CAPTCHA control.
+ *
+ * @return boolean whether the validation succeeds
+ */
+ protected function evaluateIsValid()
+ {
+ $value=$this->getValidationValue($this->getValidationTarget());
+ $control=$this->findCaptchaControl();
+ return $control->validate(trim($value));
+ }
+
+ /**
+ * @return TCaptchaControl the CAPTCHA control to be validated against
+ * @throws TConfigurationException if the CAPTCHA cannot be found according to {@link setCaptchaControl CaptchaControl}
+ */
+ protected function findCaptchaControl()
+ {
+ if(($id=$this->getCaptchaControl())==='')
+ throw new TConfigurationException('captchavalidator_captchacontrol_required');
+ else if(($control=$this->findControl($id))===null)
+ throw new TConfigurationException('captchavalidator_captchacontrol_inexistent',$id);
+ else if(!($control instanceof TCaptcha))
+ throw new TConfigurationException('captchavalidator_captchacontrol_invalid',$id);
+ else
+ return $control;
+ }
+
+ /**
+ * Returns an array of javascript validator options.
+ * @return array javascript validator options.
+ */
+ protected function getClientScriptOptions()
+ {
+ $options=parent::getClientScriptOptions();
+ $control=$this->findCaptchaControl();
+ if($control->getCaseSensitive())
+ {
+ $options['TokenHash']=$this->generateTokenHash($control->getToken());
+ $options['CaseSensitive']=true;
+ }
+ else
+ {
+ $options['TokenHash']=$this->generateTokenHash(strtoupper($control->getToken()));
+ $options['CaseSensitive']=false;
+ }
+ return $options;
+ }
+
+ private function generateTokenHash($token)
+ {
+ for($h=0,$i=strlen($token)-1;$i>=0;--$i)
+ $h+=ord($token[$i]);
+ return $h;
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCheckBox.php b/lib/prado/framework/Web/UI/WebControls/TCheckBox.php
new file mode 100644
index 0000000..bfbfbf5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCheckBox.php
@@ -0,0 +1,536 @@
+<?php
+/**
+ * TCheckBox class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TCheckBox class
+ *
+ * TCheckBox displays a check box on the page.
+ * You can specify the caption to display beside the check box by setting
+ * the {@link setText Text} property. The caption can appear either on the right
+ * or left of the check box, which is determined by the {@link setTextAlign TextAlign}
+ * property.
+ *
+ * To determine whether the TCheckBox component is checked, test the {@link getChecked Checked}
+ * property. The {@link onCheckedChanged OnCheckedChanged} event is raised when
+ * the {@link getChecked Checked} state of the TCheckBox 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 TCheckBox component changes
+ * between posts to the server.
+ *
+ * If {@link setAutoPostBack AutoPostBack} is set true, changing the check box 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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TCheckBox extends TWebControl implements IPostBackDataHandler, IValidatable, IDataRenderer, ISurroundable
+{
+ private $_dataChanged=false;
+ private $_isValid=true;
+
+ /**
+ * @return string tag name of the button
+ */
+ protected function getTagName()
+ {
+ return 'input';
+ }
+
+ /**
+ * 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)
+ {
+ $checked=$this->getChecked();
+ if($newChecked=isset($values[$key]))
+ $this->setValue($values[$key]);
+ $this->setChecked($newChecked);
+ return $this->_dataChanged=($newChecked!==$checked);
+ }
+
+ /**
+ * Raises postdata changed event.
+ * This method raises {@link onCheckedChanged OnCheckedChanged} event.
+ * This method is primarly used by framework developers.
+ */
+ public function raisePostDataChangedEvent()
+ {
+ if($this->getAutoPostBack() && $this->getCausesValidation())
+ $this->getPage()->validate($this->getValidationGroup());
+ $this->onCheckedChanged(null);
+ }
+
+ /**
+ * Raises <b>OnCheckedChanged</b> event when {@link getChecked Checked} changes value during postback.
+ * 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 onCheckedChanged($param)
+ {
+ $this->raiseEvent('OnCheckedChanged',$this,$param);
+ }
+
+ /**
+ * Registers the checkbox to receive postback data during postback.
+ * This is necessary because a checkbox if unchecked, when postback,
+ * does not have direct mapping between post data and the checkbox name.
+ *
+ * This method overrides the parent implementation and is invoked before render.
+ * @param mixed event parameter
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ if($this->getEnabled(true))
+ $this->getPage()->registerRequiresPostData($this);
+ }
+
+ /**
+ * 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 of the property that needs validation.
+ * @return mixed the property value to be validated
+ */
+ public function getValidationPropertyValue()
+ {
+ return $this->getChecked();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * @return string the text caption of the checkbox
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * Sets the text caption of the checkbox.
+ * @param string the text caption to be set
+ */
+ public function setText($value)
+ {
+ $this->setViewState('Text',$value,'');
+ }
+
+ /**
+ * @return string the value of the checkbox. Defaults to empty.
+ */
+ public function getValue()
+ {
+ return $this->getViewState('Value','');
+ }
+
+ /**
+ * @param string the value of the checkbox
+ */
+ public function setValue($value)
+ {
+ $this->setViewState('Value',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return TTextAlign the alignment (Left or Right) of the text caption, defaults to TTextAlign::Right.
+ */
+ public function getTextAlign()
+ {
+ return $this->getViewState('TextAlign',TTextAlign::Right);
+ }
+
+ /**
+ * @param TTextAlign the alignment of the text caption. Valid values include Left and Right.
+ */
+ public function setTextAlign($value)
+ {
+ $this->setViewState('TextAlign',TPropertyValue::ensureEnum($value,'TTextAlign'),TTextAlign::Right);
+ }
+
+ /**
+ * @return boolean whether the checkbox is checked
+ */
+ public function getChecked()
+ {
+ return $this->getViewState('Checked',false);
+ }
+
+ /**
+ * Sets a value indicating whether the checkbox is to be checked or not.
+ * @param boolean whether the checkbox is to be checked or not.
+ */
+ public function setChecked($value)
+ {
+ $this->setViewState('Checked',TPropertyValue::ensureBoolean($value),false);
+ }
+
+ /**
+ * Returns the value indicating whether the checkbox is checked.
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link getChecked()}.
+ * @return boolean whether the checkbox is checked.
+ * @see getChecked
+ * @since 3.1.0
+ */
+ public function getData()
+ {
+ return $this->getChecked();
+ }
+
+ /**
+ * Sets the value indicating whether the checkbox is to be checked or not.
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link setChecked()}.
+ * @param boolean whether the checkbox is to be checked
+ * @see setChecked
+ * @since 3.1.0
+ */
+ public function setData($value)
+ {
+ $this->setChecked($value);
+ }
+
+ /**
+ * @return boolean whether clicking on the checkbox will post the page.
+ */
+ public function getAutoPostBack()
+ {
+ return $this->getViewState('AutoPostBack',false);
+ }
+
+ /**
+ * Sets a value indicating whether clicking on the checkbox will post the page.
+ * @param boolean whether clicking on the checkbox will post the page.
+ */
+ public function setAutoPostBack($value)
+ {
+ $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false);
+ }
+
+ /**
+ * @return boolean whether postback event triggered by this checkbox will cause input validation, default is true.
+ */
+ public function getCausesValidation()
+ {
+ return $this->getViewState('CausesValidation',true);
+ }
+
+ /**
+ * Sets the value indicating whether postback event trigger by this checkbox will cause input validation.
+ * @param boolean whether postback event trigger by this checkbox will cause input validation.
+ */
+ public function setCausesValidation($value)
+ {
+ $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @return string the group of validators which the checkbox causes validation upon postback
+ */
+ public function getValidationGroup()
+ {
+ return $this->getViewState('ValidationGroup','');
+ }
+
+ /**
+ * @param string the group of validators which the checkbox causes validation upon postback
+ */
+ public function setValidationGroup($value)
+ {
+ $this->setViewState('ValidationGroup',$value,'');
+ }
+
+ /**
+ * @return string the tag used to wrap the control in.
+ */
+ public function getSurroundingTag()
+ {
+ return 'span';
+ }
+
+ /**
+ * @return string the id of the surrounding tag or this clientID if no such tag needed.
+ */
+ public function getSurroundingTagID()
+ {
+ return $this->getSpanNeeded() ? $this->getClientID().'_parent' : $this->getClientID();
+ }
+
+ /**
+ * Renders the checkbox control.
+ * This method overrides the parent implementation by rendering a checkbox input element
+ * and a span element if needed.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ public function render($writer)
+ {
+ $this->getPage()->ensureRenderInForm($this);
+ if($this->getHasStyle())
+ $this->getStyle()->addAttributesToRender($writer);
+ if(($tooltip=$this->getToolTip())!=='')
+ $writer->addAttribute('title',$tooltip);
+ if($this->getHasAttributes())
+ {
+ $attributes=$this->getAttributes();
+ $value=$attributes->remove('value');
+ // onclick js should only be added to input tag
+ if(($onclick=$attributes->remove('onclick'))===null)
+ $onclick='';
+ if($attributes->getCount())
+ $writer->addAttributes($attributes);
+ if($value!==null)
+ $attributes->add('value',$value);
+ }
+ else
+ $onclick='';
+ if($needspan=$this->getSpanNeeded())
+ {
+ $writer->addAttribute('id',$this->getSurroundingTagID());
+ $writer->renderBeginTag($this->getSurroundingTag());
+ }
+ $clientID=$this->getClientID();
+ if(($text=$this->getText())!=='')
+ {
+ if($this->getTextAlign()===TTextAlign::Left)
+ {
+ $this->renderLabel($writer,$clientID,$text);
+ $this->renderInputTag($writer,$clientID,$onclick);
+ }
+ else
+ {
+ $this->renderInputTag($writer,$clientID,$onclick);
+ $this->renderLabel($writer,$clientID,$text);
+ }
+ }
+ else
+ $this->renderInputTag($writer,$clientID,$onclick);
+ if($needspan)
+ $writer->renderEndTag();
+ }
+
+ /**
+ * @return TMap list of attributes to be rendered for label beside the checkbox
+ */
+ public function getLabelAttributes()
+ {
+ if($attributes=$this->getViewState('LabelAttributes',null))
+ return $attributes;
+ else
+ {
+ $attributes=new TAttributeCollection;
+ $this->setViewState('LabelAttributes',$attributes,null);
+ return $attributes;
+ }
+ }
+
+ /**
+ * @return TMap list of attributes to be rendered for the checkbox
+ */
+ public function getInputAttributes()
+ {
+ if($attributes=$this->getViewState('InputAttributes',null))
+ return $attributes;
+ else
+ {
+ $attributes=new TAttributeCollection;
+ $this->setViewState('InputAttributes',$attributes,null);
+ return $attributes;
+ }
+ }
+
+ /**
+ * @return string the value attribute to be rendered
+ */
+ protected function getValueAttribute()
+ {
+ if(($value=$this->getValue())!=='')
+ return $value;
+ else
+ {
+ $attributes=$this->getViewState('InputAttributes',null);
+ if($attributes && $attributes->contains('value'))
+ return $attributes->itemAt('value');
+ else if($this->hasAttribute('value'))
+ return $this->getAttribute('value');
+ else
+ return '';
+ }
+ }
+
+ /**
+ * @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);
+ }
+
+ /**
+ * Check if we need a span tag to surround this control. The span tag will be created if
+ * the Text property is set for this control.
+ *
+ * @return bool wether this control needs a surrounding span tag
+ */
+ protected function getSpanNeeded() {
+ return $this->getText()!=='';
+ }
+
+ /**
+ * Renders a label beside the checkbox.
+ * @param THtmlWriter the writer for the rendering purpose
+ * @param string checkbox id
+ * @param string label text
+ */
+ protected function renderLabel($writer,$clientID,$text)
+ {
+ $writer->addAttribute('for',$clientID);
+ if($attributes=$this->getViewState('LabelAttributes',null))
+ $writer->addAttributes($attributes);
+ $writer->renderBeginTag('label');
+ $writer->write($text);
+ $writer->renderEndTag();
+ }
+
+ /**
+ * Renders a checkbox 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','checkbox');
+ if(($value=$this->getValueAttribute())!=='')
+ $writer->addAttribute('value',$value);
+ if(!empty($onclick))
+ $writer->addAttribute('onclick',$onclick);
+ if(($uniqueID=$this->getUniqueID())!=='')
+ $writer->addAttribute('name',$uniqueID);
+ 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.TCheckBox';
+ }
+
+ /**
+ * Gets the post back options for this checkbox.
+ * @return array
+ */
+ protected function getPostBackOptions()
+ {
+ $options['ID'] = $this->getClientID();
+ $options['ValidationGroup'] = $this->getValidationGroup();
+ $options['CausesValidation'] = $this->getCausesValidation();
+ $options['EventTarget'] = $this->getUniqueID();
+ return $options;
+ }
+}
+
+/**
+ * TTextAlign class.
+ * TTextAlign defines the enumerable type for the possible text alignments
+ *
+ * The following enumerable values are defined:
+ * - Left: left aligned
+ * - Right: right aligned
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TTextAlign extends TEnumerable
+{
+ const Left='Left';
+ const Right='Right';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCheckBoxColumn.php b/lib/prado/framework/Web/UI/WebControls/TCheckBoxColumn.php
new file mode 100644
index 0000000..5cf0265
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCheckBoxColumn.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * TCheckBoxColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TDataGridColumn class file
+ */
+Prado::using('System.Web.UI.WebControls.TDataGridColumn');
+/**
+ * TCheckBox class file
+ */
+Prado::using('System.Web.UI.WebControls.TCheckBox');
+
+/**
+ * TCheckBoxColumn class
+ *
+ * TCheckBoxColumn represents a checkbox column that is bound to a field in a data source.
+ * The checked state of the checkboxes are determiend by the bound data at
+ * {@link setDataField DataField}. If {@link setReadOnly ReadOnly} is false,
+ * TCheckBoxColumn will display an enabled checkbox provided the cells are
+ * in edit mode. Otherwise, the checkboxes will be disabled to prevent from editting.
+ *
+ * The checkbox control in the TCheckBoxColumn can be accessed by one of
+ * the following two methods:
+ * <code>
+ * $datagridItem->CheckBoxColumnID->CheckBox
+ * $datagridItem->CheckBoxColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the checkbox control created within the
+ * datagrid cell is the first child.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TCheckBoxColumn 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 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);
+ }
+
+ /**
+ * Initializes the specified cell to its initial values.
+ * This method overrides the parent implementation.
+ * It creates a checkbox inside the cell.
+ * If the column is read-only or if the item is not in edit mode,
+ * the checkbox will be set disabled.
+ * @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)
+ {
+ $checkBox=new TCheckBox;
+ if($this->getReadOnly() || $itemType!==TListItemType::EditItem)
+ $checkBox->setEnabled(false);
+ $cell->setHorizontalAlign('Center');
+ $cell->getControls()->add($checkBox);
+ $cell->registerObject('CheckBox',$checkBox);
+ if($this->getDataField()!=='')
+ $checkBox->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+ }
+ 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->getDataField())!=='')
+ $value=TPropertyValue::ensureBoolean($this->getDataFieldValue($data,$field));
+ else
+ $value=TPropertyValue::ensureBoolean($data);
+ if($sender instanceof TCheckBox)
+ $sender->setChecked($value);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCheckBoxList.php b/lib/prado/framework/Web/UI/WebControls/TCheckBoxList.php
new file mode 100644
index 0000000..9225e17
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCheckBoxList.php
@@ -0,0 +1,509 @@
+<?php
+/**
+ * TCheckBoxList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TListControl class
+ */
+Prado::using('System.Web.UI.WebControls.TListControl');
+/**
+ * Includes TRepeatInfo class
+ */
+Prado::using('System.Web.UI.WebControls.TRepeatInfo');
+/**
+ * Includes TCheckBox class
+ */
+Prado::using('System.Web.UI.WebControls.TCheckBox');
+
+/**
+ * TCheckBoxList class
+ *
+ * TCheckBoxList displays a list of checkboxes on a Web page.
+ *
+ * The layout of the checkbox list is specified via {@link setRepeatLayout RepeatLayout},
+ * which can be either 'Table' (default) or 'Flow'.
+ * A table layout uses HTML table cells to organize the checkboxes while
+ * a flow layout uses line breaks to organize the checkboxes.
+ * When the layout is using 'Table', {@link setCellPadding CellPadding} and
+ * {@link setCellSpacing CellSpacing} can be used to adjust the cellpadding and
+ * cellpadding of the table.
+ *
+ * The number of columns used to display the checkboxes is specified via
+ * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection}
+ * governs the order of the items being rendered.
+ *
+ * The alignment of the text besides each checkbox can be specified via {@link setTextAlign TextAlign}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TCheckBoxList extends TListControl implements IRepeatInfoUser, INamingContainer, IPostBackDataHandler, IValidatable
+{
+ private $_repeatedControl;
+ private $_isEnabled;
+ private $_changedEventRaised=false;
+ private $_dataChanged=false;
+ private $_isValid=true;
+
+ /**
+ * Constructor.
+ * Remember to call parent implementation if you override this method
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->_repeatedControl=$this->createRepeatedControl();
+ $this->_repeatedControl->setEnableViewState(false);
+ $this->_repeatedControl->setID('c0');
+ $this->getControls()->add($this->_repeatedControl);
+ }
+
+ /**
+ * Creates a control used for repetition (used as a template).
+ * @return TControl the control to be repeated
+ */
+ protected function createRepeatedControl()
+ {
+ return new TCheckBoxItem;
+ }
+
+ /**
+ * Finds a control by ID.
+ * This method overrides the parent implementation so that it always returns
+ * the checkbox list itself (because the checkbox list does not have child controls.)
+ * @param string control ID
+ * @return TControl control being found
+ */
+ public function findControl($id, $real=false)
+ {
+ if ($real===true)
+ return parent::findControl($id);
+ return $this;
+ }
+
+ /**
+ * @return boolean whether this control supports multiple selection. Always true for checkbox list.
+ */
+ protected function getIsMultiSelect()
+ {
+ return true;
+ }
+
+ /**
+ * Creates a style object for the control.
+ * This method creates a {@link TTableStyle} to be used by checkbox list.
+ * @return TStyle control style to be used
+ */
+ protected function createStyle()
+ {
+ return new TTableStyle;
+ }
+
+ /**
+ * @return TTextAlign the alignment of the text caption, defaults to TTextAlign::Right.
+ */
+ public function getTextAlign()
+ {
+ return $this->getViewState('TextAlign',TTextAlign::Right);
+ }
+
+ /**
+ * @param TTextAlign the text alignment of the checkboxes
+ */
+ public function setTextAlign($value)
+ {
+ $this->setViewState('TextAlign',TPropertyValue::ensureEnum($value,'TTextAlign'),TTextAlign::Right);
+ }
+
+
+ /**
+ * @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 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 string the direction of traversing the list, defaults to 'Vertical'
+ */
+ public function getRepeatDirection()
+ {
+ return $this->getRepeatInfo()->getRepeatDirection();
+ }
+
+ /**
+ * @param string the direction (Vertical, Horizontal) of traversing the list
+ */
+ public function setRepeatDirection($value)
+ {
+ $this->getRepeatInfo()->setRepeatDirection($value);
+ }
+
+ /**
+ * @return string how the list should be displayed, using table or using line breaks. Defaults to 'Table'.
+ */
+ public function getRepeatLayout()
+ {
+ return $this->getRepeatInfo()->getRepeatLayout();
+ }
+
+ /**
+ * @param string how the list should be displayed, using table or using line breaks (Table, Flow)
+ */
+ public function setRepeatLayout($value)
+ {
+ $this->getRepeatInfo()->setRepeatLayout($value);
+ }
+
+ /**
+ * @return integer the cellspacing for the table keeping the checkbox list. Defaults to -1, meaning not set.
+ */
+ public function getCellSpacing()
+ {
+ if($this->getHasStyle())
+ return $this->getStyle()->getCellSpacing();
+ else
+ return -1;
+ }
+
+ /**
+ * Sets the cellspacing for the table keeping the checkbox list.
+ * @param integer the cellspacing for the table keeping the checkbox list.
+ */
+ public function setCellSpacing($value)
+ {
+ $this->getStyle()->setCellSpacing($value);
+ }
+
+ /**
+ * @return integer the cellpadding for the table keeping the checkbox list. Defaults to -1, meaning not set.
+ */
+ public function getCellPadding()
+ {
+ if($this->getHasStyle())
+ return $this->getStyle()->getCellPadding();
+ else
+ return -1;
+ }
+
+ /**
+ * Sets the cellpadding for the table keeping the checkbox list.
+ * @param integer the cellpadding for the table keeping the checkbox list.
+ */
+ public function setCellPadding($value)
+ {
+ $this->getStyle()->setCellPadding($value);
+ }
+
+ /**
+ * Returns a value indicating whether this control contains header item.
+ * This method is required by {@link IRepeatInfoUser} interface.
+ * @return boolean always false.
+ */
+ public function getHasHeader()
+ {
+ return false;
+ }
+
+ /**
+ * Returns a value indicating whether this control contains footer item.
+ * This method is required by {@link IRepeatInfoUser} interface.
+ * @return boolean always false.
+ */
+ public function getHasFooter()
+ {
+ return false;
+ }
+
+ /**
+ * 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 false;
+ }
+
+ /**
+ * @param boolean whether the control is to be enabled.
+ */
+ public function setEnabled($value)
+ {
+ parent::setEnabled($value);
+ $value = !TPropertyValue::ensureBoolean($value);
+ // if this is an active control,
+ // and it's a callback,
+ // and we can update clientside,
+ // then update the 'disabled' attribute of the items.
+ if(($this instanceof IActiveControl) &&
+ $this->getPage()->getIsCallBack() &&
+ $this->getActiveControl()->canUpdateClientSide())
+ {
+ $items = $this->getItems();
+ $cs = $this->getPage()->getCallbackClient();
+ $baseClientID = $this->getClientID().'_c';
+ foreach($items as $index=>$item)
+ {
+ $cs->setAttribute($baseClientID.$index, 'disabled', $value);
+ }
+ }
+ }
+
+ /**
+ * 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 null
+ */
+ public function generateItemStyle($itemType,$index)
+ {
+ 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)
+ {
+ $repeatedControl=$this->_repeatedControl;
+ $item=$this->getItems()->itemAt($index);
+ if($item->getHasAttributes())
+ $repeatedControl->getAttributes()->copyFrom($item->getAttributes());
+ else if($repeatedControl->getHasAttributes())
+ $repeatedControl->getAttributes()->clear();
+ $repeatedControl->setID("c$index");
+ $repeatedControl->setText($item->getText());
+ $repeatedControl->setChecked($item->getSelected());
+ $repeatedControl->setAttribute('value',$item->getValue());
+ $repeatedControl->setEnabled($this->_isEnabled && $item->getEnabled());
+ $repeatedControl->setEnableClientScript(false);
+ $repeatedControl->renderControl($writer);
+ }
+
+ /**
+ * 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($this->getEnabled(true))
+ {
+ $index=(int)substr($key,strlen($this->getUniqueID())+2);
+ $this->ensureDataBound();
+ if($index>=0 && $index<$this->getItemCount())
+ {
+ $item=$this->getItems()->itemAt($index);
+ if($item->getEnabled())
+ {
+ $checked=isset($values[$key]);
+ if($item->getSelected()!==$checked)
+ {
+ $item->setSelected($checked);
+ if(!$this->_changedEventRaised)
+ {
+ $this->_changedEventRaised=true;
+ 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 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);
+ }
+
+ /**
+ * Registers for post data on postback.
+ * This method overrides the parent implementation.
+ * @param mixed event parameter
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ $this->_repeatedControl->setAutoPostBack($this->getAutoPostBack());
+ $this->_repeatedControl->setCausesValidation($this->getCausesValidation());
+ $this->_repeatedControl->setValidationGroup($this->getValidationGroup());
+ $page=$this->getPage();
+ $n=$this->getItemCount();
+ for($i=0;$i<$n;++$i)
+ {
+ $this->_repeatedControl->setID("c$i");
+ $page->registerRequiresPostData($this->_repeatedControl);
+ }
+ }
+
+ /**
+ * Wether the list should be rendered inside a span or not
+ *
+ *@return boolean true if we need a span
+ */
+ protected function getSpanNeeded ()
+ {
+ return $this->getRepeatLayout()===TRepeatLayout::Raw;
+ }
+
+ /**
+ * Renders the checkbox list control.
+ * This method overrides the parent implementation.
+ * @param THtmlWriter writer for rendering purpose.
+ */
+ public function render($writer)
+ {
+ if($this->getItemCount()>0)
+ {
+ if ($needSpan=$this->getSpanNeeded())
+ {
+ $writer->addAttribute('id', $this->getClientId());
+ $writer->renderBeginTag('span');
+ }
+ $this->_isEnabled=$this->getEnabled(true);
+ $repeatInfo=$this->getRepeatInfo();
+ $accessKey=$this->getAccessKey();
+ $tabIndex=$this->getTabIndex();
+ $this->_repeatedControl->setTextAlign($this->getTextAlign());
+ $this->_repeatedControl->setAccessKey($accessKey);
+ $this->_repeatedControl->setTabIndex($tabIndex);
+ $this->setAccessKey('');
+ $this->setTabIndex(0);
+ $this->addAttributesToRender($writer);
+ $repeatInfo->renderRepeater($writer,$this);
+ $this->setAccessKey($accessKey);
+ $this->setTabIndex($tabIndex);
+ if ($needSpan)
+ $writer->renderEndTag();
+ }
+
+ //checkbox skipped the client control script in addAttributesToRender
+ if($this->getEnabled(true)
+ && $this->getEnableClientScript()
+ && $this->getAutoPostBack()
+ && $this->getPage()->getClientSupportsJavaScript())
+ {
+ $this->renderClientControlScript($writer);
+ }
+ }
+
+ /**
+ * 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->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);
+ }
+
+ /**
+ * 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.TCheckBoxList';
+ }
+
+ /**
+ * Gets the post back options for this checkbox.
+ * @return array
+ */
+ protected function getPostBackOptions()
+ {
+ $options['ID'] = $this->getClientID();
+ $options['ValidationGroup'] = $this->getValidationGroup();
+ $options['CausesValidation'] = $this->getCausesValidation();
+ $options['ListName'] = $this->getUniqueID();
+ $options['ItemCount'] = $this->getItemCount();
+ return $options;
+ }
+
+}
+
+class TCheckBoxItem extends TCheckBox {
+ /**
+ * Override client implementation to avoid emitting the javascript
+ */
+ protected function renderClientControlScript($writer)
+ {
+ }
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TClientScript.php b/lib/prado/framework/Web/UI/WebControls/TClientScript.php
new file mode 100644
index 0000000..c931bcb
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TClientScript.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * TClientScript class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TClientScript class
+ *
+ * Allows importing of Prado Client Scripts from template via the
+ * {@link setPradoScripts PradoScripts} property. Multiple Prado
+ * client-scripts can be specified using comma delimited string of the
+ * javascript library to include on the page. For example,
+ *
+ * <code>
+ * <com:TClientScript PradoScripts="effects, rico" />
+ * </code>
+ *
+ * Custom javascript files can be register using the {@link setScriptUrl ScriptUrl}
+ * property.
+ * <code>
+ * <com:TClientScript ScriptUrl=<%~ test.js %> />
+ * </code>
+ *
+ * Contents within TClientScript will be treated as javascript code and will be
+ * rendered in place.
+ *
+ * Since Prado 3.2 the property {@link setFlushScriptFiles FlushScriptFiles} controls
+ * whether Prado will flush the script files defined in the page before rendering the
+ * TClientScript contents.
+ * If you're not using any external functions in your TClientScript block, you should
+ * set the {@link setFlushScriptFiles FlushScriptFiles} property to false, so Prado
+ * can postpone the loading of all the referenced script files further down the page
+ * generation cycle.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TClientScript extends TControl
+{
+ /**
+ * @return string comma delimited list of javascript libraries to included
+ * on the page.
+ */
+ public function getPradoScripts()
+ {
+ return $this->getViewState('PradoScripts', '');
+ }
+
+ /**
+ * Include javascript library to the current page. The current supported
+ * libraries are: "prado", "effects", "ajax", "validator", "logger",
+ * "datepicker", "colorpicker". Library dependencies are automatically resolved.
+ *
+ * @param string comma delimited list of javascript libraries to include.
+ */
+ public function setPradoScripts($value)
+ {
+ $this->setViewState('PradoScripts', $value, '');
+ }
+
+ /**
+ * @return string custom javascript file url.
+ */
+ public function getScriptUrl()
+ {
+ return $this->getViewState('ScriptUrl', '');
+ }
+
+ /**
+ * @param string custom javascript file url.
+ */
+ public function setScriptUrl($value)
+ {
+ $this->setViewState('ScriptUrl', $value, '');
+ }
+
+ /**
+ * @return bool whether to flush script files using TClientScriptManager::flushScriptFiles() before rendering the script block
+ */
+ public function getFlushScriptFiles()
+ {
+ return TPropertyValue::ensureBoolean($this->getViewState('FlushScriptFiles', true));
+ }
+
+ /**
+ * @param bool whether to flush script files using TClientScriptManager::flushScriptFiles() before rendering the script block
+ */
+ public function setFlushScriptFiles($value)
+ {
+ $this->setViewState('FlushScriptFiles', TPropertyValue::ensureBoolean($value));
+ }
+
+ /**
+ * Calls the client script manager to add each of the requested client
+ * script libraries.
+ * @param mixed event parameter
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ $scripts = preg_split('/,|\s+/', $this->getPradoScripts());
+ $cs = $this->getPage()->getClientScript();
+ foreach($scripts as $script)
+ {
+ if(($script = trim($script))!=='')
+ $cs->registerPradoScript($script);
+ }
+ }
+
+ /**
+ * Renders the body content as javascript block.
+ * Overrides parent implementation, parent renderChildren method is called during
+ * {@link registerCustomScript}.
+ * @param THtmlWriter the renderer
+ */
+ public function render($writer)
+ {
+ if ($this->getFlushScriptFiles())
+ $this->getPage()->getClientScript()->flushScriptFiles($writer, $this);
+ $this->renderCustomScriptFile($writer);
+ $this->renderCustomScript($writer);
+ }
+
+ /**
+ * Renders the custom script file.
+ * @param THtmLWriter the renderer
+ */
+ protected function renderCustomScriptFile($writer)
+ {
+ if(($scriptUrl = $this->getScriptUrl())!=='')
+ $writer->write("<script type=\"text/javascript\" src=\"$scriptUrl\"></script>\n");
+ }
+
+ /**
+ * Registers the body content as javascript.
+ * @param THtmlWriter the renderer
+ */
+ protected function renderCustomScript($writer)
+ {
+ if($this->getHasControls())
+ {
+ $writer->write("<script type=\"text/javascript\">\n/*<![CDATA[*/\n");
+ $this->renderChildren($writer);
+ $writer->write("\n/*]]>*/\n</script>\n");
+ }
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TColorPicker.php b/lib/prado/framework/Web/UI/WebControls/TColorPicker.php
new file mode 100644
index 0000000..fbe71c0
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TColorPicker.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * TColorPicker class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TColorPicker class.
+ *
+ * TColorPicker displays a text box for color input purpose.
+ * Next to the textbox there's a button filled with the current chosen color.
+ * Users can write a color name directly in the text box as an hex triplet (also known as HTML notation, eg: #FF00FF).
+ * Alternatively, if the <b>ShowColorPicker</b> property is enabled (it is by default), users can click the button
+ * to have a color picker UI appear. A color chan be chosen directly by clicking on the color picker.
+ *
+ * TColorPicker has three different color picker UI <b>Mode</b>s:
+ * # <b>Simple</b> - Grid with 12 simple colors.
+ * # <b>Basic</b> - Grid with the most common 70 colors. This is the default mode.
+ * # <b>Full</b> - Full-featured color picker.
+ *
+ * The <b>CssClass</b> property can be used to override the CSS class name
+ * for the color picker panel. The <b>ColorStyle</b> property sets the packages
+ * styles available. E.g. <b>default</b>.
+ *
+ * If the <b>Mode</b> property is set to <b>Full</b>, the color picker panel will
+ * display an "Ok" and "Cancel" buttons. You can customize the button labels setting the <b>OKButtonText</b>
+ * and <b>CancelButtonText</b> properties.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TColorPicker extends TTextBox
+{
+ const SCRIPT_PATH = 'prado/colorpicker';
+
+ private $_clientSide;
+
+ /**
+ * @return boolean whether the color picker should pop up when the button is clicked.
+ */
+ public function getShowColorPicker()
+ {
+ return $this->getViewState('ShowColorPicker',true);
+ }
+
+ /**
+ * Sets whether to pop up the color picker when the button is clicked.
+ * @param boolean whether to show the color picker popup
+ */
+ public function setShowColorPicker($value)
+ {
+ $this->setViewState('ShowColorPicker',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @param TColorPickerMode color picker UI mode
+ */
+ public function setMode($value)
+ {
+ $this->setViewState('Mode', TPropertyValue::ensureEnum($value, 'TColorPickerMode'), TColorPickerMode::Basic);
+ }
+
+ /**
+ * @return TColorPickerMode current color picker UI mode. Defaults to TColorPickerMode::Basic.
+ */
+ public function getMode()
+ {
+ return $this->getViewState('Mode', TColorPickerMode::Basic);
+ }
+
+ /**
+ * @param string set the color picker style
+ */
+ public function setColorPickerStyle($value)
+ {
+ $this->setViewState('ColorStyle', $value, 'default');
+ }
+
+ /**
+ * @return string current color picker style
+ */
+ public function getColorPickerStyle()
+ {
+ return $this->getViewState('ColorStyle', 'default');
+ }
+
+ /**
+ * @return string text for the color picker OK button. Default is "OK".
+ */
+ public function getOKButtonText()
+ {
+ return $this->getViewState('OKButtonText', 'OK');
+ }
+
+ /**
+ * @param string text for the color picker OK button
+ */
+ public function setOKButtonText($value)
+ {
+ $this->setViewState('OKButtonText', $value, 'OK');
+ }
+
+ /**
+ * @return string text for the color picker Cancel button. Default is "Cancel".
+ */
+ public function getCancelButtonText()
+ {
+ return $this->getViewState('CancelButtonText', 'Cancel');
+ }
+
+ /**
+ * @param string text for the color picker Cancel button
+ */
+ public function setCancelButtonText($value)
+ {
+ $this->setViewState('CancelButtonText', $value, 'Cancel');
+ }
+
+ /**
+ * @return TColorPickerClientSide javascript event options.
+ */
+ public function getClientSide()
+ {
+ if($this->_clientSide===null)
+ $this->_clientSide = $this->createClientSide();
+ return $this->_clientSide;
+ }
+
+ /**
+ * @return TColorPickerClientSide javascript validator event options.
+ */
+ protected function createClientSide()
+ {
+ return new TColorPickerClientSide;
+ }
+
+ /**
+ * Get javascript color picker options.
+ * @return array color picker client-side options
+ */
+ protected function getPostBackOptions()
+ {
+ $options = parent::getPostBackOptions();
+ $options['ClassName'] = $this->getCssClass();
+ $options['ShowColorPicker'] = $this->getShowColorPicker();
+ if($options['ShowColorPicker'])
+ {
+ $mode = $this->getMode();
+ if($mode == TColorPickerMode::Full) $options['Mode'] = $mode;
+ else if($mode == TColorPickerMode::Simple) $options['Palette'] = 'Tiny';
+ $options['OKButtonText'] = $this->getOKButtonText();
+ $options['CancelButtonText'] = $this->getCancelButtonText();
+ }
+ $options = array_merge($options,$this->getClientSide()->getOptions()->toArray());
+ return $options;
+ }
+
+ /**
+ * @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;
+ }
+
+ /**
+ * Publish the color picker Css asset files.
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ $this->publishColorPickerAssets();
+ }
+
+ /**
+ * Publish the color picker assets.
+ */
+ protected function publishColorPickerAssets()
+ {
+ $cs = $this->getPage()->getClientScript();
+ $key = "prado:".get_class($this);
+ $imgs['button.gif'] = $this->getAssetUrl('button.gif');
+ $imgs['background.png'] = $this->getAssetUrl('background.png');
+ $options = TJavaScript::encode($imgs);
+ $code = "Prado.WebUI.TColorPicker.UIImages = {$options};";
+ $cs->registerEndScript($key, $code);
+ $cs->registerPradoScript("colorpicker");
+ $url = $this->getAssetUrl($this->getColorPickerStyle().'.css');
+ if(!$cs->isStyleSheetFileRegistered($url))
+ $cs->registerStyleSheetFile($url, $url);
+ }
+
+ /**
+ * Renders additional body content.
+ * This method overrides parent implementation by adding
+ * additional color picker button.
+ * @param THtmlWriter writer
+ */
+ public function renderEndTag($writer)
+ {
+ parent::renderEndTag($writer);
+
+ $color = $this->getText();
+ $writer->addAttribute('class', 'TColorPicker_button');
+ $writer->renderBeginTag('span');
+
+ $writer->addAttribute('id', $this->getClientID().'_button');
+ $writer->addAttribute('src', $this->getAssetUrl('button.gif'));
+ if($color !== '')
+ $writer->addAttribute('style', "background-color:{$color};");
+ $writer->addAttribute('width', '20');
+ $writer->addAttribute('height', '20');
+ $writer->addAttribute('alt', '');
+ $writer->renderBeginTag('img');
+ $writer->renderEndTag();
+ $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.TColorPicker';
+ }
+}
+
+/**
+ * TColorPickerMode class.
+ * TColorPickerMode defines the enumerable type for the possible UI mode
+ * that a {@link TColorPicker} control can take.
+ *
+ * The following enumerable values are defined:
+ * # Simple - Grid with 12 simple colors.
+ * # Basic - Grid with the most common 70 colors. This is the default mode.
+ * # Full - Full-featured color picker.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TColorPickerMode extends TEnumerable
+{
+ const Simple='Simple';
+ const Basic='Basic';
+ const Full='Full';
+}
+
+/**
+ * TColorPickerClientSide class.
+ *
+ * Client-side javascript code options.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1
+ */
+class TColorPickerClientSide extends TClientSideOptions
+{
+ /**
+ * @return string javascript code for when a color is selected.
+ */
+ public function getOnColorSelected()
+ {
+ return $this->getOption('OnColorSelected');
+ }
+
+ /**
+ * @param string javascript code for when a color is selected.
+ */
+ public function setOnColorSelected($javascript)
+ {
+ $this->setFunction('OnColorSelected', $javascript);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCompareValidator.php b/lib/prado/framework/Web/UI/WebControls/TCompareValidator.php
new file mode 100644
index 0000000..3048895
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCompareValidator.php
@@ -0,0 +1,262 @@
+<?php
+/**
+ * TCompareValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TBaseValidator class
+ */
+Prado::using('System.Web.UI.WebControls.TBaseValidator');
+
+/**
+ * TCompareValidator class
+ *
+ * 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 {@link setControlToCompare ControlToCompare} property 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
+ * {@link setValueToCompare ValueToCompare} property.
+ *
+ * The {@link setDataType 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:
+ * - <b>Integer</b> A 32-bit signed integer data type.
+ * - <b>Float</b> A double-precision floating point number data type.
+ * - <b>Date</b> A date data type. The format can be specified by the
+ * {@link setDateFormat DateFormat} property
+ * - <b>String</b> A string data type.
+ *
+ * Use the {@link setOperator Operator} property to specify the type of comparison
+ * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual,
+ * LessThan and LessThanEqual.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TCompareValidator 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.TCompareValidator';
+ }
+
+ /**
+ * @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',TValidationDataType::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);
+ }
+
+ /**
+ * @return string the input component to compare with the input control being validated.
+ */
+ public function getControlToCompare()
+ {
+ return $this->getViewState('ControlToCompare','');
+ }
+
+ /**
+ * Sets the input component to compare with the input control being validated.
+ * @param string the ID path of the component to compare with
+ */
+ public function setControlToCompare($value)
+ {
+ $this->setViewState('ControlToCompare',$value,'');
+ }
+
+ /**
+ * @return string the constant value to compare with the value entered by the user into the input component being validated.
+ */
+ public function getValueToCompare()
+ {
+ return $this->getViewState('ValueToCompare','');
+ }
+
+ /**
+ * Sets the constant value to compare with the value entered by the user into the input component being validated.
+ * @param string the constant value
+ */
+ public function setValueToCompare($value)
+ {
+ $this->setViewState('ValueToCompare',$value,'');
+ }
+
+ /**
+ * @return TValidationCompareOperator the comparison operation to perform. Defaults to TValidationCompareOperator::Equal.
+ */
+ public function getOperator()
+ {
+ return $this->getViewState('Operator',TValidationCompareOperator::Equal);
+ }
+
+ /**
+ * Sets the comparison operation to perform
+ * @param TValidationCompareOperator the comparison operation
+ */
+ public function setOperator($value)
+ {
+ $this->setViewState('Operator',TPropertyValue::ensureEnum($value,'TValidationCompareOperator'),TValidationCompareOperator::Equal);
+ }
+
+ /**
+ * 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', '');
+ }
+
+ /**
+ * This method overrides the parent's implementation.
+ * The validation succeeds if the input data compares successfully.
+ * 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;
+
+ if(($controlToCompare=$this->getControlToCompare())!=='')
+ {
+ if(($control2=$this->findControl($controlToCompare))===null)
+ throw new TInvalidDataValueException('comparevalidator_controltocompare_invalid');
+ if(($value2=$this->getValidationValue($control2))==='')
+ return false;
+ }
+ else
+ $value2=$this->getValueToCompare();
+
+ $values = $this->getComparisonValues($value, $value2);
+ switch($this->getOperator())
+ {
+ case TValidationCompareOperator::Equal:
+ return $values[0] == $values[1];
+ case TValidationCompareOperator::NotEqual:
+ return $values[0] != $values[1];
+ case TValidationCompareOperator::GreaterThan:
+ return $values[0] > $values[1];
+ case TValidationCompareOperator::GreaterThanEqual:
+ return $values[0] >= $values[1];
+ case TValidationCompareOperator::LessThan:
+ return $values[0] < $values[1];
+ case TValidationCompareOperator::LessThanEqual:
+ return $values[0] <= $values[1];
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse the pair of values into the appropriate value type.
+ * @param string value one
+ * @param string second value
+ * @return array appropriate type of the value pair, array($value1, $value2);
+ */
+ protected function getComparisonValues($value1, $value2)
+ {
+ switch($this->getDataType())
+ {
+ case TValidationDataType::Integer:
+ return array(intval($value1), intval($value2));
+ case TValidationDataType::Float:
+ return array(floatval($value1), floatval($value2));
+ case TValidationDataType::Date:
+ $dateFormat = $this->getDateFormat();
+ if($dateFormat!=='')
+ {
+ $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat);
+ return array($formatter->parse($value1), $formatter->parse($value2));
+ }
+ else
+ return array(strtotime($value1), strtotime($value2));
+ }
+ return array($value1, $value2);
+ }
+
+ /**
+ * Returns an array of javascript validator options.
+ * @return array javascript validator options.
+ */
+ protected function getClientScriptOptions()
+ {
+ $options = parent::getClientScriptOptions();
+ if(($name=$this->getControlToCompare())!=='')
+ {
+ if(($control=$this->findControl($name))!==null)
+ $options['ControlToCompare']=$control->getClientID();
+ }
+ if(($value=$this->getValueToCompare())!=='')
+ $options['ValueToCompare']=$value;
+ if(($operator=$this->getOperator())!==TValidationCompareOperator::Equal)
+ $options['Operator']=$operator;
+ $options['DataType']=$this->getDataType();
+ if(($dateFormat=$this->getDateFormat())!=='')
+ $options['DateFormat']=$dateFormat;
+ return $options;
+ }
+}
+
+
+/**
+ * TValidationCompareOperator class.
+ * TValidationCompareOperator defines the enumerable type for the comparison operations
+ * that {@link TCompareValidator} can perform validation with.
+ *
+ * The following enumerable values are defined:
+ * - Equal
+ * - NotEqual
+ * - GreaterThan
+ * - GreaterThanEqual
+ * - LessThan
+ * - LessThanEqual
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TValidationCompareOperator extends TEnumerable
+{
+ const Equal='Equal';
+ const NotEqual='NotEqual';
+ const GreaterThan='GreaterThan';
+ const GreaterThanEqual='GreaterThanEqual';
+ const LessThan='LessThan';
+ const LessThanEqual='LessThanEqual';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TConditional.php b/lib/prado/framework/Web/UI/WebControls/TConditional.php
new file mode 100644
index 0000000..d9bc168
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TConditional.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * TConditional class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TConditional class.
+ *
+ * TConditional displays appropriate content based on the evaluation result
+ * of a PHP expression specified via {@link setCondition Condition}.
+ * If the result is true, it instantiates the template {@link getTrueTemplate TrueTemplate};
+ * otherwise, the template {@link getFalseTemplate FalseTemplate} is instantiated.
+ * The PHP expression is evaluated right before {@link onInit} stage of the control lifecycle.
+ *
+ * Since {@link setCondition Condition} is evaluated at a very early stage, it is recommended
+ * you set {@link setCondition Condition} in template and the expression should not refer to
+ * objects that are available on or after {@link onInit} lifecycle.
+ *
+ * A typical usage of TConditional is shown as following:
+ * <code>
+ * <com:TConditional Condition="$this->User->IsGuest">
+ * <prop:TrueTemplate>
+ * <a href="path/to/login">Login</a>
+ * </prop:TrueTemplate>
+ * <prop:FalseTemplate>
+ * <a href="path/to/logout">Logout</a>
+ * </prop:FalseTemplate>
+ * </com:TConditional>
+ * </code>
+ *
+ * TConditional is very light. It instantiates either {@link getTrueTemplate TrueTemplate}
+ * or {@link getFalseTemplate FalseTemplate}, but never both. And the condition is evaluated only once.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TConditional extends TControl
+{
+ private $_condition='true';
+ private $_trueTemplate;
+ private $_falseTemplate;
+ private $_creatingChildren=false;
+
+ /**
+ * Processes an object that is created during parsing template.
+ * This method overrides the parent implementation by removing
+ * all contents enclosed in the template tag.
+ * @param string|TComponent text string or component parsed and instantiated in template
+ * @see createdOnTemplate
+ */
+ public function addParsedObject($object)
+ {
+ if($this->_creatingChildren)
+ parent::addParsedObject($object);
+ }
+
+ /**
+ * Creates child controls.
+ * This method overrides the parent implementation. It evaluates {@link getCondition Condition}
+ * and instantiate the corresponding template.
+ */
+ public function createChildControls()
+ {
+ $this->_creatingChildren=true;
+ $result=true;
+ try
+ {
+ $result=$this->getTemplateControl()->evaluateExpression($this->_condition);
+ }
+ catch(Exception $e)
+ {
+ throw new TInvalidDataValueException('conditional_condition_invalid',$this->_condition,$e->getMessage());
+ }
+ if($result)
+ {
+ if($this->_trueTemplate)
+ $this->_trueTemplate->instantiateIn($this->getTemplateControl(),$this);
+ }
+ else if($this->_falseTemplate)
+ $this->_falseTemplate->instantiateIn($this->getTemplateControl(),$this);
+ $this->_creatingChildren=false;
+ }
+
+ /**
+ * @return string the PHP expression used for determining which template to use. Defaults to 'true', meaning using TrueTemplate.
+ */
+ public function getCondition()
+ {
+ return $this->_condition;
+ }
+
+ /**
+ * Sets the PHP expression to be evaluated for conditionally displaying content.
+ * The context of the expression is the template control containing TConditional.
+ * @param string the PHP expression used for determining which template to use.
+ */
+ public function setCondition($value)
+ {
+ $this->_condition=TPropertyValue::ensureString($value);
+ }
+
+ /**
+ * @return ITemplate the template applied when {@link getCondition Condition} is true.
+ */
+ public function getTrueTemplate()
+ {
+ return $this->_trueTemplate;
+ }
+
+ /**
+ * @param ITemplate the template applied when {@link getCondition Condition} is true.
+ */
+ public function setTrueTemplate(ITemplate $value)
+ {
+ $this->_trueTemplate=$value;
+ }
+
+ /**
+ * @return ITemplate the template applied when {@link getCondition Condition} is false.
+ */
+ public function getFalseTemplate()
+ {
+ return $this->_falseTemplate;
+ }
+
+ /**
+ * @param ITemplate the template applied when {@link getCondition Condition} is false.
+ */
+ public function setFalseTemplate(ITemplate $value)
+ {
+ $this->_falseTemplate=$value;
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TContent.php b/lib/prado/framework/Web/UI/WebControls/TContent.php
new file mode 100644
index 0000000..1a0fd37
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TContent.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * TContent class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TContent class
+ *
+ * TContent specifies a block of content on a control's template
+ * that will be injected at somewhere of the master control's template.
+ * TContentPlaceHolder and {@link TContent} together implement a decoration
+ * pattern for prado templated controls. A template control
+ * (called content control) can specify a master control
+ * whose template contains some TContentPlaceHolder controls.
+ * {@link TContent} controls on the content control's template will replace the corresponding
+ * {@link TContentPlaceHolder} controls on the master control's template.
+ * This is called content injection. It is done by matching the IDs of
+ * {@link TContent} and {@link TContentPlaceHolder} controls.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TContent extends TControl implements INamingContainer
+{
+ /**
+ * This method is invoked after the control is instantiated on a template.
+ * This overrides the parent implementation by registering the content control
+ * to the template owner control.
+ * @param TControl potential parent of this control
+ */
+ public function createdOnTemplate($parent)
+ {
+ if(($id=$this->getID())==='')
+ throw new TConfigurationException('content_id_required');
+ $this->getTemplateControl()->registerContent($id,$this);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TContentPlaceHolder.php b/lib/prado/framework/Web/UI/WebControls/TContentPlaceHolder.php
new file mode 100644
index 0000000..0355467
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TContentPlaceHolder.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * TContentPlaceHolder class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TContentPlaceHolder class
+ *
+ * TContentPlaceHolder reserves a place on a template where a {@link TContent}
+ * control can inject itself and its children in. TContentPlaceHolder and {@link TContent}
+ * together implement a decoration pattern for prado templated controls.
+ * A template control (called content control) can specify a master control
+ * whose template contains some TContentPlaceHolder controls.
+ * {@link TContent} controls on the content control's template will replace the corresponding
+ * {@link TContentPlaceHolder} controls on the master control's template.
+ * This is called content injection. It is done by matching the IDs of
+ * {@link TContent} and {@link TContentPlaceHolder} controls.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TContentPlaceHolder extends TControl
+{
+ /**
+ * This method is invoked after the control is instantiated on a template.
+ * This overrides the parent implementation by registering the content placeholder
+ * control to the template owner control. The placeholder control will NOT
+ * be added to the potential parent control!
+ * @param TControl potential parent of this control
+ */
+ public function createdOnTemplate($parent)
+ {
+ if(($id=$this->getID())==='')
+ throw new TConfigurationException('contentplaceholder_id_required');
+ $this->getTemplateControl()->registerContentPlaceHolder($id,$this);
+ $parent->getControls()->add($this);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TCustomValidator.php b/lib/prado/framework/Web/UI/WebControls/TCustomValidator.php
new file mode 100644
index 0000000..ca31a2a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TCustomValidator.php
@@ -0,0 +1,204 @@
+<?php
+/**
+ * TCustomValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * <script type="text/javascript"><!--
+ * function ValidationFunctionName(sender, parameter)
+ * {
+ * // if(parameter == ...)
+ * // return true;
+ * // else
+ * // return false;
+ * }
+ * --></script>
+ * </code>
+ * Use the {@link setClientValidationFunction ClientValidationFunction} property
+ * to specify the name of the client-side validation script function associated
+ * with the TCustomValidator.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <b>OnServerValidate</b> event.
+ * The method also allows derived classes to handle the event without attaching a delegate.
+ * <b>Note</b> The derived classes should call parent implementation
+ * to ensure the <b>OnServerValidate</b> 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
+ * <b>OnServerValidate</b> event of TCustomValidator components.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataBoundControl.php b/lib/prado/framework/Web/UI/WebControls/TDataBoundControl.php
new file mode 100644
index 0000000..8078b6f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataBoundControl.php
@@ -0,0 +1,582 @@
+<?php
+/**
+ * TDataBoundControl class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>OnDataBound</b> 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 <b>OnPreLoad</b> 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 <b>PreLoad</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataGrid.php b/lib/prado/framework/Web/UI/WebControls/TDataGrid.php
new file mode 100644
index 0000000..109252a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataGrid.php
@@ -0,0 +1,2244 @@
+<?php
+/**
+ * TDataGrid related class files.
+ * This file contains the definition of the following classes:
+ * TDataGrid, TDataGridItem, TDataGridItemCollection, TDataGridColumnCollection,
+ * TDataGridPagerStyle, TDataGridItemEventParameter,
+ * TDataGridCommandEventParameter, TDataGridSortCommandEventParameter,
+ * TDataGridPageChangedEventParameter
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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.
+ * <ul>
+ * <li>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.</li>
+ * <li>Specified in template. For example,
+ * <code>
+ * <com:TDataGrid ...>
+ * <com:TBoundColumn .../>
+ * <com:TEditCommandColumn .../>
+ * </com:TDataGrid>
+ * </code>
+ * </li>
+ * <li>Manually created in code. Columns can be manipulated via
+ * the {@link setColumns Columns} property of the datagrid. For example,
+ * <code>
+ * $column=new TBoundColumn;
+ * $datagrid->Columns[]=$column;
+ * </code>
+ * </li>
+ * </ul>
+ * 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}</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCancelCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event
+ * with <b>cancel</b> command name.
+ * @param TDataGridCommandEventParameter event parameter
+ */
+ public function onCancelCommand($param)
+ {
+ $this->raiseEvent('OnCancelCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnDeleteCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event
+ * with <b>delete</b> command name.
+ * @param TDataGridCommandEventParameter event parameter
+ */
+ public function onDeleteCommand($param)
+ {
+ $this->raiseEvent('OnDeleteCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnEditCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event
+ * with <b>edit</b> command name.
+ * @param TDataGridCommandEventParameter event parameter
+ */
+ public function onEditCommand($param)
+ {
+ $this->raiseEvent('OnEditCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnItemCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event.
+ * @param TDataGridCommandEventParameter event parameter
+ */
+ public function onItemCommand($param)
+ {
+ $this->raiseEvent('OnItemCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnSortCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event
+ * with <b>sort</b> command name.
+ * @param TDataGridSortCommandEventParameter event parameter
+ */
+ public function onSortCommand($param)
+ {
+ $this->raiseEvent('OnSortCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnUpdateCommand</b> event.
+ * This method is invoked when a button control raises <b>OnCommand</b> event
+ * with <b>update</b> command name.
+ * @param TDataGridCommandEventParameter event parameter
+ */
+ public function onUpdateCommand($param)
+ {
+ $this->raiseEvent('OnUpdateCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnItemCreated</b> 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 <b>OnPagerCreated</b> 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 <b>OnItemDataBound</b> 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 <b>OnPageIndexChanged</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>Command</b> 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 <b>Command</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> event.
+ * @param integer new page index
+ */
+ public function __construct($source,$newPageIndex)
+ {
+ $this->_source=$source;
+ $this->_newIndex=$newPageIndex;
+ }
+
+ /**
+ * @return TControl the control originally raises the <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataGridColumn.php b/lib/prado/framework/Web/UI/WebControls/TDataGridColumn.php
new file mode 100644
index 0000000..2f547ff
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataGridColumn.php
@@ -0,0 +1,564 @@
+<?php
+/**
+ * TDataGridColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>Data</b> 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 <b>Data</b> 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 <b>Sort</b> 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('&nbsp;');
+ }
+ 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('&nbsp;');
+ }
+ }
+
+ /**
+ * 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('&nbsp;');
+ }
+
+ /**
+ * 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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataGridItemRenderer.php b/lib/prado/framework/Web/UI/WebControls/TDataGridItemRenderer.php
new file mode 100644
index 0000000..23cf515
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataGridItemRenderer.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * TDataGridItemRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.0
+ */
+class TDataGridItemRenderer extends TItemDataRenderer
+{
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TDataGridPagerStyle.php b/lib/prado/framework/Web/UI/WebControls/TDataGridPagerStyle.php
new file mode 100644
index 0000000..b96d0cd
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataGridPagerStyle.php
@@ -0,0 +1,253 @@
+<?php
+/**
+ * TDataGridPagerStyle class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataList.php b/lib/prado/framework/Web/UI/WebControls/TDataList.php
new file mode 100644
index 0000000..a3cf8b6
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataList.php
@@ -0,0 +1,1760 @@
+<?php
+/**
+ * TDataList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>Data</b>
+ * 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 <b>ItemIndex</b> property will be set
+ * as the zero-based index of the item in the datalist item collection, and
+ * the <b>ItemType</b> 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 <b>OnCommand</b> event. Therefore,
+ * you can handle all sorts of <b>OnCommand</b> event in a central place by
+ * writing an event handler for {@link onItemCommand OnItemCommand}.
+ *
+ * An additional event is raised if the <b>OnCommand</b> event has one of the following
+ * command names:
+ * - edit: user wants to edit an item. <b>OnEditCommand</b> event will be raised.
+ * - update: user wants to save the change to an item. <b>OnUpdateCommand</b> event will be raised.
+ * - select: user selects an item. <b>OnSelectedIndexChanged</b> event will be raised.
+ * - delete: user deletes an item. <b>OnDeleteCommand</b> event will be raised.
+ * - cancel: user cancels previously editting action. <b>OnCancelCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnItemCreated</b> 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 <b>OnItemDataBound</b> 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 <b>OnItemCommand</b> event.
+ * This method is invoked when a child control of the data list
+ * raises an <b>OnCommand</b> event.
+ * @param TDataListCommandEventParameter event parameter
+ */
+ public function onItemCommand($param)
+ {
+ $this->raiseEvent('OnItemCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnEditCommand</b> event.
+ * This method is invoked when a child control of the data list
+ * raises an <b>OnCommand</b> event and the command name is 'edit' (case-insensitive).
+ * @param TDataListCommandEventParameter event parameter
+ */
+ public function onEditCommand($param)
+ {
+ $this->raiseEvent('OnEditCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnDeleteCommand</b> event.
+ * This method is invoked when a child control of the data list
+ * raises an <b>OnCommand</b> event and the command name is 'delete' (case-insensitive).
+ * @param TDataListCommandEventParameter event parameter
+ */
+ public function onDeleteCommand($param)
+ {
+ $this->raiseEvent('OnDeleteCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnUpdateCommand</b> event.
+ * This method is invoked when a child control of the data list
+ * raises an <b>OnCommand</b> event and the command name is 'update' (case-insensitive).
+ * @param TDataListCommandEventParameter event parameter
+ */
+ public function onUpdateCommand($param)
+ {
+ $this->raiseEvent('OnUpdateCommand',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnCancelCommand</b> event.
+ * This method is invoked when a child control of the data list
+ * raises an <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataListItemRenderer.php b/lib/prado/framework/Web/UI/WebControls/TDataListItemRenderer.php
new file mode 100644
index 0000000..d7cb59c
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataListItemRenderer.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * TDataListItemRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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/lib/prado/framework/Web/UI/WebControls/TDataRenderer.php b/lib/prado/framework/Web/UI/WebControls/TDataRenderer.php
new file mode 100644
index 0000000..6e76013
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataRenderer.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * TDataRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataSourceControl.php b/lib/prado/framework/Web/UI/WebControls/TDataSourceControl.php
new file mode 100644
index 0000000..ff47a78
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataSourceControl.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * IDataSource, TDataSourceControl, TReadOnlyDataSource class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * IDataSource class
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataSourceView.php b/lib/prado/framework/Web/UI/WebControls/TDataSourceView.php
new file mode 100644
index 0000000..ed6d868
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataSourceView.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * TDataSourceSelectParameters, TDataSourceView, TReadOnlyDataSourceView class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TDataSourceSelectParameters class
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDataTypeValidator.php b/lib/prado/framework/Web/UI/WebControls/TDataTypeValidator.php
new file mode 100644
index 0000000..b78a962
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDataTypeValidator.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * TDataTypeValidator class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * - <b>Integer</b> A 32-bit signed integer data type.
+ * - <b>Float</b> A double-precision floating point number data type.
+ * - <b>Date</b> A date data type.
+ * - <b>String</b> A string data type.
+ * For <b>Date</b> 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 <weizhuo[at]gmail[dot]com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDatePicker.php b/lib/prado/framework/Web/UI/WebControls/TDatePicker.php
new file mode 100644
index 0000000..3f9e445
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDatePicker.php
@@ -0,0 +1,978 @@
+<?php
+/**
+ * TDatePicker class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>DateFormat</b> property. Valid formats are the combination of the
+ * following tokens,
+ *
+ * <code>
+ * 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
+ * -----------------------------------------
+ * </code>
+ *
+ * TDatePicker has four <b>Mode</b> to show the date picker popup.
+ *
+ * # <b>Basic</b> -- 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.
+ * # <b>Clickable</b> -- 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.
+ * # <b>Button</b> -- Shows a button next to the text input, clicking on the
+ * button shows the date, button text can be by the
+ * <b>ButtonText</b> property
+ * # <b>ImageButton</b> -- Shows an image next to the text input, clicking on
+ * the image shows the date picker, image source can be
+ * change through the <b>ButtonImageUrl</b> property.
+ *
+ * The <b>CssClass</b> property can be used to override the css class name
+ * for the date picker panel. <b>CalendarStyle</b> property sets the packages
+ * styles available. E.g. <b>default</b>.
+ *
+ * The <b>InputMode</b> property can be set to "TextBox" or "DropDownList" with
+ * default as "TextBox".
+ * In <b>DropDownList</b> mode, in addition to the popup date picker, three
+ * drop down list (day, month and year) are presented to select the date .
+ *
+ * The <b>PositionMode</b> 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 <weizhuo[at]gmail[dot]com>
+ * @author Carl G. Mathisen <carlgmathisen@gmail.com>
+ * @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.
+ * # <tt>OnDateChanged</tt> -- 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);
+ $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->renderClientControlScript($writer);
+ $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');
+ $writer->addAttribute('class', 'datepicker_year_options');
+ if($this->getReadOnly() || !$this->getEnabled(true))
+ $writer->addAttribute('disabled', 'disabled');
+ $writer->renderBeginTag('select');
+ $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());
+ }
+
+ /**
+ * Registers the javascript code to initialize the date picker.
+ */
+ protected function registerCalendarClientScriptPre()
+ {
+ if($this->getShowCalendar())
+ {
+ $cs = $this->getPage()->getClientScript();
+ $cs->registerPradoScript("datepicker");
+ }
+ }
+
+ protected function renderClientControlScript($writer)
+ {
+ 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 <tt>OnDateChanged</tt> 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 <weizhuo[at]gmail[dot]com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <carlgmathisen@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.4
+ */
+class TDatePickerPositionMode extends TEnumerable
+{
+ const Top='Top';
+ const Bottom='Bottom';
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TDropDownList.php b/lib/prado/framework/Web/UI/WebControls/TDropDownList.php
new file mode 100644
index 0000000..0598d24
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDropDownList.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * TDropDownList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * $listitem->Attributes->Group="Group Name";
+ * // or <com:TListItem Attributes.Group="Group Name" .../> in template
+ * </code>
+ *
+ * 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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TDropDownListColumn.php b/lib/prado/framework/Web/UI/WebControls/TDropDownListColumn.php
new file mode 100644
index 0000000..507dafd
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TDropDownListColumn.php
@@ -0,0 +1,319 @@
+<?php
+/**
+ * TDropDownListColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * <com:TDropDownListColumn ....>
+ * <com:TListItem Value="1" Text="first item" />
+ * <com:TListItem Value="2" Text="second item" />
+ * <com:TListItem Value="3" Text="third item" />
+ * </com:TDropDownListColumn>
+ * </code>
+ * 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:
+ * <code>
+ * $datagridItem->DropDownListColumnID->DropDownList
+ * $datagridItem->DropDownListColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the dropdown list control created within the
+ * datagrid cell is the first child.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TEditCommandColumn.php b/lib/prado/framework/Web/UI/WebControls/TEditCommandColumn.php
new file mode 100644
index 0000000..36e188f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TEditCommandColumn.php
@@ -0,0 +1,263 @@
+<?php
+/**
+ * TEditCommandColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * $datagridItem->ButtonColumnID->EditButton (or UpdateButton, CancelButton)
+ * $datagridItem->ButtonColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the button control created within the
+ * datagrid cell is the first child.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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('&nbsp;');
+ $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/lib/prado/framework/Web/UI/WebControls/TEmailAddressValidator.php b/lib/prado/framework/Web/UI/WebControls/TEmailAddressValidator.php
new file mode 100644
index 0000000..bb95372
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TEmailAddressValidator.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * TEmailAddressValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TEmailAddressValidator extends TRegularExpressionValidator
+{
+ /**
+ * Regular expression used to validate the email address
+ * @see http://www.regular-expressions.info/email.html
+ */
+ const EMAIL_REGEXP='[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?';
+
+ /**
+ * 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()
+ {
+ $value=$this->getValidationValue($this->getValidationTarget());
+ $valid=$valid=is_string($value) && strlen($value)<=254 && parent::evaluateIsValid();
+
+ if($valid && $this->getCheckMXRecord() && function_exists('checkdnsrr'))
+ {
+ if($value!=='')
+ {
+ 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',false);
+ }
+
+ /**
+ * @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),false);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TExpression.php b/lib/prado/framework/Web/UI/WebControls/TExpression.php
new file mode 100644
index 0000000..57bbb59
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TExpression.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * TExpression class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TFileUpload.php b/lib/prado/framework/Web/UI/WebControls/TFileUpload.php
new file mode 100644
index 0000000..c8853c5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TFileUpload.php
@@ -0,0 +1,279 @@
+<?php
+/**
+ * TFileUpload class file
+ *
+ * @author Marcus Nyeholt <tanus@users.sourceforge.net>, Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <tanus@users.sourceforge.net>, Qiang Xue <qiang.xue@gmail.com>
+ * @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 <b>OnFileUpload</b> 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/lib/prado/framework/Web/UI/WebControls/TFlushOutput.php b/lib/prado/framework/Web/UI/WebControls/TFlushOutput.php
new file mode 100644
index 0000000..a88b91f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TFlushOutput.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * TFlushOutput class file
+ *
+ * @author Berczi Gabor <gabor.berczi@devworx.hu>
+ * @link https://github.com/pradosoft/prado
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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
+ * <code>
+ * <com:TFlushOutput />
+ * </code>
+ *
+ * You can specify whether you want to keep buffering of the output
+ * (if it was enabled) till the next occourence of a <com: TFlushOutput />
+ * or the end of the page rendering, or stop buffering, by using the
+ * {@link setContinueBuffering ContinueBuffering}.
+ *
+ * @author Berczi Gabor <gabor.berczi@devworx.hu>
+ * @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('<!-- flush -->');
+ // 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);
+ }
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TFont.php b/lib/prado/framework/Web/UI/WebControls/TFont.php
new file mode 100644
index 0000000..52a5db7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TFont.php
@@ -0,0 +1,316 @@
+<?php
+/**
+ * TFont class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TFont class
+ *
+ * TFont encapsulates the CSS style fields related with font settings.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/THead.php b/lib/prado/framework/Web/UI/WebControls/THead.php
new file mode 100644
index 0000000..c7c9b57
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THead.php
@@ -0,0 +1,372 @@
+<?php
+/**
+ * THead class file
+ *
+ * @author Marcus Nyeholt <tanus@users.sourceforge.net> and Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * <com:THead>
+ * <com:TMetaTag HttpEquiv="Pragma" Content="no-cache" />
+ * <com:TMetaTag Name="keywords" Content="Prado" />
+ * </com:THead>
+ * </code>
+ *
+ * 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 <tanus@users.sourceforge.net> and Qiang Xue <qiang.xue@gmail.com>
+ * @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 <base> 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 <base> 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("<head>\n<title>".THttpUtility::htmlEncode($title)."</title>\n");
+ if(($baseUrl=$this->getBaseUrl())!=='')
+ $writer->write('<base href="'.$baseUrl."\" />\n");
+ if(($icon=$this->getShortcutIcon())!=='')
+ $writer->write('<link rel="shortcut icon" href="'.$icon."\" />\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("</head>\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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/THeader1.php b/lib/prado/framework/Web/UI/WebControls/THeader1.php
new file mode 100644
index 0000000..5eee499
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader1.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader1 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader1 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2a
+ */
+
+class THeader1 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h1';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THeader2.php b/lib/prado/framework/Web/UI/WebControls/THeader2.php
new file mode 100644
index 0000000..c54b52f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader2.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader2 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader2 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2a
+ */
+
+class THeader2 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h2';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THeader3.php b/lib/prado/framework/Web/UI/WebControls/THeader3.php
new file mode 100644
index 0000000..ef89e02
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader3.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader3 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader3 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2a
+ */
+
+class THeader3 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h3';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THeader4.php b/lib/prado/framework/Web/UI/WebControls/THeader4.php
new file mode 100644
index 0000000..d2dada4
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader4.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader4 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader4 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+class THeader4 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h4';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THeader5.php b/lib/prado/framework/Web/UI/WebControls/THeader5.php
new file mode 100644
index 0000000..582567d
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader5.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader5 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader5 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+class THeader5 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h5';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THeader6.php b/lib/prado/framework/Web/UI/WebControls/THeader6.php
new file mode 100644
index 0000000..ae876f8
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THeader6.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * THeader6 class file
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * THeader6 class
+ *
+ * This is a simple class to enable your application to have headers but then have your
+ * theme be able to redefine the TagName
+ * This is also useful for the {@link TWebControlDecorator} (used by themes).
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+class THeader6 extends THtmlElement {
+
+ /**
+ * @return string tag name
+ */
+ public function getDefaultTagName()
+ {
+ return 'h6';
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/THiddenField.php b/lib/prado/framework/Web/UI/WebControls/THiddenField.php
new file mode 100644
index 0000000..8ea1c5b
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THiddenField.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * THiddenField class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.xisc.com/
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @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 <qiang.xue@gmail.com>
+ * @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();
+ }
+
+ /**
+ * 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 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/lib/prado/framework/Web/UI/WebControls/THtmlArea.php b/lib/prado/framework/Web/UI/WebControls/THtmlArea.php
new file mode 100644
index 0000000..7fc5969
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THtmlArea.php
@@ -0,0 +1,535 @@
+<?php
+/**
+ * THtmlArea class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TTextBox class
+ */
+Prado::using('System.Web.UI.WebControls.TTextBox');
+
+/**
+ * THtmlArea class
+ *
+ * THtmlArea wraps the visual editting functionalities provided by the
+ * TinyMCE project {@link http://tinymce.moxiecode.com/}.
+ *
+ * THtmlArea displays a WYSIWYG text area on the Web page for user input
+ * in the HTML format. The text displayed in the THtmlArea component is
+ * specified or determined by using the <b>Text</b> property.
+ *
+ * To enable the visual editting on the client side, set the property
+ * <b>EnableVisualEdit</b> to true (which is default value).
+ * To set the size of the editor when the visual editting is enabled,
+ * set the <b>Width</b> and <b>Height</b> properties instead of
+ * <b>Columns</b> and <b>Rows</b> because the latter has no meaning
+ * under the situation.
+ *
+ * The default editor gives only the basic tool bar. To change or add
+ * additional tool bars, use the {@link setOptions Options} property to add additional
+ * editor options with each options on a new line.
+ * See http://tinymce.moxiecode.com/tinymce/docs/index.html
+ * for a list of options. The options can be change/added as shown in the
+ * following example.
+ * <code>
+ * <com:THtmlArea>
+ * <prop:Options>
+ * plugins : "contextmenu,paste"
+ * language : "zh_cn"
+ * </prop:Options>
+ * </com:THtmlArea>
+ * </code>
+ *
+ * Compatibility
+ * The client-side visual editting capability is supported by
+ * Internet Explorer 5.0+ for Windows and Gecko-based browser.
+ * If the browser does not support the visual editting,
+ * a traditional textarea will be displayed.
+ *
+ * Browser support
+ *
+ * <code>
+ * Windows XP MacOS X 10.4
+ * ----------------------------------------------------
+ * MSIE 6 OK
+ * MSIE 5.5 SP2 OK
+ * MSIE 5.0 OK
+ * Mozilla 1.7.x OK OK
+ * Firefox 1.0.x OK OK
+ * Firefox 1.5b2 OK OK
+ * Safari 2.0 (412) OK(1)
+ * Opera 9 Preview 1 OK(1) OK(1)
+ * ----------------------------------------------------
+ * * (1) - Partialy working
+ * ----------------------------------------------------
+ * </code>
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class THtmlArea extends TTextBox
+{
+ /**
+ * @var array list of locale => language file pairs.
+ */
+ private static $_langs = array(
+ 'ar' => 'ar',
+ 'az' => 'az',
+ 'be' => 'be',
+ 'bg' => 'bg',
+ 'bn' => 'bn',
+ 'br' => 'br',
+ 'bs' => 'bs',
+ 'ca' => 'ca',
+ 'ch' => 'ch',
+ 'cn' => 'cn',
+ 'cs' => 'cs',
+ 'cy' => 'cy',
+ 'da' => 'da',
+ 'de' => 'de',
+ 'dv' => 'dv',
+ 'el' => 'el',
+ 'en' => 'en',
+ 'eo' => 'eo',
+ 'es' => 'es',
+ 'et' => 'et',
+ 'eu' => 'eu',
+ 'fa' => 'fa',
+ 'fi' => 'fi',
+ 'fr' => 'fr',
+ 'gl' => 'gl',
+ 'gu' => 'gu',
+ 'he' => 'he',
+ 'hi' => 'hi',
+ 'hr' => 'hr',
+ 'hu' => 'hu',
+ 'hy' => 'hy',
+ 'ia' => 'ia',
+ 'id' => 'id',
+ 'is' => 'is',
+ 'it' => 'it',
+ 'ja' => 'ja',
+ 'ka' => 'ka',
+ 'kl' => 'kl',
+ 'km' => 'km',
+ 'ko' => 'ko',
+ 'lb' => 'lb',
+ 'lt' => 'lt',
+ 'lv' => 'lv',
+ 'mk' => 'mk',
+ 'ml' => 'ml',
+ 'mn' => 'mn',
+ 'ms' => 'ms',
+ 'my' => 'my',
+ 'nb' => 'nb',
+ 'nl' => 'nl',
+ 'nn' => 'nn',
+ 'no' => 'no',
+ 'pl' => 'pl',
+ 'ps' => 'ps',
+ 'pt' => 'pt',
+ 'ro' => 'ro',
+ 'ru' => 'ru',
+ 'sc' => 'sc',
+ 'se' => 'se',
+ 'si' => 'si',
+ 'sk' => 'sk',
+ 'sl' => 'sl',
+ 'sq' => 'sq',
+ 'sr' => 'sr',
+ 'sv' => 'sv',
+ 'ta' => 'ta',
+ 'te' => 'te',
+ 'th' => 'th',
+ 'tn' => 'tn',
+ 'tr' => 'tr',
+ 'tt' => 'tt',
+ 'tw' => 'tw',
+ 'uk' => 'vi',
+ 'ur' => 'vi',
+ 'vi' => 'vi',
+ 'zh_CN' => 'zh-cn',
+ 'zh_TW' => 'zh-tw',
+ 'zh' => 'zh',
+ 'zu' => 'zu',
+ );
+
+ /**
+ * @var array list of default plugins to load, override using getAvailablePlugins();
+ */
+ private static $_plugins = array(
+ 'advhr',
+ 'advimage',
+ 'advlink',
+ 'advlist',
+ 'autolink',
+ 'autoresize',
+ 'autosave',
+ 'bbcode',
+ 'contextmenu',
+ 'directionality',
+ 'emotions',
+ 'example',
+ 'fullpage',
+ 'fullscreen',
+ 'iespell',
+ 'inlinepopups',
+ 'insertdatetime',
+ 'layer',
+ 'legacyoutput',
+ 'lists',
+ 'media',
+ 'nonbreaking',
+ 'noneditable',
+ 'pagebreak',
+ 'paste',
+ 'preview',
+ 'print',
+ 'save',
+ 'searchreplace',
+ 'spellchecker',
+ 'style',
+ 'tabfocus',
+ 'table',
+ 'template',
+ 'visualchars',
+ 'wordc',
+ 'wordcount',
+ 'xhtmlxtras'
+ );
+
+ /**
+ * @var array default themes to load
+ */
+ private static $_themes = array(
+ 'simple',
+ 'advanced'
+ );
+
+ /**
+ * Constructor.
+ * Sets default width and height.
+ */
+ public function __construct()
+ {
+ $this->setWidth('470px');
+ $this->setHeight('250px');
+ }
+
+ /**
+ * Overrides the parent implementation.
+ * TextMode for THtmlArea control is always 'MultiLine'
+ * @return string the behavior mode of the THtmlArea component.
+ */
+ public function getTextMode()
+ {
+ return 'MultiLine';
+ }
+
+ /**
+ * Overrides the parent implementation.
+ * TextMode for THtmlArea is always 'MultiLine' and cannot be changed to others.
+ * @param string the text mode
+ */
+ public function setTextMode($value)
+ {
+ throw new TInvalidOperationException("htmlarea_textmode_readonly");
+ }
+
+ /**
+ * @return boolean whether change of the content should cause postback. Return false if EnableVisualEdit is true.
+ */
+ public function getAutoPostBack()
+ {
+ return $this->getEnableVisualEdit() ? false : parent::getAutoPostBack();
+ }
+
+ /**
+ * @return boolean whether to show WYSIWYG text editor. Defaults to true.
+ */
+ public function getEnableVisualEdit()
+ {
+ return $this->getViewState('EnableVisualEdit',true);
+ }
+
+ /**
+ * Sets whether to show WYSIWYG text editor.
+ * @param boolean whether to show WYSIWYG text editor
+ */
+ public function setEnableVisualEdit($value)
+ {
+ $this->setViewState('EnableVisualEdit',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 html area
+ * @param string a culture string, e.g. en_AU.
+ */
+ public function setCulture($value)
+ {
+ $this->setViewState('Culture', $value, '');
+ }
+
+ /**
+ * Gets the list of options for the WYSIWYG (TinyMCE) editor
+ * @see http://tinymce.moxiecode.com/tinymce/docs/index.html
+ * @return string options
+ */
+ public function getOptions()
+ {
+ return $this->getViewState('Options', '');
+ }
+
+ /**
+ * Sets the list of options for the WYSIWYG (TinyMCE) editor
+ * @see http://tinymce.moxiecode.com/tinymce/docs/index.html
+ * @param string options
+ */
+ public function setOptions($value)
+ {
+ $this->setViewState('Options', $value, '');
+ }
+
+ /**
+ * @param string path to custom plugins to be copied.
+ */
+ public function setCustomPluginPath($value)
+ {
+ $this->setViewState('CustomPluginPath', $value);
+ }
+
+ /**
+ * @return string path to custom plugins to be copied.
+ */
+ public function getCustomPluginPath()
+ {
+ return $this->getViewState('CustomPluginPath');
+ }
+
+ /**
+ * @return boolean enable compression of the javascript files, default is true.
+ */
+ public function getEnableCompression()
+ {
+ return $this->getViewState('EnableCompression', true);
+ }
+
+ /**
+ * @param boolean enable compression of the javascript files, default is true.
+ */
+ public function setEnableCompression($value)
+ {
+ $this->setViewState('EnableCompression', TPropertyValue::ensureBoolean($value));
+ }
+
+ /**
+ * Adds attribute name-value pairs to renderer.
+ * This method overrides the parent implementation by registering
+ * additional javacript code.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function addAttributesToRender($writer)
+ {
+ if($this->getEnableVisualEdit() && $this->getEnabled(true))
+ {
+ $writer->addAttribute('id',$this->getClientID());
+ $this->registerEditorClientScript($writer);
+ }
+
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
+ * Returns a list of plugins to be loaded.
+ * Override this method to customize.
+ * @return array list of plugins to be loaded
+ */
+ public function getAvailablePlugins()
+ {
+ return self::$_plugins;
+ }
+
+ /**
+ * @return array list of available themese
+ */
+ public function getAvailableThemes()
+ {
+ return self::$_themes;
+ }
+
+ protected function getCompressionOptions()
+ {
+ return array(
+ 'plugins' => implode(',', $this->getAvailablePlugins()),
+ 'themes' => implode(',', $this->getAvailableThemes()),
+ 'languages' => $this->getLanguageSuffix($this->getCulture()),
+ 'disk_cache' => true,
+ 'debug' => false
+ );
+ }
+
+ protected function loadJavascriptLibrary()
+ {
+ $scripts = $this->getPage()->getClientScript();
+ $scripts->registerPradoScript('htmlarea');
+ $scripts->registerScriptFile('prado:THtmlArea', $this->getScriptUrl());
+ }
+
+ /**
+ * Registers the editor javascript file and code to initialize the editor.
+ */
+ protected function registerEditorClientScript($writer)
+ {
+ $this->loadJavascriptLibrary();
+ $scripts = $this->getPage()->getClientScript();
+ $options = array(
+ 'EditorOptions' => $this->getEditorOptions()
+ );
+ if($this->getEnableCompression())
+ $options['CompressionOptions'] = $this->getCompressionOptions();
+
+ $options = TJavaScript::encode($options,true,true);
+ $script = "new {$this->getClientClassName()}($options)";
+ $scripts->registerEndScript('prado:THtmlArea'.$this->ClientID,$script);
+ }
+
+ /**
+ * @return string editor script URL.
+ */
+ protected function getScriptUrl()
+ {
+ if($this->getEnableCompression())
+ return $this->getScriptDeploymentPath().'/tiny_mce/tiny_mce_gzip.js';
+ else
+ return $this->getScriptDeploymentPath().'/tiny_mce/tiny_mce.js';
+ }
+
+ /**
+ * Gets the editor script base URL by publishing the tarred source via TTarAssetManager.
+ * @return string URL base path to the published editor script
+ */
+ protected function getScriptDeploymentPath()
+ {
+ $tarfile = Prado::getPathOfNamespace('System.3rdParty.TinyMCE.tiny_mce', '.tar');
+ $md5sum = Prado::getPathOfNamespace('System.3rdParty.TinyMCE.tiny_mce', '.md5');
+ if($tarfile===null || $md5sum===null)
+ throw new TConfigurationException('htmlarea_tarfile_invalid');
+ $url = $this->getApplication()->getAssetManager()->publishTarFile($tarfile, $md5sum);
+ $this->copyCustomPlugins($url);
+ return $url;
+ }
+
+ protected function copyCustomPlugins($url)
+ {
+ if($plugins = $this->getCustomPluginPath())
+ {
+ $assets = $this->getApplication()->getAssetManager();
+ $path = is_dir($plugins) ? $plugins : Prado::getPathOfNameSpace($plugins);
+ $dest = $assets->getBasePath().'/'.basename($url).'/tiny_mce/plugins/';
+ if(!is_dir($dest) || $this->getApplication()->getMode()!==TApplicationMode::Performance)
+ $assets->copyDirectory($path, $dest);
+ }
+ }
+
+ /**
+ * Default editor options gives basic tool bar only.
+ * @return array editor initialization options.
+ */
+ protected function getEditorOptions()
+ {
+ $options['mode'] = 'exact';
+ $options['elements'] = $this->getClientID();
+ $options['language'] = $this->getLanguageSuffix($this->getCulture());
+ $options['theme'] = 'advanced';
+
+ //make it basic advanced to fit into 1 line of buttons.
+ //$options['theme_advanced_buttons1'] = 'bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright, justifyfull,separator,bullist,numlist,separator,undo,redo,separator,link,unlink,separator,charmap,separator,code,help';
+ //$options['theme_advanced_buttons2'] = ' ';
+ $options['theme_advanced_buttons1'] = 'formatselect,fontselect,fontsizeselect,separator,bold,italic,underline,strikethrough,sub,sup';
+ $options['theme_advanced_buttons2'] = 'justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,outdent,indent,separator,forecolor,backcolor,separator,hr,link,unlink,image,charmap,separator,removeformat,code,help';
+ $options['theme_advanced_buttons3'] = '';
+
+ $options['theme_advanced_toolbar_location'] = 'top';
+ $options['theme_advanced_toolbar_align'] = 'left';
+ $options['theme_advanced_path_location'] = 'bottom';
+ $options['extended_valid_elements'] = 'a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]';
+ if($this->getReadOnly())
+ $options['readonly'] = true;
+
+ $options = array_merge($options, $this->parseEditorOptions($this->getOptions()));
+ return $options;
+ }
+
+ /**
+ * Parse additional options set in the Options property.
+ * @return array additional custom options
+ */
+ protected function parseEditorOptions($string)
+ {
+ $options = array();
+ $substrings = preg_split('/,\s*\n|\n/', trim($string));
+ foreach($substrings as $bits)
+ {
+ $option = explode(":",$bits,2);
+
+ if(count($option) == 2)
+ {
+ $value=trim(trim($option[1]),"'\"");
+ if (($s=strtolower($value))==='false')
+ $value=false;
+ elseif ($s==='true')
+ $value=true;
+ $options[trim($option[0])] = $value;
+ }
+ }
+ return $options;
+ }
+
+ /**
+ * @return string localized editor interface language extension.
+ */
+ protected function getLanguageSuffix($culture)
+ {
+ $app = $this->getApplication()->getGlobalization();
+ if(empty($culture) && ($app!==null))
+ $culture = $app->getCulture();
+ $variants = array();
+ if($app!==null)
+ $variants = $app->getCultureVariants($culture);
+
+ foreach($variants as $variant)
+ {
+ if(isset(self::$_langs[$variant]))
+ return self::$_langs[$variant];
+ }
+
+ return 'en';
+ }
+
+ /**
+ * 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.THtmlArea';
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/THtmlArea4.php b/lib/prado/framework/Web/UI/WebControls/THtmlArea4.php
new file mode 100644
index 0000000..9485150
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THtmlArea4.php
@@ -0,0 +1,479 @@
+<?php
+/**
+ * THtmlArea4 class file.
+ *
+ * @author Fabio Bas <ctrlaltca[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TTextBox class
+ */
+Prado::using('System.Web.UI.WebControls.TTextBox');
+
+/**
+ * THtmlArea4 class
+ *
+ * THtmlArea4 wraps the visual editing functionalities provided by the
+ * version 4 of TinyMCE project {@link http://tinymce.com/}. It has been
+ * developed as a plug'n'play substitute for {@link THtmlArea}, that is
+ * based on the previous iteration (version 3) of the same project.
+ * Please note that both components can't be used together in the same page.
+ *
+ * THtmlArea displays a WYSIWYG text area on the Web page for user input
+ * in the HTML format. The text displayed in the THtmlArea component is
+ * specified or determined by using the <b>Text</b> property.
+ *
+ * To enable the visual editting on the client side, set the property
+ * <b>EnableVisualEdit</b> to true (which is default value).
+ * To set the size of the editor when the visual editting is enabled,
+ * set the <b>Width</b> and <b>Height</b> properties instead of
+ * <b>Columns</b> and <b>Rows</b> because the latter has no meaning
+ * under the situation.
+ *
+ * The default editor gives only the basic tool bar. To change or add
+ * additional tool bars, use the {@link setOptions Options} property to add additional
+ * editor options with each options on a new line.
+ * See http://www.tinymce.com/wiki.php/Configuration
+ * for a list of options. The options can be change/added as shown in the
+ * following example.
+ * <code>
+ * <com:THtmlArea>
+ * <prop:Options>
+ * language : "de"
+ * plugins: [ advlist anchor autolink autoresize autosave bbcode charmap code contextmenu directionality emoticons fullpage fullscreen hr image importcss insertdatetime layer legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor visualblocks visualchars wordcount ]
+ * toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media",
+ * statusbar: false
+ * </prop:Options>
+ * </com:THtmlArea>
+ * </code>
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class THtmlArea4 extends TTextBox
+{
+ /**
+ * @var array list of locale => language file pairs.
+ */
+ private static $_langs = array(
+ 'ar' => 'ar',
+ 'bg_BG' => 'bg_BG',
+ 'bs' => 'bs',
+ 'ca' => 'ca',
+ 'cs' => 'cs',
+ 'cy' => 'cy',
+ 'da' => 'da',
+ 'de' => 'de',
+ 'de_AT' => 'de_AT',
+ 'el' => 'el',
+ 'es' => 'es',
+ 'et' => 'et',
+ 'eu' => 'eu',
+ 'fa' => 'fa',
+ 'fi' => 'fi',
+ 'fo' => 'fo',
+ 'fr_FR' => 'fr_FR',
+ 'gl' => 'gl',
+ 'he_IL' => 'he_IL',
+ 'hr' => 'hr',
+ 'hu_HU' => 'hu_HU',
+ 'id' => 'id',
+ 'it' => 'it',
+ 'ja' => 'ja',
+ 'ka_GE' => 'ka_GE',
+ 'ko_KR' => 'ko_KR',
+ 'lb' => 'lb',
+ 'lt' => 'lt',
+ 'lv' => 'lv',
+ 'nb_NO' => 'nb_NO',
+ 'nl' => 'nl',
+ 'pl' => 'pl',
+ 'pt_BR' => 'pt_BR',
+ 'pt_PT' => 'pt_PT',
+ 'ro' => 'ro',
+ 'ru' => 'ru',
+ 'si_LK' => 'si_LK',
+ 'sk' => 'sk',
+ 'sl_SI' => 'sl_SI',
+ 'sr' => 'sr',
+ 'sv_SE' => 'sv_SE',
+ 'ta' => 'ta',
+ 'ta_IN' => 'ta_IN',
+ 'th_TH' => 'th_TH',
+ 'tr_TR' => 'tr_TR',
+ 'ug' => 'ug',
+ 'uk' => 'uk',
+ 'uk_UA' => 'uk_UA',
+ 'vi' => 'vi',
+ 'vi_VN' => 'vi_VN',
+ 'zh_CN' => 'zh_CN',
+ 'zh_TW' => 'zh_TW',
+ );
+
+ /**
+ * @var array list of default plugins to load, override using getAvailablePlugins();
+ */
+ private static $_plugins = array(
+ 'advlist',
+ 'anchor',
+ 'autolink',
+ 'autoresize',
+ 'autosave',
+ 'bbcode',
+ 'charmap',
+ 'code',
+ 'contextmenu',
+ 'directionality',
+ 'emoticons',
+ 'fullpage',
+ 'fullscreen',
+ 'hr',
+ 'image',
+ 'importcss',
+ 'insertdatetime',
+ 'layer',
+ 'legacyoutput',
+ 'link',
+ 'lists',
+ 'media',
+ 'nonbreaking',
+ 'noneditable',
+ 'pagebreak',
+ 'paste',
+ 'preview',
+ 'print',
+ 'save',
+ 'searchreplace',
+ 'spellchecker',
+ 'tabfocus',
+ 'table',
+ 'template',
+ 'textcolor',
+ 'visualblocks',
+ 'visualchars',
+ 'wordcount',
+ );
+
+ /**
+ * @var array default themes to load
+ */
+ private static $_themes = array(
+ 'modern',
+ );
+
+ /**
+ * Constructor.
+ * Sets default width and height.
+ */
+ public function __construct()
+ {
+ $this->setWidth('600px');
+ $this->setHeight('250px');
+ }
+
+ /**
+ * Overrides the parent implementation.
+ * TextMode for THtmlArea control is always 'MultiLine'
+ * @return string the behavior mode of the THtmlArea component.
+ */
+ public function getTextMode()
+ {
+ return 'MultiLine';
+ }
+
+ /**
+ * Overrides the parent implementation.
+ * TextMode for THtmlArea is always 'MultiLine' and cannot be changed to others.
+ * @param string the text mode
+ */
+ public function setTextMode($value)
+ {
+ throw new TInvalidOperationException("htmlarea_textmode_readonly");
+ }
+
+ /**
+ * @return boolean whether change of the content should cause postback. Return false if EnableVisualEdit is true.
+ */
+ public function getAutoPostBack()
+ {
+ return $this->getEnableVisualEdit() ? false : parent::getAutoPostBack();
+ }
+
+ /**
+ * @return boolean whether to show WYSIWYG text editor. Defaults to true.
+ */
+ public function getEnableVisualEdit()
+ {
+ return $this->getViewState('EnableVisualEdit',true);
+ }
+
+ /**
+ * Sets whether to show WYSIWYG text editor.
+ * @param boolean whether to show WYSIWYG text editor
+ */
+ public function setEnableVisualEdit($value)
+ {
+ $this->setViewState('EnableVisualEdit',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * Gets the current culture.
+ * @return string current culture, e.g. de_AT.
+ */
+ public function getCulture()
+ {
+ return $this->getViewState('Culture', '');
+ }
+
+ /**
+ * Sets the culture/language for the html area
+ * @param string a culture string, e.g. de_AT.
+ */
+ public function setCulture($value)
+ {
+ $this->setViewState('Culture', $value, '');
+ }
+
+ /**
+ * Gets the list of options for the WYSIWYG (TinyMCE) editor
+ * @see http://www.tinymce.com/wiki.php/Configuration
+ * @return string options
+ */
+ public function getOptions()
+ {
+ return $this->getViewState('Options', '');
+ }
+
+ /**
+ * Sets the list of options for the WYSIWYG (TinyMCE) editor
+ * @see http://www.tinymce.com/wiki.php/Configuration
+ * @param string options
+ */
+ public function setOptions($value)
+ {
+ $this->setViewState('Options', $value, '');
+ }
+
+ /**
+ * @param string path to custom plugins to be copied.
+ */
+ public function setCustomPluginPath($value)
+ {
+ $this->setViewState('CustomPluginPath', $value);
+ }
+
+ /**
+ * @return string path to custom plugins to be copied.
+ */
+ public function getCustomPluginPath()
+ {
+ return $this->getViewState('CustomPluginPath');
+ }
+
+ /**
+ * @return boolean enable compression of the javascript files, default is true.
+ * @deprecated since 3.2.3: tinyMCE 4 doesn't support this anymore
+ */
+ public function getEnableCompression()
+ {
+ return $this->getViewState('EnableCompression', true);
+ }
+
+ /**
+ * @param boolean enable compression of the javascript files, default is true.
+ * @deprecated since 3.2.3: tinyMCE 4 doesn't support this anymore
+ */
+ public function setEnableCompression($value)
+ {
+ $this->setViewState('EnableCompression', TPropertyValue::ensureBoolean($value));
+ }
+
+ /**
+ * Adds attribute name-value pairs to renderer.
+ * This method overrides the parent implementation by registering
+ * additional javacript code.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function addAttributesToRender($writer)
+ {
+ if($this->getEnableVisualEdit() && $this->getEnabled(true))
+ {
+ $writer->addAttribute('id',$this->getClientID());
+ $this->registerEditorClientScript($writer);
+ }
+
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
+ * Returns a list of plugins to be loaded.
+ * Override this method to customize.
+ * @return array list of plugins to be loaded
+ */
+ public function getAvailablePlugins()
+ {
+ return self::$_plugins;
+ }
+
+ /**
+ * @return array list of available themese
+ */
+ public function getAvailableThemes()
+ {
+ return self::$_themes;
+ }
+
+ /**
+ * @deprecated since 3.2.3. tinyMCE4 doesn's use this anymore
+ */
+ protected function getCompressionOptions()
+ {
+ return array();
+ }
+
+ protected function loadJavascriptLibrary()
+ {
+ $scripts = $this->getPage()->getClientScript();
+ $scripts->registerPradoScript('htmlarea4');
+ $scripts->registerScriptFile('prado:THtmlArea4', $this->getScriptUrl());
+ }
+
+ /**
+ * Registers the editor javascript file and code to initialize the editor.
+ */
+ protected function registerEditorClientScript($writer)
+ {
+ $this->loadJavascriptLibrary();
+ $scripts = $this->getPage()->getClientScript();
+ $options = array(
+ 'EditorOptions' => $this->getEditorOptions()
+ );
+
+ $options = TJavaScript::encode($options,true,true);
+ $script = "new {$this->getClientClassName()}($options)";
+ $scripts->registerEndScript('prado:THtmlArea4'.$this->ClientID,$script);
+ }
+
+ /**
+ * @return string editor script URL.
+ */
+ protected function getScriptUrl()
+ {
+ return $this->getScriptDeploymentPath().'/tinymce.min.js';
+ }
+
+ /**
+ * Gets the editor script base URL by publishing the tarred source via TTarAssetManager.
+ * @return string URL base path to the published editor script
+ */
+ protected function getScriptDeploymentPath()
+ {
+ $basedir = Prado::getPathOfNamespace('System.Web.Javascripts.source.tinymce-405');
+ $url = $this->getApplication()->getAssetManager()->publishFilePath($basedir);
+ $this->copyCustomPlugins($url);
+ return $url;
+ }
+
+ protected function copyCustomPlugins($url)
+ {
+ if($plugins = $this->getCustomPluginPath())
+ {
+ $assets = $this->getApplication()->getAssetManager();
+ $path = is_dir($plugins) ? $plugins : Prado::getPathOfNameSpace($plugins);
+ $dest = $assets->getBasePath().'/'.basename($url).'/plugins/';
+ if(!is_dir($dest) || $this->getApplication()->getMode()!==TApplicationMode::Performance)
+ $assets->copyDirectory($path, $dest);
+ }
+ }
+
+ /**
+ * Default editor options gives basic tool bar only.
+ * @return array editor initialization options.
+ */
+ protected function getEditorOptions()
+ {
+ $options['mode'] = 'exact';
+ $options['elements'] = $this->getClientID();
+ $options['language'] = $this->getLanguageSuffix($this->getCulture());
+ //$options['theme'] = 'modern'; //default
+ // mimic previous (tinyMCE3) sizing behaviour
+ $options['width'] = $this->getWidth();
+ $options['height'] = $this->getHeight();
+ $options['resize'] = 'both';
+ $options['menubar'] = false;
+ if($this->getReadOnly())
+ {
+ $options['readonly'] = true;
+ $options['toolbar'] = false;
+ $options['menubar'] = false;
+ $options['statusbar'] = false;
+ }
+
+ $options['extended_valid_elements'] = 'a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]';
+
+ $options = array_merge($options, $this->parseEditorOptions($this->getOptions()));
+ return $options;
+ }
+
+ /**
+ * Parse additional options set in the Options property.
+ * @return array additional custom options
+ */
+ protected function parseEditorOptions($string)
+ {
+ $options = array();
+ $substrings = preg_split('/,\s*\n|\n/', trim($string));
+ foreach($substrings as $bits)
+ {
+ $option = explode(":",$bits,2);
+
+ if(count($option) == 2)
+ {
+ $value=trim(trim($option[1]),"'\"");
+ if (($s=strtolower($value))==='false')
+ $value=false;
+ elseif ($s==='true')
+ $value=true;
+ $options[trim($option[0])] = $value;
+ }
+ }
+ return $options;
+ }
+
+ /**
+ * @return string localized editor interface language extension.
+ */
+ protected function getLanguageSuffix($culture)
+ {
+ $app = $this->getApplication()->getGlobalization();
+ if(empty($culture) && ($app!==null))
+ $culture = $app->getCulture();
+ $variants = array();
+ if($app!==null)
+ $variants = $app->getCultureVariants($culture);
+
+ foreach($variants as $variant)
+ {
+ if(isset(self::$_langs[$variant]))
+ return self::$_langs[$variant];
+ }
+
+ return 'en';
+ }
+
+ /**
+ * 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.THtmlArea4';
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/THtmlElement.php b/lib/prado/framework/Web/UI/WebControls/THtmlElement.php
new file mode 100644
index 0000000..6efd608
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THtmlElement.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * THtmlElement class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/THyperLink.php b/lib/prado/framework/Web/UI/WebControls/THyperLink.php
new file mode 100644
index 0000000..8e8d3f5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THyperLink.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * THyperLink class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.xisc.com/
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @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,
+ * the alignment of the image displayed is set by the
+ * {@link setImageAlign ImageAlign} property and {@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 <qiang.xue@gmail.com>
+ * @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);
+ if(($align=$this->getImageAlign())!=='')
+ $image->setImageAlign($align);
+ $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 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 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/lib/prado/framework/Web/UI/WebControls/THyperLinkColumn.php b/lib/prado/framework/Web/UI/WebControls/THyperLinkColumn.php
new file mode 100644
index 0000000..d24616a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/THyperLinkColumn.php
@@ -0,0 +1,271 @@
+<?php
+/**
+ * THyperLinkColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * $datagridItem->HyperLinkColumnID->HyperLink
+ * $datagridItem->HyperLinkColumnID->Controls[0]
+ * </code>
+ * The second method is possible because the hyperlink control created within the
+ * datagrid cell is the first child.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TImage.php b/lib/prado/framework/Web/UI/WebControls/TImage.php
new file mode 100644
index 0000000..8538545
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TImage.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * TImage class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TImageButton.php b/lib/prado/framework/Web/UI/WebControls/TImageButton.php
new file mode 100644
index 0000000..288def3
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TImageButton.php
@@ -0,0 +1,438 @@
+<?php
+/**
+ * TImageButton class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>submit</b> button or a <b>command</b> button.
+ *
+ * A <b>command</b> 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 <b>submit</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TImageMap.php b/lib/prado/framework/Web/UI/WebControls/TImageMap.php
new file mode 100644
index 0000000..8775496
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TImageMap.php
@@ -0,0 +1,828 @@
+<?php
+/**
+ * TImageMap and related class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * <com:TImageMap>
+ * <com:TCircleHotSpot ... />
+ * <com:TRectangleHotSpot ... />
+ * <com:TPolygonHotSpot ... />
+ * </com:TImageMap>
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <b>OnClick</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TInlineFrame.php b/lib/prado/framework/Web/UI/WebControls/TInlineFrame.php
new file mode 100644
index 0000000..3d18235
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TInlineFrame.php
@@ -0,0 +1,314 @@
+<?php
+/**
+ * TInlineFrame class file.
+ *
+ * @author Jason Ragsdale <jrags@jasrags.net>
+ * @author Harry Pottash <hpottash@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TInlineFrame class
+ *
+ * TInlineFrame displays an inline frame (iframe) on a Web page.
+ * The location of the frame content is specified by {@link setFrameUrl FrameUrl}.
+ * The frame's alignment is specified by {@link setAlign Align}.
+ * The {@link setMarginWidth MarginWidth} and {@link setMarginHeight MarginHeight}
+ * properties define the number of pixels to use as the left/right margins and
+ * top/bottom margins, respectively, within the inline frame.
+ * The {@link setScrollBars ScrollBars} property specifies whether scrollbars are
+ * provided for the inline frame. And {@link setDescriptionUrl DescriptionUrl}
+ * gives the URI of a long description of the frame's contents.
+ *
+ * Original Prado v2 IFrame Author Information
+ * @author Jason Ragsdale <jrags@jasrags.net>
+ * @author Harry Pottash <hpottash@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TInlineFrame extends TWebControl implements IDataRenderer
+{
+ /**
+ * @return string tag name of the iframe.
+ */
+ protected function getTagName()
+ {
+ return 'iframe';
+ }
+
+ /**
+ * @return TInlineFrameAlign alignment of the iframe. Defaults to TInlineFrameAlign::NotSet.
+ */
+ public function getAlign()
+ {
+ return $this->getViewState('Align',TInlineFrameAlign::NotSet);
+ }
+
+ /**
+ * @param TInlineFrameAlign alignment of the iframe.
+ */
+ public function setAlign($value)
+ {
+ $this->setViewState('Align',TPropertyValue::ensureEnum($value,'TInlineFrameAlign'),TInlineFrameAlign::NotSet);
+ }
+
+ /**
+ * @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,'');
+ }
+
+ /**
+ * @return boolean whether there should be a visual separator between the frames. Defaults to true.
+ */
+ public function getShowBorder()
+ {
+ return $this->getViewState('ShowBorder',true);
+ }
+
+ /**
+ * @param boolean whether there should be a visual separator between the frames.
+ */
+ public function setShowBorder($value)
+ {
+ $this->setViewState('ShowBorder',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @return string URL that this iframe will load content from. Defaults to ''.
+ */
+ public function getFrameUrl()
+ {
+ return $this->getViewState('FrameUrl','');
+ }
+
+ /**
+ * @param string URL that this iframe will load content from.
+ */
+ public function setFrameUrl($value)
+ {
+ $this->setViewState('FrameUrl',$value,'');
+ }
+
+ /**
+ * Returns the URL that this iframe will load content from
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link getFrameUrl()}.
+ * @return string the URL that this iframe will load content from
+ * @see getFrameUrl
+ * @since 3.1.0
+ */
+ public function getData()
+ {
+ return $this->getFrameUrl();
+ }
+
+ /**
+ * Sets the URL that this iframe will load content from.
+ * This method is required by {@link IDataRenderer}.
+ * It is the same as {@link setFrameUrl()}.
+ * @param string the URL that this iframe will load content from
+ * @see setFrameUrl
+ * @since 3.1.0
+ */
+ public function setData($value)
+ {
+ $this->setFrameUrl($value);
+ }
+
+ /**
+ * @return TInlineFrameScrollBars the visibility and position of scroll bars in an iframe. Defaults to TInlineFrameScrollBars::Auto.
+ */
+ public function getScrollBars()
+ {
+ return $this->getViewState('ScrollBars',TInlineFrameScrollBars::Auto);
+ }
+
+ /**
+ * @param TInlineFrameScrollBars the visibility and position of scroll bars in an iframe.
+ */
+ public function setScrollBars($value)
+ {
+ $this->setViewState('ScrollBars',TPropertyValue::ensureEnum($value,'TInlineFrameScrollBars'),TInlineFrameScrollBars::Auto);
+ }
+
+ /**
+ * @return integer the width of the control
+ */
+ public function getWidth()
+ {
+ return $this->getViewState('Width',-1);
+ }
+
+ /**
+ * @param integer the width of the control
+ */
+ public function setWidth($value)
+ {
+ if(($value=TPropertyValue::ensureInteger($value))<0)
+ $value=-1;
+ $this->setViewState('Width',$value,-1);
+ }
+
+ /**
+ * @return integer the height of the control
+ */
+ public function getHeight()
+ {
+ return $this->getViewState('Height',-1);
+ }
+
+ /**
+ * @param integer the height of the control
+ */
+ public function setHeight($value)
+ {
+ if(($value=TPropertyValue::ensureInteger($value))<0)
+ $value=-1;
+ $this->setViewState('Height',$value,-1);
+ }
+
+ /**
+ * @return integer the amount of space, in pixels, that should be left between
+ * the frame's contents and the left and right margins. Defaults to -1, meaning not set.
+ */
+ public function getMarginWidth()
+ {
+ return $this->getViewState('MarginWidth',-1);
+ }
+
+ /**
+ * @param integer the amount of space, in pixels, that should be left between
+ * the frame's contents and the left and right margins.
+ */
+ public function setMarginWidth($value)
+ {
+ if(($value=TPropertyValue::ensureInteger($value))<0)
+ $value=-1;
+ $this->setViewState('MarginWidth',$value,-1);
+ }
+
+ /**
+ * @return integer the amount of space, in pixels, that should be left between
+ * the frame's contents and the top and bottom margins. Defaults to -1, meaning not set.
+ */
+ public function getMarginHeight()
+ {
+ return $this->getViewState('MarginHeight',-1);
+ }
+
+ /**
+ * @param integer the amount of space, in pixels, that should be left between
+ * the frame's contents and the top and bottom margins.
+ */
+ public function setMarginHeight($value)
+ {
+ if(($value=TPropertyValue::ensureInteger($value))<0)
+ $value=-1;
+ $this->setViewState('MarginHeight',$value,-1);
+ }
+
+ /**
+ * 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)
+ {
+ if($this->getID()!=='')
+ $writer->addAttribute('name',$this->getUniqueID());
+
+ if(($src=$this->getFrameUrl())!=='')
+ $writer->addAttribute('src',$src);
+
+ if(($align=strtolower($this->getAlign()))!=='notset')
+ $writer->addAttribute('align',$align);
+
+ $scrollBars=$this->getScrollBars();
+ if($scrollBars===TInlineFrameScrollBars::None)
+ $writer->addAttribute('scrolling','no');
+ else if($scrollBars===TInlineFrameScrollBars::Both)
+ $writer->addAttribute('scrolling','yes');
+
+ if (!$this->getShowBorder())
+ $writer->addAttribute('frameborder','0');
+
+ if(($longdesc=$this->getDescriptionUrl())!=='')
+ $writer->addAttribute('longdesc',$longdesc);
+
+ if (($width=$this->getWidth())!==-1)
+ $writer->addAttribute('width',$width);
+
+ if (($height=$this->getHeight())!==-1)
+ $writer->addAttribute('height',$height);
+
+ if(($marginheight=$this->getMarginHeight())!==-1)
+ $writer->addAttribute('marginheight',$marginheight);
+
+ if(($marginwidth=$this->getMarginWidth())!==-1)
+ $writer->addAttribute('marginwidth',$marginwidth);
+
+ parent::addAttributesToRender($writer);
+ }
+}
+
+/**
+ * TInlineFrameAlign class.
+ * TInlineFrameAlign defines the enumerable type for the possible alignments
+ * that the content in a {@link TInlineFrame} could be.
+ *
+ * The following enumerable values are defined:
+ * - NotSet: the alignment is not specified.
+ * - Left: left aligned
+ * - Right: right aligned
+ * - Top: top aligned
+ * - Middle: middle aligned
+ * - Bottom: bottom aligned
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TInlineFrameAlign extends TEnumerable
+{
+ const NotSet='NotSet';
+ const Left='Left';
+ const Right='Right';
+ const Top='Top';
+ const Middle='Middle';
+ const Bottom='Bottom';
+}
+
+/**
+ * TInlineFrameScrollBars class.
+ * TInlineFrameScrollBars defines the enumerable type for the possible scroll bar mode
+ * that a {@link TInlineFrame} 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.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TInlineFrameScrollBars extends TEnumerable
+{
+ const None='None';
+ const Auto='Auto';
+ const Both='Both';
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TItemDataRenderer.php b/lib/prado/framework/Web/UI/WebControls/TItemDataRenderer.php
new file mode 100644
index 0000000..1800fa7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TItemDataRenderer.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * TItemDataRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TJavascriptLogger.php b/lib/prado/framework/Web/UI/WebControls/TJavascriptLogger.php
new file mode 100644
index 0000000..83f9fcb
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TJavascriptLogger.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * TJavascriptLogger class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TJavascriptLogger class.
+ *
+ * Provides logging for client-side javascript. Example: template code
+ * <code><com:TJavascriptLogger /></code>
+ *
+ * Client-side javascript code to log info, error, warn, debug
+ * <code>Logger.warn('A warning');
+ * Logger.info('something happend');
+ * </code>
+ *
+ * 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<weizhuo[at]gmail[dot]com>
+ * @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; jQuery(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 = '(<a href="http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/" target="_blank">more info</a>).';
+ $link = '<a href="javascript:if(logConsole)logConsole.toggle()">toggle the javascript log console.</a>';
+ $usage = 'Press ALT-'.$code.' (Or CTRL-'.$code.' on OS X) to';
+ $writer->write("{$usage} {$link} {$info}");
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TKeyboard.php b/lib/prado/framework/Web/UI/WebControls/TKeyboard.php
new file mode 100644
index 0000000..15a4aa3
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TKeyboard.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * TKeyboard class file.
+ *
+ * @author Sergey Morkovkin <sergeymorkovkin@mail.ru> and Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * <com:TTextBox ID="PasswordInput" />
+ * <com:TKeyboard ForControl="PasswordInput" />
+ * </code>
+ *
+ * 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 <sergeymorkovkin@mail.ru> and Qiang Xue <qiang.xue@gmail.com>
+ * @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 <div> element. Defaults to 'Keyboard'.
+ */
+ public function getKeyboardCssClass()
+ {
+ return $this->getViewState('KeyboardCssClass', 'Keyboard');
+ }
+
+ /**
+ * Sets a value indicating the CSS class name for the keyboard <div> 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 <div> 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/lib/prado/framework/Web/UI/WebControls/TLabel.php b/lib/prado/framework/Web/UI/WebControls/TLabel.php
new file mode 100644
index 0000000..bf43b4d
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TLabel.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * TLabel class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TLinkButton.php b/lib/prado/framework/Web/UI/WebControls/TLinkButton.php
new file mode 100644
index 0000000..dadea1d
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TLinkButton.php
@@ -0,0 +1,332 @@
+<?php
+/**
+ * TLinkButton class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>submit</b> button or a <b>command</b> button.
+ *
+ * A <b>command</b> 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 <b>submit</b> 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 &lt;img&gt; tag as the body of TLinkButton.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TListBox.php b/lib/prado/framework/Web/UI/WebControls/TListBox.php
new file mode 100644
index 0000000..520fc5e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TListBox.php
@@ -0,0 +1,259 @@
+<?php
+/**
+ * TListBox class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * $listitem->Attributes->Group="Group Name";
+ * // or <com:TListItem Attributes.Group="Group Name" .../> in template
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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();
+ }
+
+ /**
+ * 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);
+ }
+}
+
+
+/**
+ * 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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TListSelectionMode extends TEnumerable
+{
+ const Single='Single';
+ const Multiple='Multiple';
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TListControl.php b/lib/prado/framework/Web/UI/WebControls/TListControl.php
new file mode 100644
index 0000000..ff89a74
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TListControl.php
@@ -0,0 +1,923 @@
+<?php
+
+/**
+ * TListControl class file
+ *
+ * @author Robin J. Rogge <rojaro@gmail.com>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * <com:TListControl>
+ * <com:TListItem Value="xxx" Text="yyy" >
+ * <com:TListItem Value="xxx" Text="yyy" Selected="true" >
+ * <com:TListItem Value="xxx" Text="yyy" >
+ * </com:TListControl>
+ * </code>
+ *
+ * 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,
+ * <code>
+ * $dataSource=array(
+ * array('name'=>'John', 'age'=>31),
+ * array('name'=>'Cary', 'age'=>28),
+ * array('name'=>'Rose', 'age'=>35),
+ * );
+ * </code>
+ * 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.
+ *
+ * The {@link setPromptText PromptText} and {@link setPromptValue PromptValue} properties can
+ * be used to add a dummy list item that will be rendered first.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <weizhuo[at]gmail[dot]com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TListControlValidator.php b/lib/prado/framework/Web/UI/WebControls/TListControlValidator.php
new file mode 100644
index 0000000..76096ef
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TListControlValidator.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * TListControlValidator class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>TListControl that allows multiple selection</b>.
+ *
+ * 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
+ * <code>
+ * <com:TListBox ID="listbox" SelectionMode="Multiple">
+ * <com:TListItem Text="item1" Value="value1" />
+ * <com:TListItem Text="item2" Value="value2" />
+ * <com:TListItem Text="item3" Value="value3" />
+ * </com:TListBox>
+ *
+ * <com:TListControlValidator
+ * ControlToValidate="listbox"
+ * MinSelection="2"
+ * ErrorMessage="Please select at least 2" />
+ * </code>
+ * - "value1" must be selected <b>and</b> at least 1 other
+ * <code>
+ * <com:TCheckBoxList ID="checkboxes">
+ * <com:TListItem Text="item1" Value="value1" />
+ * <com:TListItem Text="item2" Value="value2" />
+ * <com:TListItem Text="item3" Value="value3" />
+ * </com:TCheckBoxList>
+ *
+ * <com:TListControlValidator
+ * ControlToValidate="checkboxes"
+ * RequiredSelections="value1"
+ * MinSelection="2"
+ * ErrorMessage="Please select 'item1' and at least 1 other" />
+ * </code>
+ *
+ * @author Xiang Wei Zhuo <weizhuo[at]gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TListItem.php b/lib/prado/framework/Web/UI/WebControls/TListItem.php
new file mode 100644
index 0000000..aec006d
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TListItem.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * TListItem class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TLiteral.php b/lib/prado/framework/Web/UI/WebControls/TLiteral.php
new file mode 100644
index 0000000..f2306b4
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TLiteral.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * TLiteral class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TLiteralColumn.php b/lib/prado/framework/Web/UI/WebControls/TLiteralColumn.php
new file mode 100644
index 0000000..3cf3c3a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TLiteralColumn.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * TLiteralColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 {
+ $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/lib/prado/framework/Web/UI/WebControls/TMarkdown.php b/lib/prado/framework/Web/UI/WebControls/TMarkdown.php
new file mode 100644
index 0000000..7a99b1f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TMarkdown.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * TMarkdown class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TTextHighlighter and MarkdownParser classes
+ */
+Prado::using('System.Web.UI.WebControls.TTextHighlighter');
+Prado::using('System.3rdParty.Parsedown.Parsedown');
+
+/**
+ * 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 https://daringfireball.net/projects/markdown/basics 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 <weizhuo[at]gmail[dot]com>
+ * @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)
+ {
+ $result = Parsedown::instance()->parse($text);
+ return preg_replace_callback(
+ '/<pre><code class="language-(\w+)">((.|\n)*?)<\\/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/lib/prado/framework/Web/UI/WebControls/TMultiView.php b/lib/prado/framework/Web/UI/WebControls/TMultiView.php
new file mode 100644
index 0000000..5f338d7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TMultiView.php
@@ -0,0 +1,374 @@
+<?php
+/**
+ * TMultiView and TView class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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()<TControl::CS_CHILD_INITIALIZED)
+ $this->_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 <b>OnActiveViewChanged</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TView extends TControl
+{
+ private $_active=false;
+
+ /**
+ * Raises <b>OnActivate</b> event.
+ * @param TEventParameter event parameter
+ */
+ public function onActivate($param)
+ {
+ $this->raiseEvent('OnActivate',$this,$param);
+ }
+
+ /**
+ * Raises <b>OnDeactivate</b> 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/lib/prado/framework/Web/UI/WebControls/TOutputCache.php b/lib/prado/framework/Web/UI/WebControls/TOutputCache.php
new file mode 100644
index 0000000..5615418
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TOutputCache.php
@@ -0,0 +1,616 @@
+<?php
+/**
+ * TOutputCache class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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.,
+ * <code>
+ * <com:TOutputCache>
+ * content to be cached
+ * </com:TOutputCache>
+ * </code>
+ * 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 <qiang.xue@gmail.com>
+ * @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
+ * <b>OnCheckDependency</b> event of {@link TOutputCache} control.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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
+ * <b>OnCalculateKey</b> event of {@link TOutputCache} control.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <gabor.berczi@devworx.hu>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TPager.php b/lib/prado/framework/Web/UI/WebControls/TPager.php
new file mode 100644
index 0000000..467c0f7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TPager.php
@@ -0,0 +1,810 @@
+<?php
+/**
+ * TPager class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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.)
+ *
+ * Since Prado 3.2.1, you can use the {@link setButtonCssClass ButtonCssClass} property to specify a css class
+ * that will be applied to each button created by the pager in NextPrev or Numeric mode.
+ *
+ * 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 <qiang.xue@gmail.com>
+ * @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 string the css class of the buttons.
+ * @since 3.2.1
+ */
+ public function getButtonCssClass()
+ {
+ return $this->getViewState('ButtonCssClass','');
+ }
+
+ /**
+ * @param Sets the css class of the buttons that will be rendered by this pager.
+ * @since 3.2.1
+ */
+ public function setButtonCssClass($value)
+ {
+ $this->setViewState('ButtonCssClass',TPropertyValue::ensureString($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);
+ $button->setCssClass($this->getButtonCssClass());
+ 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);
+ $button->setCssClass($this->getButtonCssClass());
+ 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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> event.
+ * @param integer new page index
+ */
+ public function __construct($source,$newPageIndex)
+ {
+ $this->_source=$source;
+ $this->_newIndex=$newPageIndex;
+ }
+
+ /**
+ * @return TControl the control originally raises the <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TPanel.php b/lib/prado/framework/Web/UI/WebControls/TPanel.php
new file mode 100644
index 0000000..22d44a5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TPanel.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * TPanel class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 &lt;div&gt; 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 <qiang.xue@gmail.com>
+ * @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())!=='')
+ {
+ if(($button=$this->findControl($butt))===null)
+ throw new TInvalidDataValueException('panel_defaultbutton_invalid',$butt);
+ else
+ $this->getPage()->getClientScript()->registerDefaultButton($this, $button);
+ }
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TPanelStyle.php b/lib/prado/framework/Web/UI/WebControls/TPanelStyle.php
new file mode 100644
index 0000000..2e0f1a2
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TPanelStyle.php
@@ -0,0 +1,274 @@
+<?php
+/**
+ * TPanelStyle class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TPlaceHolder.php b/lib/prado/framework/Web/UI/WebControls/TPlaceHolder.php
new file mode 100644
index 0000000..65f5ba2
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TPlaceHolder.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * TPlaceHolder class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TPlaceHolder extends TControl
+{
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TRadioButton.php b/lib/prado/framework/Web/UI/WebControls/TRadioButton.php
new file mode 100644
index 0000000..cfdb795
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRadioButton.php
@@ -0,0 +1,304 @@
+<?php
+/**
+ * TRadioButton class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TCheckBox parent class
+ */
+Prado::using('System.Web.UI.WebControls.TCheckBox');
+
+/**
+ * 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 <qiang.xue@gmail.com>
+ * @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;
+ }
+
+ /**
+ * @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(is_a($this->getNamingContainer(), '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/lib/prado/framework/Web/UI/WebControls/TRadioButtonList.php b/lib/prado/framework/Web/UI/WebControls/TRadioButtonList.php
new file mode 100644
index 0000000..dc9e2da
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRadioButtonList.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * TRadioButtonList class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TRadioButton class
+ */
+Prado::using('System.Web.UI.WebControls.TRadioButton');
+/**
+ * Includes TCheckBoxList class
+ */
+Prado::using('System.Web.UI.WebControls.TCheckBoxList');
+
+/**
+ * TRadioButtonList class
+ *
+ * TRadioButtonList displays a list of radiobuttons on a Web page.
+ *
+ * TRadioButtonList inherits all properties and events of {@link TCheckBoxList}.
+ * Each TRadioButtonList displays one group of radiobuttons, i.e., at most
+ * one radiobutton can be selected at a time.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TRadioButtonList extends TCheckBoxList
+{
+ /**
+ * @return boolean whether this control supports multiple selection. Always false for radiobutton list.
+ */
+ protected function getIsMultiSelect()
+ {
+ return false;
+ }
+
+ /**
+ * Creates a control used for repetition (used as a template).
+ * @return TControl the control to be repeated
+ */
+ protected function createRepeatedControl()
+ {
+ return new TRadioButtonItem;
+ }
+
+ /**
+ * 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)
+ {
+ $value=isset($values[$key])?$values[$key]:'';
+ $oldSelection=$this->getSelectedIndex();
+ $this->ensureDataBound();
+ foreach($this->getItems() as $index=>$item)
+ {
+ if($item->getEnabled() && $item->getValue()===$value)
+ {
+ if($index===$oldSelection)
+ return false;
+ else
+ {
+ $this->setSelectedIndex($index);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @throws TNotSupportedException if this method is invoked
+ */
+ public function setSelectedIndices($indices)
+ {
+ throw new TNotSupportedException('radiobuttonlist_selectedindices_unsupported');
+ }
+
+ /**
+ * 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.TRadioButtonList';
+ }
+}
+
+class TRadioButtonItem extends TRadioButton {
+ /**
+ * Override client implementation to avoid emitting the javascript
+ */
+ protected function renderClientControlScript($writer)
+ {
+ }
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TRangeValidator.php b/lib/prado/framework/Web/UI/WebControls/TRangeValidator.php
new file mode 100644
index 0000000..a6668f4
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRangeValidator.php
@@ -0,0 +1,357 @@
+<?php
+/**
+ * TRangeValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * - <b>Integer</b> A 32-bit signed integer data type.
+ * - <b>Float</b> A double-precision floating point number data type.
+ * - <b>Date</b> 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.
+ * - <b>String</b> A string data type.
+ * - <b>StringLength</b> 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 <qiang.xue@gmail.com>
+ * @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);
+ 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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TRangeValidationDataType extends TValidationDataType
+{
+ const StringLength='StringLength';
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TRatingList.php b/lib/prado/framework/Web/UI/WebControls/TRatingList.php
new file mode 100644
index 0000000..0f1f364
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRatingList.php
@@ -0,0 +1,359 @@
+<?php
+/**
+ * TRatingList class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TRadioButtonList class
+ */
+Prado::using('System.Web.UI.WebControls.TRadioButtonList');
+
+/**
+ * TRatingList class.
+ *
+ * This class is EXPERIMENTAL.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @author Bradley Booms <bradley[dot]booms[at]gmail[dot]com>
+ * @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->getPage()->findControl($id))
+ return $control;
+ if($control=$this->getNamingContainer()->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/lib/prado/framework/Web/UI/WebControls/TReCaptcha.php b/lib/prado/framework/Web/UI/WebControls/TReCaptcha.php
new file mode 100644
index 0000000..89053d1
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TReCaptcha.php
@@ -0,0 +1,271 @@
+<?php
+
+/**
+ * TReCaptcha class file
+ *
+ * @author Bérczi Gábor <gabor.berczi@devworx.hu>
+ * @link http://www.devworx.hu/
+ * @copyright Copyright &copy; 2011 DevWorx
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <code>
+ * <com:TReCaptcha ID="Captcha"
+ * PublicKey="..."
+ * PrivateKey="..."
+ * />
+ * <com:TReCaptchaValidator ControlToValidate="Captcha"
+ * ErrorMessage="You are challenged!" />
+ * </code>
+ *
+ * @author Bérczi Gábor <gabor.berczi@devworx.hu>
+ * @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));
+ }
+
+ public function getCallbackScript()
+ {
+ return $this->getViewState('CallbackScript');
+ }
+
+ public function setCallbackScript($value)
+ {
+ return $this->setViewState('CallbackScript', 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->CallbackClient->jQuery($this->getClientID().' #recaptcha_reload','click');
+ }
+
+ public function renderContents($writer)
+ {
+ $readyscript = 'jQuery(document).trigger('.TJavaScript::quoteString('captchaready:'.$this->getClientID()).')';
+ $cs = $this->Page->ClientScript;
+ $id = $this->getClientID();
+ $divid = $id.'_1_recaptchadiv';
+ $writer->write('<div id="'.htmlspecialchars($divid).'">');
+
+ if (!$this->Page->IsCallback)
+ {
+ $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);
+
+ $cs->registerEndScript('ReCaptcha::EventScript', 'jQuery(document).ready(function() { '.$readyscript.'; } );');
+ }
+ else
+ {
+ $options = $this->getClientSideOptions();
+ $options['callback'] = new TJavaScriptLiteral('function() { '.$readyscript.'; '.$this->getCallbackScript().'; }');
+ $cs->registerScriptFile('ReCaptcha::AjaxScript', 'http://www.google.com/recaptcha/api/js/recaptcha_ajax.js');
+ $cs->registerEndScript('ReCaptcha::CreateScript::'.$id, implode(' ', array(
+ 'if (!jQuery('.TJavaScript::quoteString('#'.$this->getResponseFieldName()).'))',
+ '{',
+ 'Recaptcha.destroy();',
+ 'Recaptcha.create(',
+ TJavaScript::quoteString($this->getPublicKey()).', ',
+ TJavaScript::quoteString($divid).', ',
+ TJavaScript::encode($options),
+ ');',
+ '}',
+ )));
+ }
+
+ $writer->write('</div>');
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TReCaptchaValidator.php b/lib/prado/framework/Web/UI/WebControls/TReCaptchaValidator.php
new file mode 100644
index 0000000..de3b42a
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TReCaptchaValidator.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * TReCaptchaValidator class file
+ *
+ * @author Bérczi Gábor <gabor.berczi@devworx.hu>
+ * @link http://www.devworx.hu/
+ * @copyright Copyright &copy; 2011 DevWorx
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <gabor.berczi@devworx.hu>
+ * @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();
+ $cs->registerPradoScript('validator');
+
+ // communicate validation status to the client side
+ $value = $this->_isvalid===false ? '0' : '1';
+ $cs->registerHiddenField($this->getClientID().'_1',$value);
+
+ // update validator display
+ if ($control = $this->getValidationTarget())
+ {
+ $fn = 'captchaUpdateValidatorStatus_'.$this->getClientID();
+
+ // check if we need to request a new captcha too
+ if ($this->Page->IsCallback)
+ {
+ if ($control->getVisible(true))
+ if (!is_null($this->_isvalid))
+ {
+ // if the response has been tested and we reach the pre-render phase
+ // then we need to regenerate the token, because it won't test positive
+ // anymore, even if solves correctly
+
+ $control->regenerateToken();
+ }
+ }
+
+ $cs->registerEndScript($this->getClientID().'::validate', implode(' ',array(
+ // this function will be used to update the validator
+ 'function '.$fn.'(valid)',
+ '{',
+ ' jQuery('.TJavaScript::quoteString('#'.$this->getClientID().'_1').').val(valid);',
+ ' Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).'); ',
+ '}',
+ '',
+ // update the validator to the result if we're in a callback
+ // (if we're in initial rendering or a postback then the result will be rendered directly to the page html anyway)
+ $this->Page->IsCallback ? $fn.'('.$value.');' : '',
+ '',
+ // install event handler that clears the validation error when user changes the captcha response field
+ 'jQuery("#'.$control->getClientID().'").on("keyup", '.TJavaScript::quoteString('#'.$control->getResponseFieldName()).', function() { ',
+ $fn.'("1");',
+ '});',
+ )));
+ }
+ }
+
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TRegularExpressionValidator.php b/lib/prado/framework/Web/UI/WebControls/TRegularExpressionValidator.php
new file mode 100644
index 0000000..1cdff8b
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRegularExpressionValidator.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * TRequiredFieldValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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:
+ * <pre>
+ * 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}
+ * </pre>
+ *
+ * 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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TRepeatInfo.php b/lib/prado/framework/Web/UI/WebControls/TRepeatInfo.php
new file mode 100644
index 0000000..f5823d7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRepeatInfo.php
@@ -0,0 +1,555 @@
+<?php
+/**
+ * IRepeatInfoUser, TRepeatInfo class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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("<td></td>\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("<td></td>\n");
+ }
+ if($row==$rows-1)
+ {
+ $restColumns=$columns-$lastColumns;
+ if($hasSeparators)
+ $restColumns+=$restColumns;
+ for($col=0;$col<$restColumns;++$col)
+ $writer->write("<td></td>\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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TRepeater.php b/lib/prado/framework/Web/UI/WebControls/TRepeater.php
new file mode 100644
index 0000000..3d295a2
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRepeater.php
@@ -0,0 +1,1019 @@
+<?php
+/**
+ * TRepeater class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>Data</b>
+ * 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 <b>ItemIndex</b> property will be set
+ * as the zero-based index of the item in the repeater item collection, and
+ * the <b>ItemType</b> 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 <b>OnCommand</b> event. Therefore,
+ * you can handle all sorts of <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnItemCreated</b> 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 <b>OnItemDataBound</b> 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 <b>OnItemCommand</b> event.
+ * This method is invoked after a button control in
+ * a template raises <b>OnCommand</b> 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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnCommand</b> 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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TRepeaterItemRenderer.php b/lib/prado/framework/Web/UI/WebControls/TRepeaterItemRenderer.php
new file mode 100644
index 0000000..1ea799c
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRepeaterItemRenderer.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * TRepeaterItemRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.0
+ */
+class TRepeaterItemRenderer extends TItemDataRenderer
+{
+ /**
+ * This method overrides parent's implementation by wrapping event parameter
+ * for <b>OnCommand</b> 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/lib/prado/framework/Web/UI/WebControls/TRequiredFieldValidator.php b/lib/prado/framework/Web/UI/WebControls/TRequiredFieldValidator.php
new file mode 100644
index 0000000..4587db8
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TRequiredFieldValidator.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * TRequiredFieldValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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.
+ *
+ * If the input is of TListControl type and has a {@link TListControl::setPromptValue PromptValue}
+ * set, it will be automatically considered as the validator's {@link setInitialValue InitialValue}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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
+ * unless the control has a prompt value set.
+ * If the associated input control does not change from this initial value
+ * upon postback, the validation fails.
+ */
+ public function getInitialValue()
+ {
+ return $this->getViewState('InitialValue',$this->getControlPromptValue());
+ }
+
+ /**
+ * @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.
+ */
+ protected function getControlPromptValue()
+ {
+ $control = $this->getValidationTarget();
+ if($control instanceof TListControl)
+ return $control->getPromptValue();
+ return '';
+ }
+ /**
+ * @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/lib/prado/framework/Web/UI/WebControls/TSafeHtml.php b/lib/prado/framework/Web/UI/WebControls/TSafeHtml.php
new file mode 100644
index 0000000..16743f9
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TSafeHtml.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * TSafeHtml class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <weizhuo[at]gmail[dot]com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TSlider.php b/lib/prado/framework/Web/UI/WebControls/TSlider.php
new file mode 100644
index 0000000..593cd2c
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TSlider.php
@@ -0,0 +1,570 @@
+<?php
+/**
+ * TSlider class file.
+ *
+ * @author Christophe Boulain <Christophe.Boulain@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>step</b> 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 <b>value</b>
+ * 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 <Christophe.Boulain@gmail.com>
+ * @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 <b>OnValueChanged</b> 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.
+ * # <tt>OnSliderMove</tt> -- raised when the slider is moved.
+ * # <tt>OnSliderChanged</tt> -- 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'] = array($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 <tt>OnMove</tt> event is raised when the slider moves
+ * The <tt>OnChange</tt> event is raised when the slider value is changed (or at the end of a move)
+ *
+ * @author Christophe Boulain <Christophe.Boulain@gmail.com>
+ * @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 <Christophe.Boulain@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TSliderDirection extends TEnumerable
+{
+ const Horizontal='Horizontal';
+ const Vertical='Vertical';
+}
+
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TStatements.php b/lib/prado/framework/Web/UI/WebControls/TStatements.php
new file mode 100644
index 0000000..e9cbeb3
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TStatements.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * TStatements class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TStyle.php b/lib/prado/framework/Web/UI/WebControls/TStyle.php
new file mode 100644
index 0000000..eb47f2f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TStyle.php
@@ -0,0 +1,885 @@
+<?php
+/**
+ * TStyle class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <weizhuo[at]gmail[dot]com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TStyleSheet.php b/lib/prado/framework/Web/UI/WebControls/TStyleSheet.php
new file mode 100644
index 0000000..ae5692d
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TStyleSheet.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * TStyleSheet class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TStyleSheet class.
+ *
+ * TStyleSheet represents the link to a stylesheet file and/or a piece of
+ * stylesheet code. To specify the link to a CSS file, set {@link setStyleSheetUrl StyleSheetUrl}.
+ * The child rendering result of TStyleSheet is treated as CSS code and
+ * is rendered within an appropriate style HTML element.
+ * Therefore, if the child content is not empty, you should place the TStyleSheet
+ * control in the head section of your page to conform to the HTML standard.
+ * If only CSS file URL is specified, you may place the control anywhere on your page
+ * and the style element will be rendered in the right position.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version : $ Tue Jul 4 04:38:16 EST 2006 $
+ * @package System.Web.UI.WebControls
+ * @since 3.0.2
+ */
+class TStyleSheet extends TControl
+{
+ /**
+ * @param string URL to the stylesheet file
+ */
+ public function setStyleSheetUrl($value)
+ {
+ $this->setViewState('StyleSheetUrl', $value);
+ }
+
+ /**
+ * @return string URL to the stylesheet file
+ */
+ public function getStyleSheetUrl()
+ {
+ return $this->getViewState('StyleSheetUrl', '');
+ }
+
+ /**
+ * @return string media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types.
+ */
+ public function getMediaType()
+ {
+ return $this->getViewState('MediaType','');
+ }
+
+ /**
+ * @param string media type of the CSS (such as 'print', 'screen', etc.). If empty, it means the CSS applies to all media types.
+ */
+ public function setMediaType($value)
+ {
+ $this->setViewState('MediaType',$value,'');
+ }
+
+ /**
+ * Registers the stylesheet file and content to be rendered.
+ * This method overrides the parent implementation and is invoked right before rendering.
+ * @param mixed event parameter
+ */
+ public function onPreRender($param)
+ {
+ if(($url=$this->getStyleSheetUrl())!=='')
+ $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url,$this->getMediaType());
+ }
+
+ /**
+ * Renders the control.
+ * This method overrides the parent implementation and renders nothing.
+ * @param ITextWriter writer
+ */
+ public function render($writer)
+ {
+ if($this->getHasControls())
+ {
+ $writer->write("<style type=\"text/css\">\n/*<![CDATA[*/\n");
+ $this->renderChildren($writer);
+ $writer->write("\n/*]]>*/\n</style>\n");
+ }
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TTabPanel.php b/lib/prado/framework/Web/UI/WebControls/TTabPanel.php
new file mode 100644
index 0000000..29baefd
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTabPanel.php
@@ -0,0 +1,728 @@
+<?php
+/**
+ * TTabPanel class file.
+ *
+ * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+
+/**
+ * Class TTabPanel.
+ *
+ * TTabPanel displays a tabbed panel. Users can click on the tab bar to switching among
+ * different tab views. Each tab view is an independent panel that can contain arbitrary content.
+ *
+ * If the {@link setAutoSwitch AutoSwitch} property is enabled, the user will be able to switch the active view
+ * to another one just hovering its corresponding tab caption.
+ *
+ * A TTabPanel control consists of one or several {@link TTabView} controls representing the possible
+ * tab views. At any time, only one tab view is visible (active), which is specified by any of
+ * the following properties:
+ * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection.
+ * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view.
+ * - {@link setActiveView ActiveView} - the visible view instance.
+ * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID}
+ * are set, the latter takes precedence.
+ *
+ * TTabPanel uses CSS to specify the appearance of the tab bar and panel. By default,
+ * an embedded CSS file will be published which contains the default CSS for TTabPanel.
+ * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property.
+ * The following properties specify the CSS classes used for elements in a TTabPanel:
+ * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'tab-panel');
+ * - {@link setTabCssClass TabCssClass} - the CSS class name for nonactive tab div elements (defaults to 'tab-normal');
+ * - {@link setActiveTabCssClass ActiveTabCssClass} - the CSS class name for the active tab div element (defaults to 'tab-active');
+ * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'tab-view');
+ *
+ * To use TTabPanel, write a template like following:
+ * <code>
+ * <com:TTabPanel>
+ * <com:TTabView Caption="View 1">
+ * content for view 1
+ * </com:TTabView>
+ * <com:TTabView Caption="View 2">
+ * content for view 2
+ * </com:TTabView>
+ * <com:TTabView Caption="View 3">
+ * content for view 3
+ * </com:TTabView>
+ * </com:TTabPanel>
+ * </code>
+ *
+ * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TTabPanel extends TWebControl implements IPostBackDataHandler
+{
+ private $_dataChanged=false;
+
+ /**
+ * @return string tag name for the control
+ */
+ protected function getTagName()
+ {
+ return 'div';
+ }
+
+ /**
+ * Adds object parsed from template to the control.
+ * This method adds only {@link TTabView} objects into the {@link getViews Views} collection.
+ * All other objects are ignored.
+ * @param mixed object parsed from template
+ */
+ public function addParsedObject($object)
+ {
+ if($object instanceof TTabView)
+ $this->getControls()->add($object);
+ }
+
+ /**
+ * Returns the index of the active tab view.
+ * Note, this property may not return the correct index.
+ * To ensure the correctness, call {@link getActiveView()} first.
+ * @return integer the zero-based index of the active tab view. If -1, it means no active tab view. Default is 0 (the first view is active).
+ */
+ public function getActiveViewIndex()
+ {
+ return $this->getViewState('ActiveViewIndex',0);
+ }
+
+ /**
+ * @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)
+ {
+ $this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0);
+ }
+
+ /**
+ * Returns the ID of the active tab view.
+ * Note, this property may not return the correct ID.
+ * To ensure the correctness, call {@link getActiveView()} first.
+ * @return string The ID of the active tab view. Defaults to '', meaning not set.
+ */
+ public function getActiveViewID()
+ {
+ return $this->getViewState('ActiveViewID','');
+ }
+
+ /**
+ * @param string The ID of the active tab view.
+ */
+ public function setActiveViewID($value)
+ {
+ $this->setViewState('ActiveViewID',$value,'');
+ }
+
+ /**
+ * Returns the currently active view.
+ * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to
+ * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly.
+ * @return TTabView the currently active view, null if no active view
+ * @throws TInvalidDataValueException if the active view ID or index set previously is invalid
+ */
+ public function getActiveView()
+ {
+ $activeView=null;
+ $views=$this->getViews();
+ if(($id=$this->getActiveViewID())!=='')
+ {
+ if(($index=$views->findIndexByID($id))>=0)
+ $activeView=$views->itemAt($index);
+ else
+ throw new TInvalidDataValueException('tabpanel_activeviewid_invalid',$id);
+ }
+ else if(($index=$this->getActiveViewIndex())>=0)
+ {
+ if($index<$views->getCount())
+ $activeView=$views->itemAt($index);
+ else
+ throw new TInvalidDataValueException('tabpanel_activeviewindex_invalid',$index);
+ }
+ else
+ {
+ foreach($views as $index=>$view)
+ {
+ if($view->getActive())
+ {
+ $activeView=$view;
+ break;
+ }
+ }
+ }
+ if($activeView!==null)
+ $this->activateView($activeView);
+ return $activeView;
+ }
+
+ /**
+ * @param TTabView the view to be activated
+ * @throws TInvalidOperationException if the view is not in the view collection
+ */
+ public function setActiveView($view)
+ {
+ if($this->getViews()->indexOf($view)>=0)
+ $this->activateView($view);
+ else
+ throw new TInvalidOperationException('tabpanel_view_inexistent');
+ }
+
+ /**
+ * @return bool status of automatic tab switch on hover
+ */
+ public function getAutoSwitch()
+ {
+ return TPropertyValue::ensureBoolean($this->getViewState('AutoSwitch'));
+ }
+
+ /**
+ * @param bool whether to enable automatic tab switch on hover
+ */
+ public function setAutoSwitch($value)
+ {
+ $this->setViewState('AutoSwitch',TPropertyValue::ensureBoolean($value));
+ }
+
+
+ /**
+ * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''.
+ */
+ public function getCssUrl()
+ {
+ return $this->getViewState('CssUrl','default');
+ }
+
+ /**
+ * @param string URL for the CSS file including all relevant CSS class definitions.
+ */
+ public function setCssUrl($value)
+ {
+ $this->setViewState('CssUrl',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string CSS class for the whole tab control div. Defaults to 'tab-panel'.
+ */
+ public function getCssClass()
+ {
+ $cssClass=parent::getCssClass();
+ return $cssClass===''?'tab-panel':$cssClass;
+ }
+
+ /**
+ * @return string CSS class for the currently displayed view div. Defaults to 'tab-view'.
+ */
+ public function getViewCssClass()
+ {
+ return $this->getViewStyle()->getCssClass();
+ }
+
+ /**
+ * @param string CSS class for the currently displayed view div.
+ */
+ public function setViewCssClass($value)
+ {
+ $this->getViewStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return TStyle the style for all the view div
+ */
+ public function getViewStyle()
+ {
+ if(($style=$this->getViewState('ViewStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('tab-view');
+ $this->setViewState('ViewStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * @return string CSS class for non-active tabs. Defaults to 'tab-normal'.
+ */
+ public function getTabCssClass()
+ {
+ return $this->getTabStyle()->getCssClass();
+ }
+
+ /**
+ * @param string CSS class for non-active tabs.
+ */
+ public function setTabCssClass($value)
+ {
+ $this->getTabStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return TStyle the style for all the inactive tab div
+ */
+ public function getTabStyle()
+ {
+ if(($style=$this->getViewState('TabStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('tab-normal');
+ $this->setViewState('TabStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * @return string CSS class for the active tab. Defaults to 'tab-active'.
+ */
+ public function getActiveTabCssClass()
+ {
+ return $this->getActiveTabStyle()->getCssClass();
+ }
+
+ /**
+ * @param string CSS class for the active tab.
+ */
+ public function setActiveTabCssClass($value)
+ {
+ $this->getActiveTabStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return TStyle the style for the active tab div
+ */
+ public function getActiveTabStyle()
+ {
+ if(($style=$this->getViewState('ActiveTabStyle',null))===null)
+ {
+ $style=new TStyle;
+ $style->setCssClass('tab-active');
+ $this->setViewState('ActiveTabStyle',$style,null);
+ }
+ return $style;
+ }
+
+ /**
+ * Activates the specified view.
+ * If there is any other view currently active, it will be deactivated.
+ * @param TTabView the view to be activated. If null, all views will be deactivated.
+ */
+ protected function activateView($view)
+ {
+ $this->setActiveViewIndex(-1);
+ $this->setActiveViewID('');
+ foreach($this->getViews() as $index=>$v)
+ {
+ if($view===$v)
+ {
+ $this->setActiveViewIndex($index);
+ $this->setActiveViewID($view->getID(false));
+ $view->setActive(true);
+ }
+ else
+ $v->setActive(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(($index=$values[$this->getClientID().'_1'])!==null)
+ {
+ $index=(int)$index;
+ $currentIndex=$this->getActiveViewIndex();
+ if($currentIndex!==$index)
+ {
+ $this->setActiveViewID(''); // clear up view ID
+ $this->setActiveViewIndex($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 getActiveViewIndex ActiveViewIndex} property
+ * is changed on postback.
+ * This method is primarly used by framework developers.
+ */
+ public function raisePostDataChangedEvent()
+ {
+ // do nothing
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Adds attributes to renderer.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ $writer->addAttribute('id',$this->getClientID());
+ $this->setCssClass($this->getCssClass());
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
+ * 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->getActiveView(); // determine the active view
+ $this->registerStyleSheet();
+
+ $page=$this->getPage();
+ $page->registerRequiresPostData($this);
+ $page->registerRequiresPostData($this->getClientID()."_1");
+ }
+
+ /**
+ * Registers the CSS relevant to the TTabControl.
+ * 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()
+ {
+ $url = $this->getCssUrl();
+
+ if($url === '') {
+ return;
+ }
+
+ if($url === 'default') {
+ $url = $this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'tabpanel.css');
+ }
+
+ if($url !== '') {
+ $this->getPage()->getClientScript()->registerStyleSheetFile($url, $url);
+ }
+ }
+
+ /**
+ * Registers the relevant JavaScript.
+ */
+ protected function registerClientScript()
+ {
+ $id=$this->getClientID();
+ $options=TJavaScript::encode($this->getClientOptions());
+ $className=$this->getClientClassName();
+ $cs=$this->getPage()->getClientScript();
+ $cs->registerPradoScript('tabpanel');
+ $code="new $className($options);";
+ $cs->registerEndScript("prado:$id", $code);
+ // ensure an item is always active and visible
+ $index = $this->getActiveViewIndex();
+ if(!$this->getViews()->itemAt($index)->Visible)
+ $index=0;
+ $cs->registerHiddenField($id.'_1', $index);
+ }
+
+ /**
+ * 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.TTabPanel';
+ }
+
+ /**
+ * @return array the options for JavaScript
+ */
+ protected function getClientOptions()
+ {
+ $options['ID'] = $this->getClientID();
+ $options['ActiveCssClass'] = $this->getActiveTabCssClass();
+ $options['NormalCssClass'] = $this->getTabCssClass();
+ $viewIDs = array();
+ $viewVis = array();
+ foreach($this->getViews() as $view)
+ {
+ $viewIDs[] = $view->getClientID();
+ $viewVis[] = $view->getVisible();
+ }
+ $options['Views'] = $viewIDs;
+ $options['ViewsVis'] = $viewVis;
+ $options['AutoSwitch'] = $this->getAutoSwitch();
+
+ return $options;
+ }
+
+ /**
+ * Creates a control collection object that is to be used to hold child controls
+ * @return TTabViewCollection control collection
+ */
+ protected function createControlCollection()
+ {
+ return new TTabViewCollection($this);
+ }
+
+ /**
+ * @return TTabViewCollection list of {@link TTabView} controls
+ */
+ public function getViews()
+ {
+ return $this->getControls();
+ }
+
+ public function render($writer)
+ {
+ $this->registerClientScript();
+ parent::render($writer);
+ }
+
+ /**
+ * Renders body contents of the tab control.
+ * @param THtmlWriter the writer used for the rendering purpose.
+ */
+ public function renderContents($writer)
+ {
+ $views=$this->getViews();
+ if($views->getCount()>0)
+ {
+ $writer->writeLine();
+ // render tab bar
+ foreach($views as $view)
+ {
+ $view->renderTab($writer);
+ $writer->writeLine();
+ }
+ // render tab views
+ foreach($views as $view)
+ {
+ $view->renderControl($writer);
+ $writer->writeLine();
+ }
+ }
+ }
+}
+
+/**
+ * TTabView class.
+ *
+ * TTabView represents a view in a {@link TTabPanel} control.
+ *
+ * The content in a TTabView can be specified by the {@link setText Text} property
+ * or its child controls. In template syntax, the latter means enclosing the content
+ * within the TTabView component element. If both are set, {@link getText Text} takes precedence.
+ *
+ * Each TTabView is associated with a tab in the tab bar of the TTabPanel control.
+ * The tab caption is specified by {@link setCaption Caption}. If {@link setNavigateUrl NavigateUrl}
+ * is set, the tab will contain a hyperlink pointing to the specified URL. In this case,
+ * clicking on the tab will redirect the browser to the specified URL.
+ *
+ * TTabView may be toggled between visible (active) and invisible (inactive) by
+ * setting the {@link setActive Active} property.
+ *
+ * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TTabView extends TWebControl
+{
+ private $_active=false;
+
+ /**
+ * @return the tag name for the view element
+ */
+ protected function getTagName()
+ {
+ return 'div';
+ }
+
+ /**
+ * Adds attributes to renderer.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ if(!$this->getActive() && $this->getPage()->getClientSupportsJavaScript())
+ $this->getStyle()->setStyleField('display','none');
+
+ $this->getStyle()->mergeWith($this->getParent()->getViewStyle());
+
+ parent::addAttributesToRender($writer);
+
+ $writer->addAttribute('id',$this->getClientID());
+ }
+
+ /**
+ * @return string the caption displayed on this tab. Defaults to ''.
+ */
+ public function getCaption()
+ {
+ return $this->getViewState('Caption','');
+ }
+
+ /**
+ * @param string the caption displayed on this tab
+ */
+ public function setCaption($value)
+ {
+ $this->setViewState('Caption',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string the URL of the target page. Defaults to ''.
+ */
+ public function getNavigateUrl()
+ {
+ return $this->getViewState('NavigateUrl','');
+ }
+
+ /**
+ * Sets the URL of the target page.
+ * If not empty, clicking on this tab will redirect the browser to the specified URL.
+ * @param string the URL of the target page.
+ */
+ public function setNavigateUrl($value)
+ {
+ $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return string the text content displayed on this view. Defaults to ''.
+ */
+ public function getText()
+ {
+ return $this->getViewState('Text','');
+ }
+
+ /**
+ * Sets the text content to be displayed on this view.
+ * If this is not empty, the child content of the view will be ignored.
+ * @param string the text content displayed on this view
+ */
+ public function setText($value)
+ {
+ $this->setViewState('Text',TPropertyValue::ensureString($value),'');
+ }
+
+ /**
+ * @return boolean whether this tab view is active. Defaults to false.
+ */
+ public function getActive()
+ {
+ return $this->_active;
+ }
+
+ /**
+ * @param boolean whether this tab view is active.
+ */
+ public function setActive($value)
+ {
+ $this->_active=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * Renders body contents of the tab view.
+ * @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);
+ }
+
+ /**
+ * Renders the tab associated with the tab view.
+ * @param THtmlWriter the writer for rendering purpose.
+ */
+ public function renderTab($writer)
+ {
+ if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript())
+ {
+ $writer->addAttribute('id',$this->getClientID().'_0');
+
+ $style=$this->getActive()?$this->getParent()->getActiveTabStyle():$this->getParent()->getTabStyle();
+ $style->addAttributesToRender($writer);
+
+ $writer->renderBeginTag($this->getTagName());
+
+ $this->renderTabContent($writer);
+
+ $writer->renderEndTag();
+ }
+ }
+
+ /**
+ * Renders the content in the tab.
+ * By default, a hyperlink is displayed.
+ * @param THtmlWriter the HTML writer
+ */
+ protected function renderTabContent($writer)
+ {
+ if(($url=$this->getNavigateUrl())==='')
+ $url='javascript://';
+ if(($caption=$this->getCaption())==='')
+ $caption='&nbsp;';
+ $writer->write("<a href=\"{$url}\">{$caption}</a>");
+ }
+}
+
+/**
+ * TTabViewCollection class.
+ *
+ * TTabViewCollection is used to maintain a list of views belong to a {@link TTabPanel}.
+ *
+ * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1.1
+ */
+class TTabViewCollection extends TControlCollection
+{
+ /**
+ * Inserts an item at the specified position.
+ * This overrides the parent implementation by performing sanity check on the type of new item.
+ * @param integer the speicified position.
+ * @param mixed new item
+ * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TTabView} object.
+ */
+ public function insertAt($index,$item)
+ {
+ if($item instanceof TTabView)
+ parent::insertAt($index,$item);
+ else
+ throw new TInvalidDataTypeException('tabviewcollection_tabview_required');
+ }
+
+ /**
+ * Finds the index of the tab view whose ID is the same as the one being looked for.
+ * @param string the explicit ID of the tab view to be looked for
+ * @return integer the index of the tab view found, -1 if not found.
+ */
+ public function findIndexByID($id)
+ {
+ foreach($this as $index=>$view)
+ {
+ if($view->getID(false)===$id)
+ return $index;
+ }
+ return -1;
+ }
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TTable.php b/lib/prado/framework/Web/UI/WebControls/TTable.php
new file mode 100644
index 0000000..98dc74e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTable.php
@@ -0,0 +1,406 @@
+<?php
+/**
+ * TTable and TTableRowCollection class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * <com:TTable>
+ * <com:TTableRow>
+ * <com:TTableCell Text="content" />
+ * <com:TTableCell Text="content" />
+ * </com:TTableRow>
+ * <com:TTableRow>
+ * <com:TTableCell Text="content" />
+ * <com:TTableCell Text="content" />
+ * </com:TTableRow>
+ * </com:TTable>
+ * </code>
+ * The above can also be accomplished in code as follows,
+ * <code>
+ * $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);
+ * </code>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTableCell.php b/lib/prado/framework/Web/UI/WebControls/TTableCell.php
new file mode 100644
index 0000000..b0398d2
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTableCell.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ * TTableCell class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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('&nbsp;');
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/TTableFooterRow.php b/lib/prado/framework/Web/UI/WebControls/TTableFooterRow.php
new file mode 100644
index 0000000..c3e8fe9
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTableFooterRow.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * TTableFooterRow class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTableHeaderCell.php b/lib/prado/framework/Web/UI/WebControls/TTableHeaderCell.php
new file mode 100644
index 0000000..bb2953b
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTableHeaderCell.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * TTableHeaderCell class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTableHeaderRow.php b/lib/prado/framework/Web/UI/WebControls/TTableHeaderRow.php
new file mode 100644
index 0000000..7eaedc3
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTableHeaderRow.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * TTableHeaderRow class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTableRow.php b/lib/prado/framework/Web/UI/WebControls/TTableRow.php
new file mode 100644
index 0000000..ebbe9df
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTableRow.php
@@ -0,0 +1,204 @@
+<?php
+/**
+ * TTableRow and TTableCellCollection class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTemplateColumn.php b/lib/prado/framework/Web/UI/WebControls/TTemplateColumn.php
new file mode 100644
index 0000000..b97b61f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTemplateColumn.php
@@ -0,0 +1,254 @@
+<?php
+/**
+ * TTemplateColumn class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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 <b>Data</b> 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 <b>Data</b> 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('&nbsp;');
+ }
+ 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/lib/prado/framework/Web/UI/WebControls/TTextBox.php b/lib/prado/framework/Web/UI/WebControls/TTextBox.php
new file mode 100644
index 0000000..f3a0c16
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTextBox.php
@@ -0,0 +1,648 @@
+<?php
+/**
+ * TTextBox class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <b>SingleLine</b>,
+ * a <b>MultiLine</b>, or a <b>Password</b> 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 <qiang.xue@gmail.com>
+ * @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 <b>OnTextChanged</b> 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TTextHighlighter.php b/lib/prado/framework/Web/UI/WebControls/TTextHighlighter.php
new file mode 100644
index 0000000..6183851
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTextHighlighter.php
@@ -0,0 +1,210 @@
+<?php
+/**
+ * TTextHighlighter class file
+ *
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+Prado::using('System.3rdParty.TextHighlighter.Text.Highlighter',false);
+Prado::using('System.3rdParty.TextHighlighter.Text.Highlighter.Renderer.Html',false);
+Prado::using('System.Web.UI.WebControls.TTextProcessor');
+
+
+/**
+ * TTextHighlighter class.
+ *
+ * TTextHighlighter does syntax highlighting its body content, including
+ * static text and rendering results of child controls.
+ * You can set {@link setLanguage Language} to specify what kind of syntax
+ * the body content is. Currently, TTextHighlighter supports the following
+ * languages: ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, MYSQL, PERL,
+ * PHP, PYTHON, RUBY, SQL, XML and PRADO, where PRADO refers to PRADO template
+ * syntax. By setting {@link setShowLineNumbers ShowLineNumbers}
+ * to true, the highlighted result may be shown with line numbers.
+ *
+ * Note, TTextHighlighter requires {@link THead} to be placed on the page template
+ * because it needs to insert some CSS styles.
+ *
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TTextHighlighter extends TTextProcessor
+{
+ private static $_lineNumberStyle=array(TTextHighlighterLineNumberStyle::Li => HL_NUMBERS_LI, TTextHighlighterLineNumberStyle::Table => HL_NUMBERS_TABLE);
+
+ /**
+ * @return string tag name of the panel
+ */
+ protected function getTagName()
+ {
+ return 'div';
+ }
+
+ /**
+ * @return string language whose syntax is to be used for highlighting. Defaults to 'php'.
+ */
+ public function getLanguage()
+ {
+ return $this->getViewState('Language', 'php');
+ }
+
+ /**
+ * @param string language (case-insensitive) whose syntax is to be used for highlighting.
+ * Valid values are those file names (without suffix) that are contained
+ * in '3rdParty/TextHighlighter/Text/Highlighter'. Currently, the following languages are supported:
+ * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT,
+ * MYSQL, PERL, PHP, PRADO, PYTHON, RUBY, SQL, XML
+ * If a language is not supported, it will be displayed as plain text.
+ */
+ public function setLanguage($value)
+ {
+ $this->setViewState('Language', $value, 'php');
+ }
+
+ /**
+ * @return boolean whether to show line numbers in the highlighted result.
+ */
+ public function getShowLineNumbers()
+ {
+ return $this->getViewState('ShowLineNumbers', false);
+ }
+
+ /**
+ * @param boolean whether to show line numbers in the highlighted result.
+ */
+ public function setShowLineNumbers($value)
+ {
+ $this->setViewState('ShowLineNumbers', TPropertyValue::ensureBoolean($value), false);
+ }
+
+ /**
+ * @return boolean true will show "Copy Code" link. Defaults to false.
+ */
+ public function getEnableCopyCode()
+ {
+ return $this->getViewState('CopyCode', false);
+ }
+
+ /**
+ * @param boolean true to show the "Copy Code" link.
+ */
+ public function setEnableCopyCode($value)
+ {
+ $this->setViewState('CopyCode', TPropertyValue::ensureBoolean($value), false);
+ }
+
+ /**
+ * @return TTextHighlighterLineNumberStyle style of row number, Table by default
+ */
+ public function getLineNumberStyle()
+ {
+ return $this->getViewState('LineNumberStyle', TTextHighlighterLineNumberStyle::Table);
+ }
+
+ /**
+ * @param TTextHighlighterLineNumberStyle style of row number
+ */
+ public function setLineNumberStyle($value)
+ {
+ $this->setViewState('LineNumberStyle', TPropertyValue::ensureEnum($value,'TTextHighlighterLineNumberStyle'));
+ }
+
+ /**
+ * @return integer tab size. Defaults to 4.
+ */
+ public function getTabSize()
+ {
+ return $this->getViewState('TabSize', 4);
+ }
+
+ /**
+ * @param integer tab size
+ */
+ public function setTabSize($value)
+ {
+ $this->setViewState('TabSize', TPropertyValue::ensureInteger($value));
+ }
+
+ /**
+ * Registers css style for the highlighted result.
+ * This method overrides parent implementation.
+ * @param THtmlWriter writer
+ */
+ public function onPreRender($writer)
+ {
+ parent::onPreRender($writer);
+ $this->registerStyleSheet();
+ }
+
+ /**
+ * Registers the stylesheet for presentation.
+ */
+ protected function registerStyleSheet()
+ {
+ $cs=$this->getPage()->getClientScript();
+ $cssFile=Prado::getPathOfNamespace('System.3rdParty.TextHighlighter.highlight','.css');
+ $cssKey='prado:TTextHighlighter:'.$cssFile;
+ if(!$cs->isStyleSheetFileRegistered($cssKey))
+ $cs->registerStyleSheetFile($cssKey, $this->publishFilePath($cssFile));
+ }
+
+ /**
+ * 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)
+ {
+ try
+ {
+ $highlighter=Text_Highlighter::factory($this->getLanguage());
+ }
+ catch(Exception $e)
+ {
+ $highlighter=false;
+ }
+ if($highlighter===false)
+ return ('<pre>'.htmlentities(trim($text)).'</pre>');
+
+ $options["use_language"]=true;
+ $options["tabsize"] = $this->getTabSize();
+ if ($this->getShowLineNumbers())
+ $options["numbers"] = self::$_lineNumberStyle[$this->getLineNumberStyle()];
+ $highlighter->setRenderer(new Text_Highlighter_Renderer_Html($options));
+ return $highlighter->highlight(trim($text));
+ }
+
+ /**
+ * @return string header template with "Copy code" link.
+ */
+ protected function getHeaderTemplate()
+ {
+ $id = $this->getClientID();
+ return TJavaScript::renderScriptBlock("new Prado.WebUI.TTextHighlighter('{$id}');");
+ }
+
+ public function render($writer)
+ {
+ $this->getPage()->getClientScript()->registerPradoScript('prado');
+ parent::render($writer);
+ }
+
+
+}
+
+/**
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TTextHighlighterLineNumberStyle extends TEnumerable
+{
+ const Li='Li';
+ const Table='Table';
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TTextProcessor.php b/lib/prado/framework/Web/UI/WebControls/TTextProcessor.php
new file mode 100644
index 0000000..c213a96
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TTextProcessor.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * TTextProcessor class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TValidationSummary.php b/lib/prado/framework/Web/UI/WebControls/TValidationSummary.php
new file mode 100644
index 0000000..2fbdba7
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TValidationSummary.php
@@ -0,0 +1,531 @@
+<?php
+/**
+ * TValidationSummary class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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."<br/>\n";
+ foreach($messages as $message)
+ $content.="$message<br/>\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 .= "<ul>\n";
+ foreach($messages as $message)
+ $content.= '<li>'.$message."</li>\n";
+ $content .= "</ul>\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 <tt>OnHideSummary</tt> event is raise when the validation summary
+ * requests to hide the messages.
+ *
+ * The <tt>OnShowSummary</tt> event is raised when the validation summary
+ * requests to show the messages.
+ *
+ * See the quickstart documentation for further details.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TWebControl.php b/lib/prado/framework/Web/UI/WebControls/TWebControl.php
new file mode 100644
index 0000000..619cdd9
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TWebControl.php
@@ -0,0 +1,498 @@
+<?php
+/**
+ * TWebControl class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Includes TStyle and TWebAdapter definition
+ */
+Prado::using('System.Web.UI.WebControls.TStyle');
+Prado::using('System.Web.UI.WebControls.TWebControlAdapter');
+Prado::using('System.Web.UI.WebControls.TWebControlDecorator');
+
+/**
+ * TWebControl class
+ *
+ * TWebControl is the base class for controls that share a common set
+ * of UI-related properties and methods. TWebControl-derived controls
+ * are usually associated with HTML tags. They thus have tag name, attributes
+ * and body contents. You can override {@link getTagName} to specify the tag name,
+ * {@link addAttributesToRender} to specify the attributes to be rendered,
+ * and {@link renderContents} to customize the body content rendering.
+ * TWebControl encapsulates a set of properties related with CSS style fields,
+ * such as {@link getBackColor BackColor}, {@link getBorderWidth BorderWidth}, etc.
+ *
+ * Subclasses of TWebControl typically needs to override {@link addAttributesToRender}
+ * and {@link renderContents}. The former is used to render the attributes
+ * of the HTML tag associated with the control, while the latter is to render
+ * the body contents enclosed within the HTML tag.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TWebControl extends TControl implements IStyleable
+{
+ /**
+ * @var boolean ensures the inclusion the id in the tag rendering.
+ */
+ private $_ensureid=false;
+
+ /**
+ * @var TWebControlDecorator this render things before and after both the open and close tag
+ */
+ protected $_decorator;
+
+
+ /**
+ * Subclasses can override getEnsureId or just set this property. eg. If your subclass
+ * control does work with javascript and your class wants to flag that it requires an id
+ * to operate properly. Once set to true, it stays that way.
+ * @param boolean pass true to enable enforcement of the tag attribute id.
+ */
+ public function setEnsureId($value)
+ {
+ $this->_ensureid |= TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return whether this web control must have an id
+ */
+ public function getEnsureId()
+ {
+ return $this->_ensureid;
+ }
+
+ /**
+ * @return TWebControlDecorator
+ */
+ public function getDecorator($create=true)
+ {
+ if($create && !$this->_decorator)
+ $this->_decorator = Prado::createComponent('TWebControlDecorator', $this);
+ return $this->_decorator;
+ }
+
+ /**
+ * Copies basic control attributes from another control.
+ * Properties including AccessKey, ToolTip, TabIndex, Enabled
+ * and Attributes are copied.
+ * @param TWebControl source control
+ */
+ public function copyBaseAttributes(TWebControl $control)
+ {
+ $this->setAccessKey($control->getAccessKey());
+ $this->setToolTip($control->getToolTip());
+ $this->setTabIndex($control->getTabIndex());
+ if(!$control->getEnabled())
+ $this->setEnabled(false);
+ if($control->getHasAttributes())
+ $this->getAttributes()->copyFrom($control->getAttributes());
+ }
+
+ /**
+ * @return string the access key of the control
+ */
+ public function getAccessKey()
+ {
+ return $this->getViewState('AccessKey','');
+ }
+
+ /**
+ * Sets the access key of the control.
+ * Only one-character string can be set, or an exception will be raised.
+ * Pass in an empty string if you want to disable access key.
+ * @param string the access key to be set
+ * @throws TInvalidDataValueException if the access key is specified with more than one character
+ */
+ public function setAccessKey($value)
+ {
+ if(strlen($value)>1)
+ throw new TInvalidDataValueException('webcontrol_accesskey_invalid',get_class($this),$value);
+ $this->setViewState('AccessKey',$value,'');
+ }
+
+ /**
+ * @return string the background color of the control
+ */
+ public function getBackColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBackColor();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the background color of the control
+ */
+ public function setBackColor($value)
+ {
+ $this->getStyle()->setBackColor($value);
+ }
+
+ /**
+ * @return string the border color of the control
+ */
+ public function getBorderColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderColor();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the border color of the control
+ */
+ public function setBorderColor($value)
+ {
+ $this->getStyle()->setBorderColor($value);
+ }
+
+ /**
+ * @return string the border style of the control
+ */
+ public function getBorderStyle()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderStyle();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the border style of the control
+ */
+ public function setBorderStyle($value)
+ {
+ $this->getStyle()->setBorderStyle($value);
+ }
+
+ /**
+ * @return string the border width of the control
+ */
+ public function getBorderWidth()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getBorderWidth();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the border width of the control
+ */
+ public function setBorderWidth($value)
+ {
+ $this->getStyle()->setBorderWidth($value);
+ }
+
+ /**
+ * @return TFont the font of the control
+ */
+ public function getFont()
+ {
+ return $this->getStyle()->getFont();
+ }
+
+ /**
+ * @return string the foreground color of the control
+ */
+ public function getForeColor()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getForeColor();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the foreground color of the control
+ */
+ public function setForeColor($value)
+ {
+ $this->getStyle()->setForeColor($value);
+ }
+
+ /**
+ * @return string the height of the control
+ */
+ public function getHeight()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getHeight();
+ else
+ return '';
+ }
+
+ /**
+ * @param TDisplayStyle display style of the control, default is TDisplayStyle::Fixed
+ */
+ public function setDisplay($value)
+ {
+ $this->getStyle()->setDisplayStyle($value);
+ }
+
+ /**
+ * @return TDisplayStyle display style of the control, default is TDisplayStyle::Fixed
+ */
+ public function getDisplay()
+ {
+ return $this->getStyle()->getDisplayStyle();
+ }
+
+ /**
+ * @param string the css class of the control
+ */
+ public function setCssClass($value)
+ {
+ $this->getStyle()->setCssClass($value);
+ }
+
+ /**
+ * @return string the css class of the control
+ */
+ public function getCssClass()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getCssClass();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the height of the control
+ */
+ public function setHeight($value)
+ {
+ $this->getStyle()->setHeight($value);
+ }
+
+ /**
+ * @return boolean whether the control has defined any style information
+ */
+ public function getHasStyle()
+ {
+ return $this->getViewState('Style',null)!==null;
+ }
+
+ /**
+ * Creates a style object to be used by the control.
+ * This method may be overriden by controls to provide customized style.
+ * @return TStyle the default style created for TWebControl
+ */
+ protected function createStyle()
+ {
+ return new TStyle;
+ }
+
+ /**
+ * @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;
+ }
+ }
+
+ /**
+ * Sets the css style string of the control.
+ * The style string will be prefixed to the styles set via other control properties (e.g. Height, Width).
+ * @param string the css style string
+ * @throws TInvalidDataValueException if the parameter is not a string
+ */
+ public function setStyle($value)
+ {
+ if(is_string($value))
+ $this->getStyle()->setCustomStyle($value);
+ else
+ throw new TInvalidDataValueException('webcontrol_style_invalid',get_class($this));
+ }
+
+ /**
+ * Removes all style data.
+ */
+ public function clearStyle()
+ {
+ $this->clearViewState('Style');
+ }
+
+ /**
+ * @return integer the tab index of the control
+ */
+ public function getTabIndex()
+ {
+ return $this->getViewState('TabIndex',0);
+ }
+
+ /**
+ * Sets the tab index of the control.
+ * Pass 0 if you want to disable tab index.
+ * @param integer the tab index to be set
+ */
+ public function setTabIndex($value)
+ {
+ $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0);
+ }
+
+ /**
+ * 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.
+ * @return string tag name of the control to be rendered
+ */
+ protected function getTagName()
+ {
+ return 'span';
+ }
+
+ /**
+ * @return string the tooltip of the control
+ */
+ public function getToolTip()
+ {
+ return $this->getViewState('ToolTip','');
+ }
+
+ /**
+ * Sets the tooltip of the control.
+ * Pass an empty string if you want to disable tooltip.
+ * @param string the tooltip to be set
+ */
+ public function setToolTip($value)
+ {
+ $this->setViewState('ToolTip',$value,'');
+ }
+
+ /**
+ * @return string the width of the control
+ */
+ public function getWidth()
+ {
+ if($style=$this->getViewState('Style',null))
+ return $style->getWidth();
+ else
+ return '';
+ }
+
+ /**
+ * @param string the width of the control
+ */
+ public function setWidth($value)
+ {
+ $this->getStyle()->setWidth($value);
+ }
+
+
+ /**
+ * If your subclass overrides the onPreRender method be sure to call
+ * this method through parent::onPreRender($param); so your sub-class can be decorated,
+ * among other things.
+ * @param TEventParameter event parameter to be passed to the event handlers
+ */
+ public function onPreRender($param) {
+ if($decorator = $this->getDecorator(false))
+ $decorator->instantiate();
+
+ parent::onPreRender($param);
+ }
+
+ /**
+ * Adds attribute name-value pairs to renderer.
+ * By default, the method will render 'id', 'accesskey', 'disabled',
+ * 'tabindex', 'title' and all custom attributes.
+ * The method can be overriden to provide customized attribute rendering.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function addAttributesToRender($writer)
+ {
+ if($this->getID()!=='' || $this->getEnsureId())
+ $writer->addAttribute('id',$this->getClientID());
+ if(($accessKey=$this->getAccessKey())!=='')
+ $writer->addAttribute('accesskey',$accessKey);
+ if(!$this->getEnabled())
+ $writer->addAttribute('disabled','disabled');
+ if(($tabIndex=$this->getTabIndex())>0)
+ $writer->addAttribute('tabindex',"$tabIndex");
+ if(($toolTip=$this->getToolTip())!=='')
+ $writer->addAttribute('title',$toolTip);
+ if($style=$this->getViewState('Style',null))
+ $style->addAttributesToRender($writer);
+ if($this->getHasAttributes())
+ {
+ foreach($this->getAttributes() as $name=>$value)
+ $writer->addAttribute($name,$value);
+ }
+ }
+
+ /**
+ * Renders the control.
+ * This method overrides the parent implementation by replacing it with
+ * the following sequence:
+ * - {@link renderBeginTag}
+ * - {@link renderContents}
+ * - {@link renderEndTag}
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ public function render($writer)
+ {
+ $this->renderBeginTag($writer);
+ $this->renderContents($writer);
+ $this->renderEndTag($writer);
+ }
+
+ /**
+ * Renders the openning tag for the control (including attributes)
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ public function renderBeginTag($writer)
+ {
+ if($decorator = $this->getDecorator(false)) {
+ $decorator->renderPreTagText($writer);
+ $this->addAttributesToRender($writer);
+ $writer->renderBeginTag($this->getTagName());
+ $decorator->renderPreContentsText($writer);
+ } else {
+ $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
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ public function renderEndTag($writer)
+ {
+ if($decorator = $this->getDecorator(false)) {
+ $decorator->renderPostContentsText($writer);
+ $writer->renderEndTag();
+ $decorator->renderPostTagText($writer);
+ } else
+ $writer->renderEndTag($writer);
+ }
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TWebControlAdapter.php b/lib/prado/framework/Web/UI/WebControls/TWebControlAdapter.php
new file mode 100644
index 0000000..50da857
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TWebControlAdapter.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * TWebControlAdapter class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TWebControlDecorator.php b/lib/prado/framework/Web/UI/WebControls/TWebControlDecorator.php
new file mode 100644
index 0000000..07c7836
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TWebControlDecorator.php
@@ -0,0 +1,382 @@
+<?php
+/**
+ * TWebControlDecorator class file.
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+
+/**
+ * TWebControlDecorator class
+ *
+ * A TWebControlDecorator can be applied to a {@link TWebControl} to customize its rendering.
+ * TWebControlDecorator can add custom html code before and after both the open and close
+ * tag of a {@link TWebControl}.
+ * The html code can be an user-defined text or an external template file that will be
+ * instantiated and rendered in place.
+ *
+ * This is an easy way to have your look and feel depend upon the theme instead of writing
+ * specific html in your templates to achieve your website desires.
+ * Here is an example of how to code your theme skin:
+ * <code>
+ * <com:THeader3>
+ * <prop:Decorator.PreTagText>
+ * <!-- Surround the control with a div and apply a css class to it -->
+ * <div class="imported-theme-h3-container">
+ * </prop:Decorator.PreTagText>
+ * <prop:Decorator.PostTagText>
+ * <!-- Properly close the tag -->
+ * </div>
+ * </prop:Decorator.PostTagText>
+ * </com:THeader3>
+ * </code>
+ *
+ * The order of the inclusion of the decoration into the page goes like this:
+ * * PreTagTemplate
+ * * PreTagText
+ * * TWebControl Open Tag Rendered
+ * * PreContentsText
+ * * PreContentsTemplate
+ * * TWebControl Children Rendered
+ * * PostContentsTemplate
+ * * PostContentsText
+ * * TWebControl CloseTag Rendered
+ * * PostTagText
+ * * PostTagTemplate
+ *
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.2
+ */
+
+class TWebControlDecorator extends TComponent {
+
+ /**
+ * @var boolean tells if there should only be decoration around the inner content
+ */
+ private $_internalonly;
+
+ /**
+ * @var boolean tells if the decoration uses state in its templates. If there are no templates
+ * in the instance of the decoration this variable is unused.
+ */
+ private $_usestate = false;
+
+ /**
+ * @var TWebControl the control to decorate
+ */
+ private $_control;
+
+ /**
+ * @var TControl to tell the decorator where to place the outer controls
+ */
+ private $_outercontrol;
+
+ /**
+ * @var boolean This tells if the Templates have been
+ */
+ private $_addedTemplateDecoration=false;
+
+
+ /**
+ * @var string the text that goes before the open tag
+ */
+ private $_pretagtext = '';
+ /**
+ * @var string the text that goes after the open tag
+ */
+ private $_precontentstext = '';
+ /**
+ * @var string the text that goes before the close tag
+ */
+ private $_postcontentstext = '';
+ /**
+ * @var string the text that goes after the close tag
+ */
+ private $_posttagtext = '';
+
+
+
+ /**
+ * @var TTemplate the template that goes before the open tag
+ */
+ private $_pretagtemplate;
+ /**
+ * @var TTemplate the template that goes after the open tag
+ */
+ private $_precontentstemplate;
+ /**
+ * @var TTemplate the template that goes before the close tag
+ */
+ private $_postcontentstemplate;
+ /**
+ * @var TTemplate the template that goes after the close tag
+ */
+ private $_posttagtemplate;
+
+ /**
+ * Constructor.
+ * Initializes the control .
+ * @param TWebControl The control that is to be decorated.
+ * @param boolean whether decoration is just around the inner content
+ */
+ public function __construct($control, $onlyinternal = false) {
+ $this->_control = $control;
+ $this->_internalonly = $onlyinternal;
+ }
+
+ /**
+ * @return boolean if the templates in this decoration need state. This defaults to false
+ */
+ public function getUseState()
+ {
+ return $this->_usestate;
+ }
+
+ /**
+ * @param boolean $value true to tell the decoration that the templates need state and should be
+ * placed in a control step before the state is saved.
+ */
+ public function setUseState($value)
+ {
+ $this->_usestate = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return string gets the text before the open tag in the TWebControl
+ */
+ public function getPreTagText() {
+ return $this->_pretagtext;
+ }
+
+ /**
+ * @param string sets the text before the open tag in the TWebControl
+ */
+ public function setPreTagText($value) {
+ if(!$this->_internalonly && !$this->_control->getIsSkinApplied())
+ $this->_pretagtext = TPropertyValue::ensureString($value);
+ }
+
+
+ /**
+ * @return string the text after the open tag in the TWebControl
+ */
+ public function getPreContentsText() {
+ return $this->_precontentstext;
+ }
+
+ /**
+ * @param string sets the text after the open tag in the TWebControl
+ */
+ public function setPreContentsText($value) {
+ if(!$this->_control->getIsSkinApplied())
+ $this->_precontentstext = TPropertyValue::ensureString($value);
+ }
+
+
+ /**
+ * @return string the text before the close tag in the TWebControl
+ */
+ public function getPostContentsText() {
+ return $this->_postcontentstext;
+ }
+
+ /**
+ * @param string sets the text before the close tag in the TWebControl
+ */
+ public function setPostContentsText($value) {
+ if(!$this->_control->getIsSkinApplied())
+ $this->_postcontentstext = TPropertyValue::ensureString($value);
+ }
+
+
+ /**
+ * @return string the text before the close tag in the TWebControl
+ */
+ public function getPostTagText() {
+ return $this->_posttagtext;
+ }
+
+ /**
+ * @param string sets the text after the close tag in the TWebControl
+ */
+ public function setPostTagText($value) {
+ if(!$this->_internalonly && !$this->_control->getIsSkinApplied())
+ $this->_posttagtext = TPropertyValue::ensureString($value);
+ }
+
+
+ /**
+ * @return TTemplate|null the template before the open tag in the TWebControl. Defaults to null.
+ */
+ public function getPreTagTemplate() {
+ return $this->_pretagtemplate;
+ }
+
+ /**
+ * @param TTemplate sets the template before the open tag in the TWebControl
+ */
+ public function setPreTagTemplate($value) {
+ if(!$this->_internalonly && !$this->_control->getIsSkinApplied())
+ $this->_pretagtemplate = $value;
+ }
+
+
+ /**
+ * @return TTemplate|null the template after the open tag in the TWebControl. Defaults to null.
+ */
+ public function getPreContentsTemplate() {
+ return $this->_precontentstemplate;
+ }
+
+ /**
+ * @param TTemplate sets the template after the open tag in the TWebControl
+ */
+ public function setPreContentsTemplate($value) {
+ if(!$this->_control->getIsSkinApplied())
+ $this->_precontentstemplate = $value;
+ }
+
+
+ /**
+ * @return TTemplate|null the template before the close tag in the TWebControl. Defaults to null.
+ */
+ public function getPostContentsTemplate() {
+ return $this->_postcontentstemplate;
+ }
+
+ /**
+ * @param TTemplate sets the template before the close tag in the TWebControl
+ */
+ public function setPostContentsTemplate($value) {
+ if(!$this->_control->getIsSkinApplied())
+ $this->_postcontentstemplate = $value;
+ }
+
+
+ /**
+ * @return TTemplate|null the template after the close tag in the TWebControl. Defaults to null.
+ */
+ public function getPostTagTemplate() {
+ return $this->_posttagtemplate;
+ }
+
+ /**
+ * @param TTemplate sets the template before the close tag in the TWebControl
+ */
+ public function setPostTagTemplate($value) {
+ if(!$this->_internalonly && !$this->_control->getIsSkinApplied())
+ $this->_posttagtemplate = $value;
+ }
+
+ /**
+ * this is a framework call. The Text decoration can't
+ * influence the object hierarchy because they are rendered into into the writer directly.
+ * This call attaches the ensureTemplateDecoration to the TPage onSaveStateComplete so
+ * these controls don't have page states. This is as close to not influencing the page as possible.
+ */
+ public function instantiate($outercontrol = null) {
+ if($this->getPreTagTemplate() || $this->getPreContentsTemplate() ||
+ $this->getPostContentsTemplate() || $this->getPostTagTemplate()) {
+
+ $this->_outercontrol = $outercontrol;
+ if($this->getUseState())
+ $this->ensureTemplateDecoration();
+ else
+ $this->_control->getPage()->onSaveStateComplete[] = array($this, 'ensureTemplateDecoration');
+ }
+ }
+
+
+ /**
+ * This method places the templates around the open and close tag. This takes a parameter which is
+ * to specify the control to get the outer template decoration. If no outer control is specified
+ * @param TComponent this indicates the component or control to get the outer tag elements, just in case it's
+ * different than attached TWebControl. If none is provided, the outer templates default to the attached
+ * control
+ * @return boolean returns true if the template decorations have been added
+ */
+ public function ensureTemplateDecoration($sender=null, $param=null) {
+
+ $control = $this->_control;
+ $outercontrol = $this->_outercontrol;
+ if($outercontrol === null)
+ $outercontrol = $control;
+
+ if($this->_addedTemplateDecoration)
+ return $this->_addedTemplateDecoration;
+
+ $this->_addedTemplateDecoration = true;
+
+ if($this->getPreContentsTemplate())
+ {
+ $precontents = Prado::createComponent('TCompositeControl');
+ $this->getPreContentsTemplate()->instantiateIn($precontents);
+ $control->getControls()->insertAt(0, $precontents);
+ }
+
+ if($this->getPostContentsTemplate())
+ {
+ $postcontents = Prado::createComponent('TCompositeControl');
+ $this->getPostContentsTemplate()->instantiateIn($postcontents);
+ $control->getControls()->add($postcontents);
+ }
+
+ if(!$outercontrol->getParent())
+ return $this->_addedTemplateDecoration;
+
+
+ if($this->getPreTagTemplate())
+ {
+ $pretag = Prado::createComponent('TCompositeControl');
+ $this->getPreTagTemplate()->instantiateIn($pretag);
+ $outercontrol->getParent()->getControls()->insertBefore($outercontrol, $pretag);
+ }
+
+ if($this->getPostTagTemplate())
+ {
+ $posttag = Prado::createComponent('TCompositeControl');
+ $this->getPostTagTemplate()->instantiateIn($posttag);
+ $outercontrol->getParent()->getControls()->insertAfter($outercontrol, $posttag);
+ }
+ return true;
+ }
+
+
+ /**
+ * This method places the pre tag text into the {@link TTextWriter}
+ * @param {@link TTextWriter} the writer to which the text is written
+ */
+ public function renderPreTagText($writer) {
+ $writer->write($this->getPreTagText());
+ }
+
+ /**
+ * This method places the pre contents text into the {@link TTextWriter}
+ * @param {@link TTextWriter} the writer to which the text is written
+ */
+ public function renderPreContentsText($writer) {
+ $writer->write($this->getPreContentsText());
+ }
+
+ /**
+ * This method places the post contents text into the {@link TTextWriter}
+ * @param {@link TTextWriter} the writer to which the text is written
+ */
+ public function renderPostContentsText($writer) {
+ $writer->write($this->getPostContentsText());
+ }
+
+ /**
+ * This method places the post tag text into the {@link TTextWriter}
+ * @param {@link TTextWriter} the writer to which the text is written
+ */
+ public function renderPostTagText($writer) {
+ $writer->write($this->getPostTagText());
+ }
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/TWizard.php b/lib/prado/framework/Web/UI/WebControls/TWizard.php
new file mode 100644
index 0000000..bacb637
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TWizard.php
@@ -0,0 +1,2145 @@
+<?php
+/**
+ * TWizard and the relevant class definitions.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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,
+ * <code>
+ * <com:TWizard>
+ * <com:TWizardStep Title="step 1">
+ * content in step 1, may contain other controls
+ * </com:TWizardStep>
+ * <com:TWizardStep Title="step 2">
+ * content in step 2, may contain other controls
+ * </com:TWizardStep>
+ * </com:TWizard>
+ * </code>
+ *
+ * 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 <qiang.xue@gmail.com>
+ * @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);
+ }
+
+ /**
+ * @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 <b>OnActiveStepChanged</b> 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 <b>OnCancelButtonClick</b> 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 <b>OnCompleteButtonClick</b> 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 <b>OnNextButtonClick</b> 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 <b>OnPreviousButtonClick</b> 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 <b>OnSideBarButtonClick</b> 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();
+ $this->setEnsureId(true);
+ 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<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\">\n<tr><td width=\"1\" valign=\"top\">\n");
+ $this->_sideBar->renderControl($writer);
+ $writer->write("\n</td><td valign=\"top\">\n");
+ $this->_header->renderControl($writer);
+ $this->_stepContent->renderControl($writer);
+ $this->_navigation->renderControl($writer);
+ $writer->write("\n</td></tr></table>\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))
+ {
+ $requestedIndex=-1;
+ foreach ($this->getWizardSteps() as $index=>$step)
+ if ($step->getId()===$requestedStep)
+ {
+ $requestedIndex=$index;
+ break;
+ }
+ if ($requestedIndex<0)
+ 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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php b/lib/prado/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php
new file mode 100644
index 0000000..de94fc5
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * TWizardNavigationButtonStyle class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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 <qiang.xue@gmail.com>
+ * @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/lib/prado/framework/Web/UI/WebControls/TXmlTransform.php b/lib/prado/framework/Web/UI/WebControls/TXmlTransform.php
new file mode 100644
index 0000000..e3c206f
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/TXmlTransform.php
@@ -0,0 +1,193 @@
+<?php
+/**
+ * TXmlTransform class file
+ *
+ * @author Knut Urdalen <knut.urdalen@gmail.com>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TXmlTransform class
+ *
+ * TXmlTransform uses the PHP's XSL extension to perform
+ * {@link http://www.w3.org/TR/xslt XSL transformations} using the
+ * {@link http://xmlsoft.org/XSLT/ libxslt library}.
+ *
+ * To associate an XML style sheet with TXmlTransform set the
+ * {@link setTransformPath TransformPath} property to the namespace or path to the style sheet
+ * or set the {@link setTransformContent TransformContent} property to the XML style sheet
+ * data as a string.
+ *
+ * To associate the XML data to be transformed set the {@link setDocumentPath DocumentPath}
+ * property to the namespace or path to the XML document or set the
+ * {@link setDocumentContent DocumentContent} property to the XML data as a string.
+ *
+ * To add additional parameters to the transformation process you can use the {@link getParameters Parameters}
+ * property.
+ *
+ * @author Knut Urdalen <knut.urdalen@gmail.com>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @package System.Web.UI.WebControls
+ * @since 3.1
+ */
+class TXmlTransform extends TControl {
+
+ const EXT_XML_FILE = '.xml';
+ const EXT_XSL_FILE = '.xsl';
+
+ /**
+ * Constructor
+ *
+ * Initializes the TXmlTransform object and ensure that the XSL extension is available
+ * @throws TConfigurationException If XSL extension is not available
+ */
+ public function __construct() {
+ if(!class_exists('XSLTProcessor', false)) {
+ throw new TConfigurationException('xmltransform_xslextension_required');
+ }
+ }
+
+ /**
+ * @return string The path to the XML style sheet.
+ */
+ public function getTransformPath() {
+ return $this->getViewState('TransformPath', '');
+ }
+
+ /**
+ * @param string The path to the XML style sheet. It must be in namespace format.
+ */
+ public function setTransformPath($value) {
+ if(!is_file($value)) {
+ $value = Prado::getPathOfNamespace($value, self::EXT_XSL_FILE);
+ if($value === null) {
+ throw new TInvalidDataValueException('xmltransform_transformpath_invalid', $value);
+ }
+ }
+ $this->setViewState('TransformPath', $value, '');
+ }
+
+ /**
+ * @return string XML style sheet as string
+ */
+ public function getTransformContent() {
+ return $this->getViewState('TransformContent', '');
+ }
+
+ /**
+ * @param string $value XML style sheet as string
+ */
+ public function setTransformContent($value) {
+ $this->setViewState('TransformContent', $value, '');
+ }
+
+ /**
+ * @return string The path to the XML document. It must be in namespace format.
+ */
+ public function getDocumentPath() {
+ return $this->getViewState('DocumentPath', '');
+ }
+
+ /**
+ * @param string Namespace or path to XML document
+ * @throws TInvalidDataValueException
+ */
+ public function setDocumentPath($value) {
+ if(!is_file($value)) {
+ $value = Prado::getPathOfNamespace($value, self::EXT_XML_FILE);
+ if($value === null) {
+ throw new TInvalidDataValueException('xmltransform_documentpath_invalid', $value);
+ }
+ }
+ $this->setViewState('DocumentPath', $value, '');
+ }
+
+ /**
+ * @return string XML data
+ */
+ public function getDocumentContent() {
+ return $this->getViewState('DocumentContent', '');
+ }
+
+ /**
+ * @param string $value XML data. If not empty, it takes precedence over {@link setDocumentPath DocumentPath}.
+ */
+ public function setDocumentContent($value) {
+ $this->setViewState('DocumentContent', $value, '');
+ }
+
+ /**
+ * Returns the list of parameters to be applied to the transform.
+ * @return TAttributeCollection the list of custom parameters
+ */
+ public function getParameters() {
+ if($params = $this->getViewState('Parameters',null)) {
+ return $params;
+ } else {
+ $params = new TAttributeCollection();
+ $this->setViewState('Parameters', $params, null);
+ return $params;
+ }
+ }
+
+ private function getTransformXmlDocument() {
+ if(($content = $this->getTransformContent()) !== '') {
+ $document = new DOMDocument();
+ $document->loadXML($content);
+ return $document;
+ } else if(($path = $this->getTransformPath()) !== '') {
+ $document = new DOMDocument();
+ $document->load($path);
+ return $document;
+ } else {
+ throw new TConfigurationException('xmltransform_transform_required');
+ }
+ }
+
+ private function getSourceXmlDocument() {
+ if(($content = $this->getDocumentContent()) !== '') {
+ $document = new DOMDocument();
+ $document->loadXML($content);
+ return $document;
+ } else if(($path = $this->getDocumentPath()) !== '') {
+ $document = new DOMDocument();
+ $document->load($path);
+ return $document;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Performs XSL transformation and render the output.
+ * @param THtmlWriter The writer used for the rendering purpose
+ */
+ public function render($writer) {
+ if(($document=$this->getSourceXmlDocument()) === null) {
+ $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter());
+ parent::render($htmlWriter);
+ $document = new DOMDocument();
+ $document->loadXML($htmlWriter->flush());
+ }
+ $stylesheet = $this->getTransformXmlDocument();
+
+ // Perform XSL transformation
+ $xslt = new XSLTProcessor();
+ $xslt->importStyleSheet($stylesheet);
+
+ // Check for parameters
+ $parameters = $this->getParameters();
+ foreach($parameters as $name => $value) {
+ $xslt->setParameter('', $name, $value);
+ }
+ $output = $xslt->transformToXML($document);
+
+ // Write output
+ $writer->write($output);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSlider.css b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSlider.css
new file mode 100755
index 0000000..458467e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSlider.css
@@ -0,0 +1,96 @@
+/* Base CSS Class for the slider */
+.Slider
+{
+ position: relative;
+ margin: 10px;
+ padding: 0;
+ background-color: Gainsboro;
+ border: 1px outset;
+}
+
+/* Class for horizontal slider */
+.HorizontalSlider
+{
+ width: 210px;
+ height: 14px;
+ text-align: left;
+}
+
+/* Class for vertical slider */
+.VerticalSlider
+{
+ height: 210px;
+ width: 14px;
+}
+
+/* Class for Track */
+.Track
+{
+ border-style: inset;
+ outline-color: invert;
+ outline-style: none;
+ outline-width: 1px;
+ border-width: 1px;
+ position: absolute;
+ background-color: lightBlue;
+}
+
+/* Horizontal Track */
+.HorizontalSlider .Track
+{
+
+ height: 3px;
+ top: 5px;
+ width: 97%;
+ left: 1%;
+}
+
+/* Vertical Track */
+.VerticalSlider .Track
+{
+ height: 97%;
+ width: 3px;
+ left: 5px;
+ top: 1%;
+}
+
+/* Class for the progress indicator */
+.Progress
+{
+ background-color: red;
+}
+
+/* Horizontal one, take 100% of track (parent) height*/
+.HorizontalSlider .Progress
+{
+ height: 100%;
+}
+
+/* Vertical progress indicator, take 100% of track width*/
+.VerticalSlider .Progress
+{
+ width: 100%
+}
+
+/* Class for handle */
+.Handle {
+ border: 0px none;
+ position: relative;
+ cursor: move;
+}
+
+/* Horizontal slider handle */
+.HorizontalSlider .Handle
+{
+ width: 31px;
+ height: 14px;
+ background: transparent url("TSliderHandleHorizontal.png") no-repeat scroll center;
+}
+
+/* Vertical slider handle */
+.VerticalSlider .Handle
+{
+ width: 14px;
+ height: 31px;
+ background: transparent url("TSliderHandleVertical.png") no-repeat scroll center;
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png
new file mode 100644
index 0000000..d9dc7ff
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png
Binary files differ
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png
new file mode 100644
index 0000000..00532fb
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png
Binary files differ
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/accordion.css b/lib/prado/framework/Web/UI/WebControls/assets/accordion.css
new file mode 100644
index 0000000..842868e
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/accordion.css
@@ -0,0 +1,28 @@
+
+div.accordion {
+ position: relative;
+}
+
+div.accordion-header {
+ padding: 3px;
+ z-index: 10; position: relative;
+ cursor: pointer;
+ background-color: #5577aa;
+ color: white; font-weight: bold;
+ border: 1px solid;
+}
+
+div.accordion-header-active {
+ padding: 3px;
+ z-index: 10; position: relative;
+ cursor: pointer;
+ background-color: #334477;
+ color: red; font-weight: bold;
+ border: 1px solid red;
+}
+
+div.accordion-view {
+ overflow: auto;
+ padding-left: 3px; padding-right: 3px;
+ margin-bottom: 2px;
+}
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/captcha.php b/lib/prado/framework/Web/UI/WebControls/assets/captcha.php
new file mode 100644
index 0000000..bc20a57
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/captcha.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * CAPTCHA generator script.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @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<rand(5,10);$j++)
+ {
+ $points[]=rand(2*(20*($i+1)),2*(50*($i+1)));
+ $points[]=rand(30,$height+30);
+ }
+ imagesetthickness($image,rand(2,6));
+ imagepolygon($image,$points,intval(sizeof($points)/2),$color);
+ imagecolordeallocate($image,$color);
+ }
+}
+
+function morphImage($image,$width,$height)
+{
+ $tempImage=imagecreatetruecolor($width,$height);
+ $chunk=rand(1,5);
+ for($x=$y=0;$x<$width;$x+=$chunk)
+ {
+ $chunk=rand(1,5);
+ $y+=rand(-1,1);
+ if($y>=$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);
+ }
+}
+
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/keyboard.css b/lib/prado/framework/Web/UI/WebControls/assets/keyboard.css
new file mode 100644
index 0000000..ad88d76
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/keyboard.css
@@ -0,0 +1,80 @@
+div.Keyboard
+{
+ -moz-background-clip: -moz-initial;
+ -moz-background-origin: -moz-initial;
+ -moz-background-inline-policy: -moz-initial;
+ -moz-user-select: none;
+
+ display: block;
+ position: absolute;
+ border: 1px solid #698977;
+ background-color: #DDDDDD;
+ z-index: 1000 !important;
+ visibility: hidden;
+ padding: 4px 4px 4px 4px;
+ text-align: center;
+}
+
+div.Keyboard div.Line
+{
+ width: 267px;
+ height: 18px;
+}
+
+div.Keyboard div.Key
+{
+ width: 17px;
+ height: 17px;
+ margin: 1px 0px 0px 1px;
+ overflow: hidden;
+ float: left;
+ display: inline;
+}
+
+div.Keyboard div.Key div.Key1,
+div.Keyboard div.Key div.Key2
+{
+ overflow: hidden;
+ border: 1px solid #A5A5A5;
+ background-color: #F7F7F7;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #556C5F;
+ font-weight: normal;
+ text-align: center;
+ line-height: 15px;
+ vertical-align: middle;
+ display: block;
+ cursor: pointer;
+}
+
+div.Keyboard div.Key div.Hover
+{
+ color: #E02C03;
+ border-color: #E0431D;
+}
+
+div.Keyboard div.Key div.Active
+{
+ color: #FFFFFF;
+ background-color: #E0431D;
+}
+
+div.Keyboard.Shift div.Key div.Key1,
+div.Keyboard.Caps div.L div.Key1,
+div.Keyboard.Off div.Key div.Key2
+{
+ display: none;
+}
+
+div.Keyboard.Caps div.L div.Key2
+{
+ display: block;
+}
+
+div.Keyboard div.Bksp {width: 31px;}
+div.Keyboard div.Exit {width: 33px;}
+div.Keyboard div.Caps {width: 33px;}
+div.Keyboard div.\\ {width: 24px;}
+div.Keyboard div.Del {width: 24px;}
+div.Keyboard div.Shift {width: 42px;} \ No newline at end of file
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/tabpanel.css b/lib/prado/framework/Web/UI/WebControls/assets/tabpanel.css
new file mode 100644
index 0000000..43a42c6
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/tabpanel.css
@@ -0,0 +1,71 @@
+.tab-panel
+{
+ position: relative;
+ float: left;
+ width: 100%;
+}
+
+.tab-normal
+{
+ font-family: Verdana, Helvetica, Arial;
+ font-size: 12px;
+ display: inline;
+ margin: 1px -5px 1px 5px;
+ float: left;
+ padding: 3px 6px 4px 6px;
+ background: rgb(234,242,255);
+ border: 1px solid;
+ border-color: rgb(120,172,255);
+ z-index: 1;
+ position: relative;
+ top: 0;
+}
+
+.tab-active
+{
+ position: relative;
+ display: inline;
+ float: left;
+
+ font-family: Verdana, Helvetica, Arial;
+ font-size: 12px;
+
+ border: 1px solid rgb(120,172,255);
+ border-bottom: 0;
+ z-index: 3;
+ padding: 2px 6px 8px 6px;
+ margin: 1px -6px -2px 0px;
+ top: -2px;
+ background: white;
+}
+
+.tab-view
+{
+ clear: both;
+ border: 1px solid rgb(120,172,255);
+ z-index: 2;
+ position: relative;
+ top: -2px;
+ padding: 10px;
+}
+
+.tab-active a
+{
+ color: black;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+.tab-normal a
+{
+ color: gray;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+.tab-normal a:hover, .tab-normal a:focus
+{
+ color: rgb(120,172,255);
+ text-decoration: none;
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/lib/prado/framework/Web/UI/WebControls/assets/verase.ttf b/lib/prado/framework/Web/UI/WebControls/assets/verase.ttf
new file mode 100644
index 0000000..4b4ecc6
--- /dev/null
+++ b/lib/prado/framework/Web/UI/WebControls/assets/verase.ttf
Binary files differ