]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/UI/TControl.php
baculum: New Baculum API and Baculum Web
[bacula/bacula] / gui / baculum / framework / Web / UI / TControl.php
1 <?php
2 /**
3  * TControl, TControlCollection, TEventParameter and INamingContainer class file
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link https://github.com/pradosoft/prado
7  * @copyright Copyright &copy; 2005-2016 The PRADO Group
8  * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
9  * @package System.Web.UI
10  */
11
12 /**
13  * Includes TAttributeCollection and TControlAdapter class
14  */
15 Prado::using('System.Collections.TAttributeCollection');
16 Prado::using('System.Web.UI.TControlAdapter');
17
18 /**
19  * TControl class
20  *
21  * TControl is the base class for all components on a page hierarchy.
22  * It implements the following features for UI-related functionalities:
23  * - databinding feature
24  * - parent and child relationship
25  * - naming container and containee relationship
26  * - viewstate and controlstate features
27  * - rendering scheme
28  * - control lifecycles
29  *
30  * A property can be data-bound with an expression. By calling {@link dataBind},
31  * expressions bound to properties will be evaluated and the results will be
32  * set to the corresponding properties.
33  *
34  * Parent and child relationship determines how the presentation of controls are
35  * enclosed within each other. A parent will determine where to place
36  * the presentation of its child controls. For example, a TPanel will enclose
37  * all its child controls' presentation within a div html tag. A control's parent
38  * can be obtained via {@link getParent Parent} property, and its
39  * {@link getControls Controls} property returns a list of the control's children,
40  * including controls and static texts. The property can be manipulated
41  * like an array for adding or removing a child (see {@link TList} for more details).
42  *
43  * A naming container control implements INamingContainer and ensures that
44  * its containee controls can be differentiated by their ID property values.
45  * Naming container and containee realtionship specifies a protocol to uniquely
46  * identify an arbitrary control on a page hierarchy by an ID path (concatenation
47  * of all naming containers' IDs and the target control's ID).
48  *
49  * Viewstate and controlstate are two approaches to preserve state across
50  * page postback requests. ViewState is mainly related with UI specific state
51  * and can be disabled if not needed. ControlState represents crucial logic state
52  * and cannot be disabled.
53  *
54  * A control is rendered via its {@link render()} method (the method is invoked
55  * by the framework.) Descendant control classes may override this method for
56  * customized rendering. By default, {@link render()} invokes {@link renderChildren()}
57  * which is responsible for rendering of children of the control.
58  * Control's {@link getVisible Visible} property governs whether the control
59  * should be rendered or not.
60  *
61  * Each control on a page will undergo a series of lifecycles, including
62  * control construction, Init, Load, PreRender, Render, and OnUnload.
63  * They work together with page lifecycles to process a page request.
64  *
65  * @author Qiang Xue <qiang.xue@gmail.com>
66  * @package System.Web.UI
67  * @since 3.0
68  */
69 class TControl extends TApplicationComponent implements IRenderable, IBindable
70 {
71         /**
72          * format of control ID
73          */
74         const ID_FORMAT='/^[a-zA-Z_]\\w*$/';
75         /**
76          * separator char between IDs in a UniqueID
77          */
78         const ID_SEPARATOR='$';
79         /**
80          * separator char between IDs in a ClientID
81          */
82         const CLIENT_ID_SEPARATOR='_';
83         /**
84          * prefix to an ID automatically generated
85          */
86         const AUTOMATIC_ID_PREFIX='ctl';
87
88         /**
89          * the stage of lifecycles that the control is currently at
90          */
91         const CS_CONSTRUCTED=0;
92         const CS_CHILD_INITIALIZED=1;
93         const CS_INITIALIZED=2;
94         const CS_STATE_LOADED=3;
95         const CS_LOADED=4;
96         const CS_PRERENDERED=5;
97
98         /**
99          * State bits.
100          */
101         const IS_ID_SET=0x01;
102         const IS_DISABLE_VIEWSTATE=0x02;
103         const IS_SKIN_APPLIED=0x04;
104         const IS_STYLESHEET_APPLIED=0x08;
105         const IS_DISABLE_THEMING=0x10;
106         const IS_CHILD_CREATED=0x20;
107         const IS_CREATING_CHILD=0x40;
108
109         /**
110          * Indexes for the rare fields.
111          * In order to save memory, rare fields will only be created if they are needed.
112          */
113         const RF_CONTROLS=0;                    // child controls
114         const RF_CHILD_STATE=1;                 // child state field
115         const RF_NAMED_CONTROLS=2;              // list of controls whose namingcontainer is this control
116         const RF_NAMED_CONTROLS_ID=3;   // counter for automatic id
117         const RF_SKIN_ID=4;                             // skin ID
118         const RF_DATA_BINDINGS=5;               // data bindings
119         const RF_EVENTS=6;                              // event handlers
120         const RF_CONTROLSTATE=7;                // controlstate
121         const RF_NAMED_OBJECTS=8;               // controls declared with ID on template
122         const RF_ADAPTER=9;                             // adapter
123         const RF_AUTO_BINDINGS=10;              // auto data bindings
124
125         /**
126          * @var string control ID
127          */
128         private $_id='';
129         /**
130          * @var string control unique ID
131          */
132         private $_uid;
133         /**
134          * @var TControl parent of the control
135          */
136         private $_parent;
137         /**
138          * @var TPage page that the control resides in
139          */
140         private $_page;
141         /**
142          * @var TControl naming container of the control
143          */
144         private $_namingContainer;
145         /**
146          * @var TTemplateControl control whose template contains the control
147          */
148         private $_tplControl;
149         /**
150          * @var array viewstate data
151          */
152         private $_viewState=array();
153         /**
154          * @var array temporary state (usually set in template)
155          */
156         private $_tempState=array();
157         /**
158          * @var boolean whether we should keep state in viewstate
159          */
160         private $_trackViewState=true;
161         /**
162          * @var integer the current stage of the control lifecycles
163          */
164         private $_stage=0;
165         /**
166          * @var integer representation of the state bits
167          */
168         private $_flags=0;
169         /**
170          * @var array a collection of rare control data
171          */
172         private $_rf=array();
173
174         /**
175          * Returns a property value by name or a control by ID.
176          * This overrides the parent implementation by allowing accessing
177          * a control via its ID using the following syntax,
178          * <code>
179          * $menuBar=$this->menuBar;
180          * </code>
181          * Note, the control must be configured in the template
182          * with explicit ID. If the name matches both a property and a control ID,
183          * the control ID will take the precedence.
184          *
185          * @param string the property name or control ID
186          * @return mixed the property value or the target control
187          * @throws TInvalidOperationException if the property is not defined.
188          * @see registerObject
189          */
190         public function __get($name)
191         {
192                 if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name]))
193                         return $this->_rf[self::RF_NAMED_OBJECTS][$name];
194                 else
195                         return parent::__get($name);
196         }
197
198         /**
199          * Checks for the existance of a property value by name or a control by ID.
200          * This overrides the parent implementation by allowing checking for the
201          * existance of a control via its ID using the following syntax,
202          * <code>
203          * $menuBarExists = isset($this->menuBar);
204          * </code>
205          * Do not call this method. This is a PHP magic method that we override
206          * to allow using isset() to detect if a component property is set or not.
207          * Note, the control must be configured in the template
208          * with explicit ID. If the name matches both a property and a control ID,
209          * the control ID will take the precedence.
210          *
211          * @param string the property name or control ID
212          * @return bool wether the control or property exists
213          * @see __get
214          */
215         public function __isset($name) {
216                 if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) {
217                         return true;
218                 } else {
219                         return parent::__isset($name);
220                 }
221         }
222
223         /**
224          * @return boolean whether there is an adapter for this control
225          */
226         public function getHasAdapter()
227         {
228                 return isset($this->_rf[self::RF_ADAPTER]);
229         }
230
231         /**
232          * @return TControlAdapter control adapter. Null if not exists.
233          */
234         public function getAdapter()
235         {
236                 return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null;
237         }
238
239         /**
240          * @param TControlAdapter control adapter
241          */
242         public function setAdapter(TControlAdapter $adapter)
243         {
244                 $this->_rf[self::RF_ADAPTER]=$adapter;
245         }
246
247         /**
248          * @return TControl the parent of this control
249          */
250         public function getParent()
251         {
252                 return $this->_parent;
253         }
254
255         /**
256          * @return TControl the naming container of this control
257          */
258         public function getNamingContainer()
259         {
260                 if(!$this->_namingContainer && $this->_parent)
261                 {
262                         if($this->_parent instanceof INamingContainer)
263                                 $this->_namingContainer=$this->_parent;
264                         else
265                                 $this->_namingContainer=$this->_parent->getNamingContainer();
266                 }
267                 return $this->_namingContainer;
268         }
269
270         /**
271          * @return TPage the page that contains this control
272          */
273         public function getPage()
274         {
275                 if(!$this->_page)
276                 {
277                         if($this->_parent)
278                                 $this->_page=$this->_parent->getPage();
279                         else if($this->_tplControl)
280                                 $this->_page=$this->_tplControl->getPage();
281                 }
282                 return $this->_page;
283         }
284
285         /**
286          * Sets the page for a control.
287          * Only framework developers should use this method.
288          * @param TPage the page that contains this control
289          */
290         public function setPage($page)
291         {
292                 $this->_page=$page;
293         }
294
295         /**
296          * Sets the control whose template contains this control.
297          * Only framework developers should use this method.
298          * @param TTemplateControl the control whose template contains this control
299          */
300         public function setTemplateControl($control)
301         {
302                 $this->_tplControl=$control;
303         }
304
305         /**
306          * @return TTemplateControl the control whose template contains this control
307          */
308         public function getTemplateControl()
309         {
310                 if(!$this->_tplControl && $this->_parent)
311                         $this->_tplControl=$this->_parent->getTemplateControl();
312                 return $this->_tplControl;
313         }
314
315         /**
316          * @return TTemplateControl the control whose template is loaded from
317          * some external storage, such as file, db, and whose template ultimately
318          * contains this control.
319          */
320         public function getSourceTemplateControl()
321         {
322                 $control=$this;
323                 while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null)
324                 {
325                         if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl())
326                                 return $control;
327                 }
328                 return $this->getPage();
329         }
330
331         /**
332          * Gets the lifecycle step the control is currently at.
333          * This method should only be used by control developers.
334          * @return integer the lifecycle step the control is currently at.
335          * The value can be CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED,
336          * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED.
337          */
338         protected function getControlStage()
339         {
340                 return $this->_stage;
341         }
342
343         /**
344          * Sets the lifecycle step the control is currently at.
345          * This method should only be used by control developers.
346          * @param integer the lifecycle step the control is currently at.
347          * Valid values include CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED,
348          * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED.
349          */
350         protected function setControlStage($value)
351         {
352                 $this->_stage=$value;
353         }
354
355         /**
356          * Returns the id of the control.
357          * Control ID can be either manually set or automatically generated.
358          * If $hideAutoID is true, automatically generated ID will be returned as an empty string.
359          * @param boolean whether to hide automatically generated ID
360          * @return string the ID of the control
361          */
362         public function getID($hideAutoID=true)
363         {
364                 if($hideAutoID)
365                         return ($this->_flags & self::IS_ID_SET) ? $this->_id : '';
366                 else
367                         return $this->_id;
368         }
369
370         /**
371          * @param string the new control ID. The value must consist of word characters [a-zA-Z0-9_] only
372          * @throws TInvalidDataValueException if ID is in a bad format
373          */
374         public function setID($id)
375         {
376                 if(!preg_match(self::ID_FORMAT,$id))
377                         throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id);
378                 $this->_id=$id;
379                 $this->_flags |= self::IS_ID_SET;
380                 $this->clearCachedUniqueID($this instanceof INamingContainer);
381                 if($this->_namingContainer)
382                         $this->_namingContainer->clearNameTable();
383         }
384
385         /**
386          * Returns a unique ID that identifies the control in the page hierarchy.
387          * A unique ID is the contenation of all naming container controls' IDs and the control ID.
388          * These IDs are separated by '$' character.
389          * Control users should not rely on the specific format of UniqueID, however.
390          * @return string a unique ID that identifies the control in the page hierarchy
391          */
392         public function getUniqueID()
393         {
394                 if($this->_uid==='' || $this->_uid===null)      // need to build the UniqueID
395                 {
396                         $this->_uid='';  // set to not-null, so that clearCachedUniqueID() may take action
397                         if($namingContainer=$this->getNamingContainer())
398                         {
399                                 if($this->getPage()===$namingContainer)
400                                         return ($this->_uid=$this->_id);
401                                 else if(($prefix=$namingContainer->getUniqueID())==='')
402                                         return $this->_id;
403                                 else
404                                         return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id);
405                         }
406                         else    // no naming container
407                                 return $this->_id;
408                 }
409                 else
410                         return $this->_uid;
411         }
412
413         /**
414          * Sets input focus to this control.
415          */
416         public function focus()
417         {
418                 $this->getPage()->setFocus($this);
419         }
420
421         /**
422          * Returns the client ID of the control.
423          * The client ID can be used to uniquely identify
424          * the control in client-side scripts (such as JavaScript).
425          * Do not rely on the explicit format of the return ID.
426          * @return string the client ID of the control
427          */
428         public function getClientID()
429         {
430                 return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR);
431         }
432
433         /**
434          * Converts a unique ID to a client ID.
435          * @param string the unique ID of a control
436          * @return string the client ID of the control
437          */
438         public static function convertUniqueIdToClientId($uniqueID)
439         {
440                 return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR);
441         }
442
443         /**
444          * @return string the skin ID of this control, '' if not set
445          */
446         public function getSkinID()
447         {
448                 return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:'';
449         }
450
451         /**
452          * @param string the skin ID of this control
453          * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already.
454          */
455         public function setSkinID($value)
456         {
457                 if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED)
458                         throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this));
459                 else
460                         $this->_rf[self::RF_SKIN_ID]=$value;
461         }
462
463         /**
464          * @param string the skin ID of this control
465          * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already.
466          */
467         public function getIsSkinApplied()
468         {
469                 return ($this->_flags & self::IS_SKIN_APPLIED);
470         }
471
472         /**
473          * @return boolean whether theming is enabled for this control.
474          * The theming is enabled if the control and all its parents have it enabled.
475          */
476         public function getEnableTheming()
477         {
478                 if($this->_flags & self::IS_DISABLE_THEMING)
479                         return false;
480                 else
481                         return $this->_parent?$this->_parent->getEnableTheming():true;
482         }
483
484         /**
485          * @param boolean whether to enable theming
486          * @throws TInvalidOperationException if this method is invoked after OnPreInit
487          */
488         public function setEnableTheming($value)
489         {
490                 if($this->_stage>=self::CS_CHILD_INITIALIZED)
491                         throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID());
492                 else if(TPropertyValue::ensureBoolean($value))
493                         $this->_flags &= ~self::IS_DISABLE_THEMING;
494                 else
495                         $this->_flags |= self::IS_DISABLE_THEMING;
496         }
497
498         /**
499          * Returns custom data associated with this control.
500          * A control may be associated with some custom data for various purposes.
501          * For example, a button may be associated with a string to identify itself
502          * in a generic OnClick event handler.
503          * @return mixed custom data associated with this control. Defaults to null.
504          */
505         public function getCustomData()
506         {
507                 return $this->getViewState('CustomData',null);
508         }
509
510         /**
511          * Associates custom data with this control.
512          * Note, the custom data must be serializable and unserializable.
513          * @param mixed custom data
514          */
515         public function setCustomData($value)
516         {
517                 $this->setViewState('CustomData',$value,null);
518         }
519
520         /**
521          * @return boolean whether the control has child controls
522          */
523         public function getHasControls()
524         {
525                 return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0;
526         }
527
528         /**
529          * @return TControlCollection the child control collection
530          */
531         public function getControls()
532         {
533                 if(!isset($this->_rf[self::RF_CONTROLS]))
534                         $this->_rf[self::RF_CONTROLS]=$this->createControlCollection();
535                 return $this->_rf[self::RF_CONTROLS];
536         }
537
538         /**
539          * Creates a control collection object that is to be used to hold child controls
540          * @return TControlCollection control collection
541          * @see getControls
542          */
543         protected function createControlCollection()
544         {
545                 return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this);
546         }
547
548         /**
549          * Checks if a control is visible.
550          * If parent check is required, then a control is visible only if the control
551          * and all its ancestors are visible.
552          * @param boolean whether the parents should also be checked if visible
553          * @return boolean whether the control is visible (default=true).
554          */
555         public function getVisible($checkParents=true)
556         {
557                 if($checkParents)
558                 {
559                         for($control=$this;$control;$control=$control->_parent)
560                                 if(!$control->getVisible(false))
561                                         return false;
562                         return true;
563                 }
564                 else
565                         return $this->getViewState('Visible',true);
566         }
567
568         /**
569          * @param boolean whether the control is visible
570          */
571         public function setVisible($value)
572         {
573                 $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true);
574         }
575
576         /**
577          * Returns a value indicating whether the control is enabled.
578          * A control is enabled if it allows client user interaction.
579          * If $checkParents is true, all parent controls will be checked,
580          * and unless they are all enabled, false will be returned.
581          * The property Enabled is mainly used for {@link TWebControl}
582          * derived controls.
583          * @param boolean whether the parents should also be checked enabled
584          * @return boolean whether the control is enabled.
585          */
586         public function getEnabled($checkParents=false)
587         {
588                 if($checkParents)
589                 {
590                         for($control=$this;$control;$control=$control->_parent)
591                                 if(!$control->getViewState('Enabled',true))
592                                         return false;
593                         return true;
594                 }
595                 else
596                         return $this->getViewState('Enabled',true);
597         }
598
599         /**
600          * @param boolean whether the control is to be enabled.
601          */
602         public function setEnabled($value)
603         {
604                 $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true);
605         }
606
607         /**
608          * @return boolean whether the control has custom attributes
609          */
610         public function getHasAttributes()
611         {
612                 if($attributes=$this->getViewState('Attributes',null))
613                         return $attributes->getCount()>0;
614                 else
615                         return false;
616         }
617
618         /**
619          * Returns the list of custom attributes.
620          * Custom attributes are name-value pairs that may be rendered
621          * as HTML tags' attributes.
622          * @return TAttributeCollection the list of custom attributes
623          */
624         public function getAttributes()
625         {
626                 if($attributes=$this->getViewState('Attributes',null))
627                         return $attributes;
628                 else
629                 {
630                         $attributes=new TAttributeCollection;
631                         $this->setViewState('Attributes',$attributes,null);
632                         return $attributes;
633                 }
634         }
635
636         /**
637          * @return boolean whether the named attribute exists
638          */
639         public function hasAttribute($name)
640         {
641                 if($attributes=$this->getViewState('Attributes',null))
642                         return $attributes->contains($name);
643                 else
644                         return false;
645         }
646
647         /**
648          * @return string attribute value, null if attribute does not exist
649          */
650         public function getAttribute($name)
651         {
652                 if($attributes=$this->getViewState('Attributes',null))
653                         return $attributes->itemAt($name);
654                 else
655                         return null;
656         }
657
658         /**
659          * Sets a custom control attribute.
660          * @param string attribute name
661          * @param string value of the attribute
662          */
663         public function setAttribute($name,$value)
664         {
665                 $this->getAttributes()->add($name,$value);
666         }
667
668         /**
669          * Removes the named attribute.
670          * @param string the name of the attribute to be removed.
671          * @return string attribute value removed, null if attribute does not exist.
672          */
673         public function removeAttribute($name)
674         {
675                 if($attributes=$this->getViewState('Attributes',null))
676                         return $attributes->remove($name);
677                 else
678                         return null;
679         }
680
681         /**
682          * @return boolean whether viewstate is enabled
683          */
684         public function getEnableViewState($checkParents=false)
685         {
686                 if($checkParents)
687                 {
688                         for($control=$this;$control!==null;$control=$control->getParent())
689                                 if($control->_flags & self::IS_DISABLE_VIEWSTATE)
690                                         return false;
691                         return true;
692                 }
693                 else
694                         return !($this->_flags & self::IS_DISABLE_VIEWSTATE);
695         }
696
697         /**
698          * @param boolean set whether to enable viewstate
699          */
700         public function setEnableViewState($value)
701         {
702                 if(TPropertyValue::ensureBoolean($value))
703                         $this->_flags &= ~self::IS_DISABLE_VIEWSTATE;
704                 else
705                         $this->_flags |= self::IS_DISABLE_VIEWSTATE;
706         }
707
708         /**
709          * Returns a controlstate value.
710          *
711          * This function is mainly used in defining getter functions for control properties
712          * that must be kept in controlstate.
713          * @param string the name of the controlstate value to be returned
714          * @param mixed the default value. If $key is not found in controlstate, $defaultValue will be returned
715          * @return mixed the controlstate value corresponding to $key
716          */
717         protected function getControlState($key,$defaultValue=null)
718         {
719                 return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue;
720         }
721
722         /**
723          * Sets a controlstate value.
724          *
725          * This function is very useful in defining setter functions for control properties
726          * that must be kept in controlstate.
727          * Make sure that the controlstate value must be serializable and unserializable.
728          * @param string the name of the controlstate value
729          * @param mixed the controlstate value to be set
730          * @param mixed default value. If $value===$defaultValue, the item will be cleared from controlstate
731          */
732         protected function setControlState($key,$value,$defaultValue=null)
733         {
734                 if($value===$defaultValue)
735                         unset($this->_rf[self::RF_CONTROLSTATE][$key]);
736                 else
737                         $this->_rf[self::RF_CONTROLSTATE][$key]=$value;
738         }
739
740         /**
741          * Clears a controlstate value.
742          * @param string the name of the controlstate value to be cleared
743          */
744         protected function clearControlState($key)
745         {
746                 unset($this->_rf[self::RF_CONTROLSTATE][$key]);
747         }
748
749         /**
750          * Sets a value indicating whether we should keep data in viewstate.
751          * When it is false, data saved via setViewState() will not be persisted.
752          * By default, it is true, meaning data will be persisted across postbacks.
753          * @param boolean whether data should be persisted
754          */
755         public function trackViewState($enabled)
756         {
757                 $this->_trackViewState=TPropertyValue::ensureBoolean($enabled);
758         }
759
760         /**
761          * Returns a viewstate value.
762          *
763          * This function is very useful in defining getter functions for component properties
764          * that must be kept in viewstate.
765          * @param string the name of the viewstate value to be returned
766          * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned
767          * @return mixed the viewstate value corresponding to $key
768          */
769         public function getViewState($key,$defaultValue=null)
770         {
771                 if(isset($this->_viewState[$key]))
772                         return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue;
773                 else if(isset($this->_tempState[$key]))
774                 {
775                         if(is_object($this->_tempState[$key]) && $this->_trackViewState)
776                                 $this->_viewState[$key]=$this->_tempState[$key];
777                         return $this->_tempState[$key];
778                 }
779                 else
780                         return $defaultValue;
781         }
782
783         /**
784          * Sets a viewstate value.
785          *
786          * This function is very useful in defining setter functions for control properties
787          * that must be kept in viewstate.
788          * Make sure that the viewstate value must be serializable and unserializable.
789          * @param string the name of the viewstate value
790          * @param mixed the viewstate value to be set
791          * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate.
792          */
793         public function setViewState($key,$value,$defaultValue=null)
794         {
795                 if($this->_trackViewState)
796                 {
797                         unset($this->_tempState[$key]);
798                         $this->_viewState[$key]=$value;
799                 }
800                 else
801                 {
802                         unset($this->_viewState[$key]);
803                         if($value===$defaultValue)
804                                 unset($this->_tempState[$key]);
805                         else
806                                 $this->_tempState[$key]=$value;
807                 }
808         }
809
810         /**
811          * Clears a viewstate value.
812          * @param string the name of the viewstate value to be cleared
813          */
814         public function clearViewState($key)
815         {
816                 unset($this->_viewState[$key]);
817                 unset($this->_tempState[$key]);
818         }
819
820         /**
821          * Sets up the binding between a property (or property path) and an expression.
822          * The context of the expression is the template control (or the control itself if it is a page).
823          * @param string the property name, or property path
824          * @param string the expression
825          */
826         public function bindProperty($name,$expression)
827         {
828                 $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression;
829         }
830
831         /**
832          * Breaks the binding between a property (or property path) and an expression.
833          * @param string the property name (or property path)
834          */
835         public function unbindProperty($name)
836         {
837                 unset($this->_rf[self::RF_DATA_BINDINGS][$name]);
838         }
839
840         /**
841          * Sets up the binding between a property (or property path) and an expression.
842          * Unlike regular databinding, the expression bound by this method
843          * is automatically evaluated during {@link prerenderRecursive()}.
844          * The context of the expression is the template control (or the control itself if it is a page).
845          * @param string the property name, or property path
846          * @param string the expression
847          */
848         public function autoBindProperty($name,$expression)
849         {
850                 $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression;
851         }
852
853         /**
854          * Performs the databinding for this control.
855          */
856         public function dataBind()
857         {
858                 $this->dataBindProperties();
859                 $this->onDataBinding(null);
860                 $this->dataBindChildren();
861         }
862
863         /**
864          * Databinding properties of the control.
865          */
866         protected function dataBindProperties()
867         {
868                 Prado::trace("Data bind properties",'System.Web.UI.TControl');
869                 if(isset($this->_rf[self::RF_DATA_BINDINGS]))
870                 {
871                         if(($context=$this->getTemplateControl())===null)
872                                 $context=$this;
873                         foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression)
874                                 $this->setSubProperty($property,$context->evaluateExpression($expression));
875                 }
876         }
877
878         /**
879          * Auto databinding properties of the control.
880          */
881         protected function autoDataBindProperties()
882         {
883                 if(isset($this->_rf[self::RF_AUTO_BINDINGS]))
884                 {
885                         if(($context=$this->getTemplateControl())===null)
886                                 $context=$this;
887                         foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression)
888                                 $this->setSubProperty($property,$context->evaluateExpression($expression));
889                 }
890         }
891
892         /**
893          * Databinding child controls.
894          */
895         protected function dataBindChildren()
896         {
897                 Prado::trace("dataBindChildren()",'System.Web.UI.TControl');
898                 if(isset($this->_rf[self::RF_CONTROLS]))
899                 {
900                         foreach($this->_rf[self::RF_CONTROLS] as $control)
901                                 if($control instanceof IBindable)
902                                         $control->dataBind();
903                 }
904         }
905
906         /**
907          * @return boolean whether child controls have been created
908          */
909         final protected function getChildControlsCreated()
910         {
911                 return ($this->_flags & self::IS_CHILD_CREATED)!==0;
912         }
913
914         /**
915          * Sets a value indicating whether child controls are created.
916          * If false, any existing child controls will be cleared up.
917          * @param boolean whether child controls are created
918          */
919         final protected function setChildControlsCreated($value)
920         {
921                 if($value)
922                         $this->_flags |= self::IS_CHILD_CREATED;
923                 else
924                 {
925                         if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED))
926                                 $this->getControls()->clear();
927                         $this->_flags &= ~self::IS_CHILD_CREATED;
928                 }
929         }
930
931         /**
932          * Ensures child controls are created.
933          * If child controls are not created yet, this method will invoke
934          * {@link createChildControls} to create them.
935          */
936         public function ensureChildControls()
937         {
938                 if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD))
939                 {
940                         try
941                         {
942                                 $this->_flags |= self::IS_CREATING_CHILD;
943                                 if(isset($this->_rf[self::RF_ADAPTER]))
944                                         $this->_rf[self::RF_ADAPTER]->createChildControls();
945                                 else
946                                         $this->createChildControls();
947                                 $this->_flags &= ~self::IS_CREATING_CHILD;
948                                 $this->_flags |= self::IS_CHILD_CREATED;
949                         }
950                         catch(Exception $e)
951                         {
952                                 $this->_flags &= ~self::IS_CREATING_CHILD;
953                                 $this->_flags |= self::IS_CHILD_CREATED;
954                                 throw $e;
955                         }
956                 }
957         }
958
959         /**
960          * Creates child controls.
961          * This method can be overriden for controls who want to have their controls.
962          * Do not call this method directly. Instead, call {@link ensureChildControls}
963          * to ensure child controls are created only once.
964          */
965         public function createChildControls()
966         {
967         }
968
969         /**
970          * Finds a control by ID path within the current naming container.
971          * The current naming container is either the control itself
972          * if it implements {@link INamingContainer} or the control's naming container.
973          * The ID path is an ID sequence separated by {@link TControl::ID_SEPARATOR}.
974          * For example, 'Repeater1.Item1.Button1' looks for a control with ID 'Button1'
975          * whose naming container is 'Item1' whose naming container is 'Repeater1'.
976          * @param string ID of the control to be looked up
977          * @return TControl|null the control found, null if not found
978          * @throws TInvalidDataValueException if a control's ID is found not unique within its naming container.
979          */
980         public function findControl($id)
981         {
982                 $id=strtr($id,'.',self::ID_SEPARATOR);
983                 $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer();
984                 if(!$container || !$container->getHasControls())
985                         return null;
986                 if(!isset($container->_rf[self::RF_NAMED_CONTROLS]))
987                 {
988                         $container->_rf[self::RF_NAMED_CONTROLS]=array();
989                         $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]);
990                 }
991                 if(($pos=strpos($id,self::ID_SEPARATOR))===false)
992                         return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null;
993                 else
994                 {
995                         $cid=substr($id,0,$pos);
996                         $sid=substr($id,$pos+1);
997                         if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid]))
998                                 return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid);
999                         else
1000                                 return null;
1001                 }
1002         }
1003
1004         /**
1005          * Finds all child and grand-child controls that are of the specified type.
1006          * @param string the class name
1007          * @param boolean whether the type comparison is strict or not. If false, controls of the parent classes of the specified class will also be returned.
1008          * @return array list of controls found
1009          */
1010         public function findControlsByType($type,$strict=true)
1011         {
1012                 $controls=array();
1013                 if($this->getHasControls())
1014                 {
1015                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1016                         {
1017                                 if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type))))
1018                                         $controls[]=$control;
1019                                 if(($control instanceof TControl) && $control->getHasControls())
1020                                         $controls=array_merge($controls,$control->findControlsByType($type,$strict));
1021                         }
1022                 }
1023                 return $controls;
1024         }
1025
1026         /**
1027          * Finds all child and grand-child controls with the specified ID.
1028          * Note, this method is different from {@link findControl} in that
1029          * it searches through all controls that have this control as the ancestor
1030          * while {@link findcontrol} only searches through controls that have this
1031          * control as the direct naming container.
1032          * @param string the ID being looked for
1033          * @return array list of controls found
1034          */
1035         public function findControlsByID($id)
1036         {
1037                 $controls=array();
1038                 if($this->getHasControls())
1039                 {
1040                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1041                         {
1042                                 if($control instanceof TControl)
1043                                 {
1044                                         if($control->_id===$id)
1045                                                 $controls[]=$control;
1046                                         $controls=array_merge($controls,$control->findControlsByID($id));
1047                                 }
1048                         }
1049                 }
1050                 return $controls;
1051         }
1052
1053         /**
1054          * Resets the control as a naming container.
1055          * Only framework developers should use this method.
1056          */
1057         public function clearNamingContainer()
1058         {
1059                 unset($this->_rf[self::RF_NAMED_CONTROLS_ID]);
1060                 $this->clearNameTable();
1061         }
1062
1063         /**
1064          * Registers an object by a name.
1065          * A registered object can be accessed like a public member variable.
1066          * This method should only be used by framework and control developers.
1067          * @param string name of the object
1068          * @param object object to be declared
1069          * @see __get
1070          */
1071         public function registerObject($name,$object)
1072         {
1073                 if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name]))
1074                         throw new TInvalidOperationException('control_object_reregistered',$name);
1075                 $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object;
1076         }
1077
1078         /**
1079          * Unregisters an object by name.
1080          * @param string name of the object
1081          * @see registerObject
1082          */
1083         public function unregisterObject($name)
1084         {
1085                 unset($this->_rf[self::RF_NAMED_OBJECTS][$name]);
1086         }
1087
1088         /**
1089          * @return boolean whether an object has been registered with the name
1090          * @see registerObject
1091          */
1092         public function isObjectRegistered($name)
1093         {
1094                 return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]);
1095         }
1096
1097         /**
1098          * @return boolean true if the child control has been initialized.
1099          */
1100         public function getHasChildInitialized()
1101         {
1102                 return $this->getControlStage() >= self::CS_CHILD_INITIALIZED;
1103         }
1104
1105         /**
1106          * @return boolean true if the onInit event has raised.
1107          */
1108         public function getHasInitialized()
1109         {
1110                 return $this->getControlStage() >= self::CS_INITIALIZED;
1111         }
1112
1113         /**
1114          * @return boolean true if the control has loaded post data.
1115          */
1116         public function getHasLoadedPostData()
1117         {
1118                 return $this->getControlStage() >= self::CS_STATE_LOADED;
1119         }
1120
1121         /**
1122          * @return boolean true if the onLoad event has raised.
1123          */
1124         public function getHasLoaded()
1125         {
1126                 return $this->getControlStage() >= self::CS_LOADED;
1127         }
1128
1129         /**
1130          * @return boolean true if onPreRender event has raised.
1131          */
1132         public function getHasPreRendered()
1133         {
1134                 return $this->getControlStage() >= self::CS_PRERENDERED;
1135         }
1136
1137         /**
1138          * Returns the named registered object.
1139          * A component with explicit ID on a template will be registered to
1140          * the template owner. This method allows you to obtain this component
1141          * with the ID.
1142          * @return mixed the named registered object. Null if object is not found.
1143          */
1144         public function getRegisteredObject($name)
1145         {
1146                 return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null;
1147         }
1148
1149         /**
1150          * @return boolean whether body contents are allowed for this control. Defaults to true.
1151          */
1152         public function getAllowChildControls()
1153         {
1154                 return true;
1155         }
1156
1157         /**
1158          * Adds the object instantiated on a template to the child control collection.
1159          * This method overrides the parent implementation.
1160          * Only framework developers and control developers should use this method.
1161          * @param string|TComponent text string or component parsed and instantiated in template
1162          * @see createdOnTemplate
1163          */
1164         public function addParsedObject($object)
1165         {
1166                 $this->getControls()->add($object);
1167         }
1168
1169         /**
1170          * Clears up the child state data.
1171          * After a control loads its state, those state that do not belong to
1172          * any existing child controls are stored as child state.
1173          * This method will remove these state.
1174          * Only frameworker developers and control developers should use this method.
1175          */
1176         final protected function clearChildState()
1177         {
1178                 unset($this->_rf[self::RF_CHILD_STATE]);
1179         }
1180
1181         /**
1182          * @param TControl the potential ancestor control
1183          * @return boolean if the control is a descendent (parent, parent of parent, etc.)
1184          * of the specified control
1185          */
1186         final protected function isDescendentOf($ancestor)
1187         {
1188                 $control=$this;
1189                 while($control!==$ancestor && $control->_parent)
1190                         $control=$control->_parent;
1191                 return $control===$ancestor;
1192         }
1193
1194         /**
1195          * Adds a control into the child collection of the control.
1196          * Control lifecycles will be caught up during the addition.
1197          * Only framework developers should use this method.
1198          * @param TControl the new child control
1199          */
1200         public function addedControl($control)
1201         {
1202                 if($control->_parent)
1203                         $control->_parent->getControls()->remove($control);
1204                 $control->_parent=$this;
1205                 $control->_page=$this->getPage();
1206                 $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer;
1207                 if($namingContainer)
1208                 {
1209                         $control->_namingContainer=$namingContainer;
1210                         if($control->_id==='')
1211                                 $control->generateAutomaticID();
1212                         else
1213                                 $namingContainer->clearNameTable();
1214                         $control->clearCachedUniqueID($control instanceof INamingContainer);
1215                 }
1216
1217                 if($this->_stage>=self::CS_CHILD_INITIALIZED)
1218                 {
1219                         $control->initRecursive($namingContainer);
1220                         if($this->_stage>=self::CS_STATE_LOADED)
1221                         {
1222                                 if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id]))
1223                                 {
1224                                         $state=$this->_rf[self::RF_CHILD_STATE][$control->_id];
1225                                         unset($this->_rf[self::RF_CHILD_STATE][$control->_id]);
1226                                 }
1227                                 else
1228                                         $state=null;
1229                                 $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE));
1230                                 if($this->_stage>=self::CS_LOADED)
1231                                 {
1232                                         $control->loadRecursive();
1233                                         if($this->_stage>=self::CS_PRERENDERED)
1234                                                 $control->preRenderRecursive();
1235                                 }
1236                         }
1237                 }
1238         }
1239
1240         /**
1241          * Removes a control from the child collection of the control.
1242          * Only framework developers should use this method.
1243          * @param TControl the child control removed
1244          */
1245         public function removedControl($control)
1246         {
1247                 if($this->_namingContainer)
1248                         $this->_namingContainer->clearNameTable();
1249                 $control->unloadRecursive();
1250                 $control->_parent=null;
1251                 $control->_page=null;
1252                 $control->_namingContainer=null;
1253                 $control->_tplControl=null;
1254                 //$control->_stage=self::CS_CONSTRUCTED;
1255                 if(!($control->_flags & self::IS_ID_SET))
1256                         $control->_id='';
1257                 else
1258                         unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]);
1259                 $control->clearCachedUniqueID(true);
1260         }
1261
1262         /**
1263          * Performs the Init step for the control and all its child controls.
1264          * Only framework developers should use this method.
1265          * @param TControl the naming container control
1266          */
1267         protected function initRecursive($namingContainer=null)
1268         {
1269                 $this->ensureChildControls();
1270                 if($this->getHasControls())
1271                 {
1272                         if($this instanceof INamingContainer)
1273                                 $namingContainer=$this;
1274                         $page=$this->getPage();
1275                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1276                         {
1277                                 if($control instanceof TControl)
1278                                 {
1279                                         $control->_namingContainer=$namingContainer;
1280                                         $control->_page=$page;
1281                                         if($control->_id==='' && $namingContainer)
1282                                                 $control->generateAutomaticID();
1283                                         $control->initRecursive($namingContainer);
1284                                 }
1285                         }
1286                 }
1287                 if($this->_stage<self::CS_INITIALIZED)
1288                 {
1289                         $this->_stage=self::CS_CHILD_INITIALIZED;
1290                         if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED))
1291                         {
1292                                 $page->applyControlSkin($this);
1293                                 $this->_flags |= self::IS_SKIN_APPLIED;
1294                         }
1295                         if(isset($this->_rf[self::RF_ADAPTER]))
1296                                 $this->_rf[self::RF_ADAPTER]->onInit(null);
1297                         else
1298                                 $this->onInit(null);
1299                         $this->_stage=self::CS_INITIALIZED;
1300                 }
1301         }
1302
1303         /**
1304          * Performs the Load step for the control and all its child controls.
1305          * Only framework developers should use this method.
1306          */
1307         protected function loadRecursive()
1308         {
1309                 if($this->_stage<self::CS_LOADED)
1310                 {
1311                         if(isset($this->_rf[self::RF_ADAPTER]))
1312                                 $this->_rf[self::RF_ADAPTER]->onLoad(null);
1313                         else
1314                                 $this->onLoad(null);
1315                 }
1316                 if($this->getHasControls())
1317                 {
1318                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1319                         {
1320                                 if($control instanceof TControl)
1321                                         $control->loadRecursive();
1322                         }
1323                 }
1324                 if($this->_stage<self::CS_LOADED)
1325                         $this->_stage=self::CS_LOADED;
1326         }
1327
1328         /**
1329          * Performs the PreRender step for the control and all its child controls.
1330          * Only framework developers should use this method.
1331          */
1332         protected function preRenderRecursive()
1333         {
1334                 $this->autoDataBindProperties();
1335
1336                 if($this->getVisible(false))
1337                 {
1338                         if(isset($this->_rf[self::RF_ADAPTER]))
1339                                 $this->_rf[self::RF_ADAPTER]->onPreRender(null);
1340                         else
1341                                 $this->onPreRender(null);
1342                         if($this->getHasControls())
1343                         {
1344                                 foreach($this->_rf[self::RF_CONTROLS] as $control)
1345                                 {
1346                                         if($control instanceof TControl)
1347                                                 $control->preRenderRecursive();
1348                                         else if($control instanceof TCompositeLiteral)
1349                                                 $control->evaluateDynamicContent();
1350                                 }
1351                         }
1352                 }
1353                 $this->_stage=self::CS_PRERENDERED;
1354         }
1355
1356         /**
1357          * Performs the Unload step for the control and all its child controls.
1358          * Only framework developers should use this method.
1359          */
1360         protected function unloadRecursive()
1361         {
1362                 if(!($this->_flags & self::IS_ID_SET))
1363                         $this->_id='';
1364                 if($this->getHasControls())
1365                 {
1366                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1367                                 if($control instanceof TControl)
1368                                         $control->unloadRecursive();
1369                 }
1370                 if(isset($this->_rf[self::RF_ADAPTER]))
1371                         $this->_rf[self::RF_ADAPTER]->onUnload(null);
1372                 else
1373                         $this->onUnload(null);
1374         }
1375
1376         /**
1377          * This method is invoked when the control enters 'OnInit' stage.
1378          * The method raises 'OnInit' event.
1379          * If you override this method, be sure to call the parent implementation
1380          * so that the event handlers can be invoked.
1381          * @param TEventParameter event parameter to be passed to the event handlers
1382          */
1383         public function onInit($param)
1384         {
1385                 $this->raiseEvent('OnInit',$this,$param);
1386         }
1387
1388         /**
1389          * This method is invoked when the control enters 'OnLoad' stage.
1390          * The method raises 'OnLoad' event.
1391          * If you override this method, be sure to call the parent implementation
1392          * so that the event handlers can be invoked.
1393          * @param TEventParameter event parameter to be passed to the event handlers
1394          */
1395         public function onLoad($param)
1396         {
1397                 $this->raiseEvent('OnLoad',$this,$param);
1398         }
1399
1400         /**
1401          * Raises 'OnDataBinding' event.
1402          * This method is invoked when {@link dataBind} is invoked.
1403          * @param TEventParameter event parameter to be passed to the event handlers
1404          */
1405         public function onDataBinding($param)
1406         {
1407                 Prado::trace("onDataBinding()",'System.Web.UI.TControl');
1408                 $this->raiseEvent('OnDataBinding',$this,$param);
1409         }
1410
1411
1412         /**
1413          * This method is invoked when the control enters 'OnUnload' stage.
1414          * The method raises 'OnUnload' event.
1415          * If you override this method, be sure to call the parent implementation
1416          * so that the event handlers can be invoked.
1417          * @param TEventParameter event parameter to be passed to the event handlers
1418          */
1419         public function onUnload($param)
1420         {
1421                 $this->raiseEvent('OnUnload',$this,$param);
1422         }
1423
1424         /**
1425          * This method is invoked when the control enters 'OnPreRender' stage.
1426          * The method raises 'OnPreRender' event.
1427          * If you override this method, be sure to call the parent implementation
1428          * so that the event handlers can be invoked.
1429          * @param TEventParameter event parameter to be passed to the event handlers
1430          */
1431         public function onPreRender($param)
1432         {
1433                 $this->raiseEvent('OnPreRender',$this,$param);
1434         }
1435
1436         /**
1437          * Invokes the parent's bubbleEvent method.
1438          * A control who wants to bubble an event must call this method in its onEvent method.
1439          * @param TControl sender of the event
1440          * @param TEventParameter event parameter
1441          * @see bubbleEvent
1442          */
1443         protected function raiseBubbleEvent($sender,$param)
1444         {
1445                 $control=$this;
1446                 while($control=$control->_parent)
1447                 {
1448                         if($control->bubbleEvent($sender,$param))
1449                                 break;
1450                 }
1451         }
1452
1453         /**
1454          * This method responds to a bubbled event.
1455          * This method should be overriden to provide customized response to a bubbled event.
1456          * Check the type of event parameter to determine what event is bubbled currently.
1457          * @param TControl sender of the event
1458          * @param TEventParameter event parameters
1459          * @return boolean true if the event bubbling is handled and no more bubbling.
1460          * @see raiseBubbleEvent
1461          */
1462         public function bubbleEvent($sender,$param)
1463         {
1464                 return false;
1465         }
1466
1467         /**
1468          * Broadcasts an event.
1469          * The event will be sent to all controls on the current page hierarchy.
1470          * If a control defines the event, the event will be raised for the control.
1471          * If a control implements {@link IBroadcastEventReceiver}, its
1472          * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} method will
1473          * be invoked which gives the control a chance to respond to the event.
1474          * For example, when broadcasting event 'OnClick', all controls having 'OnClick'
1475          * event will have this event raised, and all controls implementing
1476          * {@link IBroadcastEventReceiver} will also have its
1477          * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()}
1478          * invoked.
1479          * @param string name of the broadcast event
1480          * @param TControl sender of this event
1481          * @param TEventParameter event parameter
1482          */
1483         public function broadcastEvent($name,$sender,$param)
1484         {
1485                 $rootControl=(($page=$this->getPage())===null)?$this:$page;
1486                 $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param));
1487         }
1488
1489         /**
1490          * Recursively broadcasts an event.
1491          * This method should only be used by framework developers.
1492          * @param string name of the broadcast event
1493          * @param TControl sender of the event
1494          * @param TBroadcastEventParameter event parameter
1495          */
1496         private function broadcastEventInternal($name,$sender,$param)
1497         {
1498                 if($this->hasEvent($name))
1499                         $this->raiseEvent($name,$sender,$param->getParameter());
1500                 if($this instanceof IBroadcastEventReceiver)
1501                         $this->broadcastEventReceived($sender,$param);
1502                 if($this->getHasControls())
1503                 {
1504                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1505                         {
1506                                 if($control instanceof TControl)
1507                                         $control->broadcastEventInternal($name,$sender,$param);
1508                         }
1509                 }
1510         }
1511
1512         /**
1513          * Traverse the whole control hierarchy rooted at this control.
1514          * Callback function may be invoked for each control being visited.
1515          * A pre-callback is invoked before traversing child controls;
1516          * A post-callback is invoked after traversing child controls.
1517          * Callback functions can be global functions or class methods.
1518          * They must be of the following signature:
1519          * <code>
1520          * function callback_func($control,$param) {...}
1521          * </code>
1522          * where $control refers to the control being visited and $param
1523          * is the parameter that is passed originally when calling this traverse function.
1524          *
1525          * @param mixed parameter to be passed to callbacks for each control
1526          * @param callback callback invoked before traversing child controls. If null, it is ignored.
1527          * @param callback callback invoked after traversing child controls. If null, it is ignored.
1528          */
1529         protected function traverseChildControls($param,$preCallback=null,$postCallback=null)
1530         {
1531                 if($preCallback!==null)
1532                         call_user_func($preCallback,$this,$param);
1533                 if($this->getHasControls())
1534                 {
1535                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1536                         {
1537                                 if($control instanceof TControl)
1538                                 {
1539                                         $control->traverseChildControls($param,$preCallback,$postCallback);
1540                                 }
1541                         }
1542                 }
1543                 if($postCallback!==null)
1544                         call_user_func($postCallback,$this,$param);
1545         }
1546
1547         /**
1548          * Renders the control.
1549          * Only when the control is visible will the control be rendered.
1550          * @param THtmlWriter the writer used for the rendering purpose
1551          */
1552         public function renderControl($writer)
1553         {
1554                 if($this instanceof IActiveControl || $this->getVisible(false))
1555                 {
1556                         if(isset($this->_rf[self::RF_ADAPTER]))
1557                                 $this->_rf[self::RF_ADAPTER]->render($writer);
1558                         else
1559                                 $this->render($writer);
1560                 }
1561         }
1562
1563         /**
1564          * Renders the control.
1565          * This method is invoked by {@link renderControl} when the control is visible.
1566          * You can override this method to provide customized rendering of the control.
1567          * By default, the control simply renders all its child contents.
1568          * @param THtmlWriter the writer used for the rendering purpose
1569          */
1570         public function render($writer)
1571         {
1572                 $this->renderChildren($writer);
1573         }
1574
1575         /**
1576          * Renders the children of the control.
1577          * This method iterates through all child controls and static text strings
1578          * and renders them in order.
1579          * @param THtmlWriter the writer used for the rendering purpose
1580          */
1581         public function renderChildren($writer)
1582         {
1583                 if($this->getHasControls())
1584                 {
1585                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1586                         {
1587                                 if(is_string($control))
1588                                         $writer->write($control);
1589                                 else if($control instanceof TControl)
1590                                         $control->renderControl($writer);
1591                                 else if($control instanceof IRenderable)
1592                                         $control->render($writer);
1593                         }
1594                 }
1595         }
1596
1597         /**
1598          * This method is invoked when control state is to be saved.
1599          * You can override this method to do last step state saving.
1600          * Parent implementation must be invoked.
1601          */
1602         public function saveState()
1603         {
1604         }
1605
1606         /**
1607          * This method is invoked right after the control has loaded its state.
1608          * You can override this method to initialize data from the control state.
1609          * Parent implementation must be invoked.
1610          */
1611         public function loadState()
1612         {
1613         }
1614
1615         /**
1616          * Loads state (viewstate and controlstate) into a control and its children.
1617          * This method should only be used by framework developers.
1618          * @param array the collection of the state
1619          * @param boolean whether the viewstate should be loaded
1620          */
1621         protected function loadStateRecursive(&$state,$needViewState=true)
1622         {
1623                 if(is_array($state))
1624                 {
1625                         // A null state means the stateful properties all take default values.
1626                         // So if the state is enabled, we have to assign the null value.
1627                         $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE));
1628                         if(isset($state[1]))
1629                         {
1630                                 $this->_rf[self::RF_CONTROLSTATE]=&$state[1];
1631                                 unset($state[1]);
1632                         }
1633                         else
1634                                 unset($this->_rf[self::RF_CONTROLSTATE]);
1635                         if($needViewState)
1636                         {
1637                                 if(isset($state[0]))
1638                                         $this->_viewState=&$state[0];
1639                                 else
1640                                         $this->_viewState=array();
1641                         }
1642                         unset($state[0]);
1643                         if($this->getHasControls())
1644                         {
1645                                 foreach($this->_rf[self::RF_CONTROLS] as $control)
1646                                 {
1647                                         if($control instanceof TControl)
1648                                         {
1649                                                 if(isset($state[$control->_id]))
1650                                                 {
1651                                                         $control->loadStateRecursive($state[$control->_id],$needViewState);
1652                                                         unset($state[$control->_id]);
1653                                                 }
1654                                         }
1655                                 }
1656                         }
1657                         if(!empty($state))
1658                                 $this->_rf[self::RF_CHILD_STATE]=&$state;
1659                 }
1660                 $this->_stage=self::CS_STATE_LOADED;
1661                 if(isset($this->_rf[self::RF_ADAPTER]))
1662                         $this->_rf[self::RF_ADAPTER]->loadState();
1663                 else
1664                         $this->loadState();
1665         }
1666
1667         /**
1668          * Saves all control state (viewstate and controlstate) as a collection.
1669          * This method should only be used by framework developers.
1670          * @param boolean whether the viewstate should be saved
1671          * @return array the collection of the control state (including its children's state).
1672          */
1673         protected function &saveStateRecursive($needViewState=true)
1674         {
1675                 if(isset($this->_rf[self::RF_ADAPTER]))
1676                         $this->_rf[self::RF_ADAPTER]->saveState();
1677                 else
1678                         $this->saveState();
1679                 $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE));
1680                 $state=array();
1681                 if($this->getHasControls())
1682                 {
1683                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1684                         {
1685                                 if($control instanceof TControl)
1686                                 {
1687                                         if(count($tmp = &$control->saveStateRecursive($needViewState)))
1688                                                 $state[$control->_id]=$tmp;
1689                                 }
1690                         }
1691                 }
1692                 if($needViewState && !empty($this->_viewState))
1693                         $state[0]=&$this->_viewState;
1694                 if(isset($this->_rf[self::RF_CONTROLSTATE]))
1695                         $state[1]=&$this->_rf[self::RF_CONTROLSTATE];
1696                 return $state;
1697         }
1698
1699         /**
1700          * Applies a stylesheet skin to a control.
1701          * @param TPage the page containing the control
1702          * @throws TInvalidOperationException if the stylesheet skin is applied already
1703          */
1704         public function applyStyleSheetSkin($page)
1705         {
1706                 if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED))
1707                 {
1708                         $page->applyControlStyleSheet($this);
1709                         $this->_flags |= self::IS_STYLESHEET_APPLIED;
1710                 }
1711                 else if($this->_flags & self::IS_STYLESHEET_APPLIED)
1712                         throw new TInvalidOperationException('control_stylesheet_applied',get_class($this));
1713         }
1714
1715         /**
1716          * Clears the cached UniqueID.
1717          * If $recursive=true, all children's cached UniqueID will be cleared as well.
1718          * @param boolean whether the clearing is recursive.
1719          */
1720         private function clearCachedUniqueID($recursive)
1721         {
1722                 if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS]))
1723                 {
1724                         foreach($this->_rf[self::RF_CONTROLS] as $control)
1725                                 if($control instanceof TControl)
1726                                         $control->clearCachedUniqueID($recursive);
1727                 }
1728                 $this->_uid=null;
1729         }
1730
1731         /**
1732          * Generates an automatic ID for the control.
1733          */
1734         private function generateAutomaticID()
1735         {
1736                 $this->_flags &= ~self::IS_ID_SET;
1737                 if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]))
1738                         $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0;
1739                 $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++;
1740                 $this->_id=self::AUTOMATIC_ID_PREFIX . $id;
1741                 $this->_namingContainer->clearNameTable();
1742         }
1743
1744         /**
1745          * Clears the list of the controls whose IDs are managed by the specified naming container.
1746          */
1747         private function clearNameTable()
1748         {
1749                 unset($this->_rf[self::RF_NAMED_CONTROLS]);
1750         }
1751
1752         /**
1753          * Updates the list of the controls whose IDs are managed by the specified naming container.
1754          * @param TControl the naming container
1755          * @param TControlCollection list of controls
1756          * @throws TInvalidDataValueException if a control's ID is not unique within its naming container.
1757          */
1758         private function fillNameTable($container,$controls)
1759         {
1760                 foreach($controls as $control)
1761                 {
1762                         if($control instanceof TControl)
1763                         {
1764                                 if($control->_id!=='')
1765                                 {
1766                                         if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id]))
1767                                                 throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id);
1768                                         else
1769                                                 $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control;
1770                                 }
1771                                 if(!($control instanceof INamingContainer) && $control->getHasControls())
1772                                         $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]);
1773                         }
1774                 }
1775         }
1776 }
1777
1778
1779 /**
1780  * TControlCollection class
1781  *
1782  * TControlCollection implements a collection that enables
1783  * controls to maintain a list of their child controls.
1784  *
1785  * @author Qiang Xue <qiang.xue@gmail.com>
1786  * @package System.Web.UI
1787  * @since 3.0
1788  */
1789 class TControlCollection extends TList
1790 {
1791         /**
1792          * the control that owns this collection.
1793          * @var TControl
1794          */
1795         private $_o;
1796
1797         /**
1798          * Constructor.
1799          * @param TControl the control that owns this collection.
1800          * @param boolean whether the list is read-only
1801          */
1802         public function __construct(TControl $owner,$readOnly=false)
1803         {
1804                 $this->_o=$owner;
1805                 parent::__construct(null,$readOnly);
1806         }
1807
1808         /**
1809          * @return TControl the control that owns this collection.
1810          */
1811         protected function getOwner()
1812         {
1813                 return $this->_o;
1814         }
1815
1816         /**
1817          * Inserts an item at the specified position.
1818          * This overrides the parent implementation by performing additional
1819          * operations for each newly added child control.
1820          * @param integer the speicified position.
1821          * @param mixed new item
1822          * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl.
1823          */
1824         public function insertAt($index,$item)
1825         {
1826                 if($item instanceof TControl)
1827                 {
1828                         parent::insertAt($index,$item);
1829                         $this->_o->addedControl($item);
1830                 }
1831                 else if(is_string($item) || ($item instanceof IRenderable))
1832                         parent::insertAt($index,$item);
1833                 else
1834                         throw new TInvalidDataTypeException('controlcollection_control_required');
1835         }
1836
1837         /**
1838          * Removes an item at the specified position.
1839          * This overrides the parent implementation by performing additional
1840          * cleanup work when removing a child control.
1841          * @param integer the index of the item to be removed.
1842          * @return mixed the removed item.
1843          */
1844         public function removeAt($index)
1845         {
1846                 $item=parent::removeAt($index);
1847                 if($item instanceof TControl)
1848                         $this->_o->removedControl($item);
1849                 return $item;
1850         }
1851
1852         /**
1853          * Overrides the parent implementation by invoking {@link TControl::clearNamingContainer}
1854          */
1855         public function clear()
1856         {
1857                 parent::clear();
1858                 if($this->_o instanceof INamingContainer)
1859                         $this->_o->clearNamingContainer();
1860         }
1861 }
1862
1863 /**
1864  * TEmptyControlCollection class
1865  *
1866  * TEmptyControlCollection implements an empty control list that prohibits adding
1867  * controls to it. This is useful for controls that do not allow child controls.
1868  *
1869  * @author Qiang Xue <qiang.xue@gmail.com>
1870  * @package System.Web.UI
1871  * @since 3.0
1872  */
1873 class TEmptyControlCollection extends TControlCollection
1874 {
1875         /**
1876          * Constructor.
1877          * @param TControl the control that owns this collection.
1878          */
1879         public function __construct(TControl $owner)
1880         {
1881                 parent::__construct($owner,true);
1882         }
1883
1884         /**
1885          * Inserts an item at the specified position.
1886          * This overrides the parent implementation by ignoring new addition.
1887          * @param integer the speicified position.
1888          * @param mixed new item
1889          */
1890         public function insertAt($index,$item)
1891         {
1892                 if(!is_string($item))  // string is possible if property tag is used. we simply ignore it in this case
1893                         parent::insertAt($index,$item);  // this will generate an exception in parent implementation
1894         }
1895 }
1896
1897 /**
1898  * INamingContainer interface.
1899  * INamingContainer marks a control as a naming container.
1900  *
1901  * @author Qiang Xue <qiang.xue@gmail.com>
1902  * @package System.Web.UI
1903  * @since 3.0
1904  */
1905 interface INamingContainer
1906 {
1907 }
1908
1909 /**
1910  * IPostBackEventHandler interface
1911  *
1912  * If a control wants to respond to postback event, it must implement this interface.
1913  *
1914  * @author Qiang Xue <qiang.xue@gmail.com>
1915  * @package System.Web.UI
1916  * @since 3.0
1917  */
1918 interface IPostBackEventHandler
1919 {
1920         /**
1921          * Raises postback event.
1922          * The implementation of this function should raise appropriate event(s) (e.g. OnClick, OnCommand)
1923          * indicating the component is responsible for the postback event.
1924          * @param string the parameter associated with the postback event
1925          */
1926         public function raisePostBackEvent($param);
1927 }
1928
1929 /**
1930  * IPostBackDataHandler interface
1931  *
1932  * If a control wants to load post data, it must implement this interface.
1933  *
1934  * @author Qiang Xue <qiang.xue@gmail.com>
1935  * @package System.Web.UI
1936  * @since 3.0
1937  */
1938 interface IPostBackDataHandler
1939 {
1940         /**
1941          * Loads user input data.
1942          * The implementation of this function can use $values[$key] to get the user input
1943          * data that are meant for the particular control.
1944          * @param string the key that can be used to retrieve data from the input data collection
1945          * @param array the input data collection
1946          * @return boolean whether the data of the control has been changed
1947          */
1948         public function loadPostData($key,$values);
1949         /**
1950          * Raises postdata changed event.
1951          * The implementation of this function should raise appropriate event(s) (e.g. OnTextChanged)
1952          * indicating the control data is changed.
1953          */
1954         public function raisePostDataChangedEvent();
1955         /**
1956          * @return boolean whether postback causes the data change. Defaults to false for non-postback state.
1957          */
1958         public function getDataChanged();
1959 }
1960
1961
1962 /**
1963  * IValidator interface
1964  *
1965  * If a control wants to validate user input, it must implement this interface.
1966  *
1967  * @author Qiang Xue <qiang.xue@gmail.com>
1968  * @package System.Web.UI
1969  * @since 3.0
1970  */
1971 interface IValidator
1972 {
1973         /**
1974          * Validates certain data.
1975          * The implementation of this function should validate certain data
1976          * (e.g. data entered into TTextBox control).
1977          * @return boolean whether the data passes the validation
1978          */
1979         public function validate();
1980         /**
1981          * @return boolean whether the previous {@link validate()} is successful.
1982          */
1983         public function getIsValid();
1984         /**
1985          * @param boolean whether the validator validates successfully
1986          */
1987         public function setIsValid($value);
1988         /**
1989          * @return string error message during last validate
1990          */
1991         public function getErrorMessage();
1992         /**
1993          * @param string error message for the validation
1994          */
1995         public function setErrorMessage($value);
1996 }
1997
1998
1999 /**
2000  * IValidatable interface
2001  *
2002  * If a control wants to be validated by a validator, it must implement this interface.
2003  *
2004  * @author Qiang Xue <qiang.xue@gmail.com>
2005  * @package System.Web.UI
2006  * @since 3.0
2007  */
2008 interface IValidatable
2009 {
2010         /**
2011          * @return mixed the value of the property to be validated.
2012          */
2013         public function getValidationPropertyValue();
2014         /**
2015          * @return boolean wether this control's validators validated successfully (must default to true)
2016          */
2017         public function getIsValid();
2018         /**
2019          * @return boolean wether this control's validators validated successfully
2020          */
2021         public function setIsValid($value);
2022 }
2023
2024 /**
2025  * IBroadcastEventReceiver interface
2026  *
2027  * If a control wants to check broadcast event, it must implement this interface.
2028  *
2029  * @author Qiang Xue <qiang.xue@gmail.com>
2030  * @package System.Web.UI
2031  * @since 3.0
2032  */
2033 interface IBroadcastEventReceiver
2034 {
2035         /**
2036          * Handles broadcast event.
2037          * This method is invoked automatically when an event is broadcasted.
2038          * Within this method, you may check the event name given in
2039          * the event parameter to determine  whether you should respond to
2040          * this event.
2041          * @param TControl sender of the event
2042          * @param TBroadCastEventParameter event parameter
2043          */
2044         public function broadcastEventReceived($sender,$param);
2045 }
2046
2047 /**
2048  * ITheme interface.
2049  *
2050  * This interface must be implemented by theme.
2051  *
2052  * @author Qiang Xue <qiang.xue@gmail.com>
2053  * @package System.Web.UI
2054  * @since 3.0
2055  */
2056 interface ITheme
2057 {
2058         /**
2059          * Applies this theme to the specified control.
2060          * @param TControl the control to be applied with this theme
2061          */
2062         public function applySkin($control);
2063 }
2064
2065 /**
2066  * ITemplate interface
2067  *
2068  * ITemplate specifies the interface for classes encapsulating
2069  * parsed template structures.
2070  *
2071  * @author Qiang Xue <qiang.xue@gmail.com>
2072  * @package System.Web.UI
2073  * @since 3.0
2074  */
2075 interface ITemplate
2076 {
2077         /**
2078          * Instantiates the template.
2079          * Content in the template will be instantiated as components and text strings
2080          * and passed to the specified parent control.
2081          * @param TControl the parent control
2082          */
2083         public function instantiateIn($parent);
2084 }
2085
2086 /**
2087  * IButtonControl interface
2088  *
2089  * IButtonControl specifies the common properties and events that must
2090  * be implemented by a button control, such as {@link TButton}, {@link TLinkButton},
2091  * {@link TImageButton}.
2092  *
2093  * @author Qiang Xue <qiang.xue@gmail.com>
2094  * @package System.Web.UI
2095  * @since 3.0
2096  */
2097 interface IButtonControl
2098 {
2099         /**
2100          * @return string caption of the button
2101          */
2102         public function getText();
2103
2104         /**
2105          * @param string caption of the button
2106          */
2107         public function setText($value);
2108
2109         /**
2110          * @return boolean whether postback event trigger by this button will cause input validation
2111          */
2112         public function getCausesValidation();
2113
2114         /**
2115          * @param boolean whether postback event trigger by this button will cause input validation
2116          */
2117         public function setCausesValidation($value);
2118
2119         /**
2120          * @return string the command name associated with the {@link onCommand OnCommand} event.
2121          */
2122         public function getCommandName();
2123
2124         /**
2125          * @param string the command name associated with the {@link onCommand OnCommand} event.
2126          */
2127         public function setCommandName($value);
2128
2129         /**
2130          * @return string the parameter associated with the {@link onCommand OnCommand} event
2131          */
2132         public function getCommandParameter();
2133
2134         /**
2135          * @param string the parameter associated with the {@link onCommand OnCommand} event.
2136          */
2137         public function setCommandParameter($value);
2138
2139         /**
2140          * @return string the group of validators which the button causes validation upon postback
2141          */
2142         public function getValidationGroup();
2143
2144         /**
2145          * @param string the group of validators which the button causes validation upon postback
2146          */
2147         public function setValidationGroup($value);
2148
2149         /**
2150          * Raises <b>OnClick</b> event.
2151          * @param TEventParameter event parameter to be passed to the event handlers
2152          */
2153         public function onClick($param);
2154
2155         /**
2156          * Raises <b>OnCommand</b> event.
2157          * @param TCommandEventParameter event parameter to be passed to the event handlers
2158          */
2159         public function onCommand($param);
2160
2161         /**
2162          * @param boolean set by a panel to register this button as the default button for the panel.
2163          */
2164         public function setIsDefaultButton($value);
2165
2166         /**
2167          * @return boolean true if this button is registered as a default button for a panel.
2168          */
2169         public function getIsDefaultButton();
2170 }
2171
2172 /**
2173  * ISurroundable interface
2174  *
2175  * Identifies controls that may create an additional surrounding tag. The id of the
2176  * tag can be obtained with {@link getSurroundingTagID}, the tag used to render the
2177  * surrounding container is obtained by {@link getSurroundingTag}.
2178  *
2179  * @package System.Web.UI
2180  * @since 3.1.2
2181  */
2182 interface ISurroundable
2183 {
2184         /**
2185          * @return string the tag used to wrap the control in (if surrounding is needed).
2186          */
2187         public function getSurroundingTag();
2188
2189         /**
2190          * @return string the id of the embedding tag of the control or the control's clientID if not surrounded.
2191          */
2192         public function getSurroundingTagID();
2193 }
2194
2195 /**
2196  * TBroadcastEventParameter class
2197  *
2198  * TBroadcastEventParameter encapsulates the parameter data for
2199  * events that are broadcasted. The name of of the event is specified via
2200  * {@link setName Name} property while the event parameter is via
2201  * {@link setParameter Parameter} property.
2202  *
2203  * @author Qiang Xue <qiang.xue@gmail.com>
2204  * @package System.Web.UI
2205  * @since 3.0
2206  */
2207 class TBroadcastEventParameter extends TEventParameter
2208 {
2209         private $_name;
2210         private $_param;
2211
2212         /**
2213          * Constructor.
2214          * @param string name of the broadcast event
2215          * @param mixed parameter of the broadcast event
2216          */
2217         public function __construct($name='',$parameter=null)
2218         {
2219                 $this->_name=$name;
2220                 $this->_param=$parameter;
2221         }
2222
2223         /**
2224          * @return string name of the broadcast event
2225          */
2226         public function getName()
2227         {
2228                 return $this->_name;
2229         }
2230
2231         /**
2232          * @param string name of the broadcast event
2233          */
2234         public function setName($value)
2235         {
2236                 $this->_name=$value;
2237         }
2238
2239         /**
2240          * @return mixed parameter of the broadcast event
2241          */
2242         public function getParameter()
2243         {
2244                 return $this->_param;
2245         }
2246
2247         /**
2248          * @param mixed parameter of the broadcast event
2249          */
2250         public function setParameter($value)
2251         {
2252                 $this->_param=$value;
2253         }
2254 }
2255
2256 /**
2257  * TCommandEventParameter class
2258  *
2259  * TCommandEventParameter encapsulates the parameter data for <b>Command</b>
2260  * event of button controls. You can access the name of the command via
2261  * {@link getCommandName CommandName} property, and the parameter carried
2262  * with the command via {@link getCommandParameter CommandParameter} property.
2263  *
2264  * @author Qiang Xue <qiang.xue@gmail.com>
2265  * @package System.Web.UI
2266  * @since 3.0
2267  */
2268 class TCommandEventParameter extends TEventParameter
2269 {
2270         private $_name;
2271         private $_param;
2272
2273         /**
2274          * Constructor.
2275          * @param string name of the command
2276          * @param string parameter of the command
2277          */
2278         public function __construct($name='',$parameter='')
2279         {
2280                 $this->_name=$name;
2281                 $this->_param=$parameter;
2282         }
2283
2284         /**
2285          * @return string name of the command
2286          */
2287         public function getCommandName()
2288         {
2289                 return $this->_name;
2290         }
2291
2292         /**
2293          * @return string parameter of the command
2294          */
2295         public function getCommandParameter()
2296         {
2297                 return $this->_param;
2298         }
2299 }
2300
2301
2302 /**
2303  * TCompositeLiteral class
2304  *
2305  * TCompositeLiteral is used internally by {@link TTemplate} for representing
2306  * consecutive static strings, expressions and statements.
2307  *
2308  * @author Qiang Xue <qiang.xue@gmail.com>
2309  * @package System.Web.UI
2310  * @since 3.0
2311  */
2312 class TCompositeLiteral extends TComponent implements IRenderable, IBindable
2313 {
2314         const TYPE_EXPRESSION=0;
2315         const TYPE_STATEMENTS=1;
2316         const TYPE_DATABINDING=2;
2317         private $_container=null;
2318         private $_items=array();
2319         private $_expressions=array();
2320         private $_statements=array();
2321         private $_bindings=array();
2322
2323         /**
2324          * Constructor.
2325          * @param array list of items to be represented by TCompositeLiteral
2326          */
2327         public function __construct($items)
2328         {
2329                 $this->_items=array();
2330                 $this->_expressions=array();
2331                 $this->_statements=array();
2332                 foreach($items as $id=>$item)
2333                 {
2334                         if(is_array($item))
2335                         {
2336                                 if($item[0]===self::TYPE_EXPRESSION)
2337                                         $this->_expressions[$id]=$item[1];
2338                                 else if($item[0]===self::TYPE_STATEMENTS)
2339                                         $this->_statements[$id]=$item[1];
2340                                 else if($item[0]===self::TYPE_DATABINDING)
2341                                         $this->_bindings[$id]=$item[1];
2342                                 $this->_items[$id]='';
2343                         }
2344                         else
2345                                 $this->_items[$id]=$item;
2346                 }
2347         }
2348
2349         /**
2350          * @return TComponent container of this component. It serves as the evaluation context of expressions and statements.
2351          */
2352         public function getContainer()
2353         {
2354                 return $this->_container;
2355         }
2356
2357         /**
2358          * @param TComponent container of this component. It serves as the evaluation context of expressions and statements.
2359          */
2360         public function setContainer(TComponent $value)
2361         {
2362                 $this->_container=$value;
2363         }
2364
2365         /**
2366          * Evaluates the expressions and/or statements in the component.
2367          */
2368         public function evaluateDynamicContent()
2369         {
2370                 $context=$this->_container===null?$this:$this->_container;
2371                 foreach($this->_expressions as $id=>$expression)
2372                         $this->_items[$id]=$context->evaluateExpression($expression);
2373                 foreach($this->_statements as $id=>$statement)
2374                         $this->_items[$id]=$context->evaluateStatements($statement);
2375         }
2376
2377         /**
2378          * Performs databindings.
2379          * This method is required by {@link IBindable}
2380          */
2381         public function dataBind()
2382         {
2383                 $context=$this->_container===null?$this:$this->_container;
2384                 foreach($this->_bindings as $id=>$binding)
2385                         $this->_items[$id]=$context->evaluateExpression($binding);
2386         }
2387
2388         /**
2389          * Renders the content stored in this component.
2390          * This method is required by {@link IRenderable}
2391          * @param ITextWriter
2392          */
2393         public function render($writer)
2394         {
2395                 $writer->write(implode('',$this->_items));
2396         }
2397 }
2398