root/trunk/framework/Web/UI/WebControls/TTabPanel.php

Revision 2197 (checked in by xue, 11 months ago)

rearranged js location.

  • Property svn:keywords set to Id
Line 
1 <?php
2 /**
3  * TTabPanel class file.
4  *
5  * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
6  * @link http://www.pradosoft.com/
7  * @copyright Copyright &copy; 2005-2007 PradoSoft
8  * @license http://www.pradosoft.com/license/
9  * @version $Id$
10  * @package System.Web.UI.WebControls
11  * @since 3.1.1
12  */
13
14 /**
15  * Class TTabPanel.
16  *
17  * TTabPanel displays a tabbed panel. Users can click on the tab bar to switching among
18  * different tab views. Each tab view is an independent panel that can contain arbitrary content.
19  *
20  * A TTabPanel control consists of one or several {@link TTabView} controls representing the possible
21  * tab views. At any time, only one tab view is visible (active), which is specified by any of
22  * the following properties:
23  * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection.
24  * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view.
25  * - {@link setActiveView ActiveView} - the visible view instance.
26  * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID}
27  * are set, the latter takes precedence.
28  *
29  * TTabPanel uses CSS to specify the appearance of the tab bar and panel. By default,
30  * an embedded CSS file will be published which contains the default CSS for TTabPanel.
31  * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property.
32  * The following properties specify the CSS classes used for elements in a TTabPanel:
33  * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'tab-panel');
34  * - {@link setTabCssClass TabCssClass} - the CSS class name for nonactive tab div elements (defaults to 'tab-normal');
35  * - {@link setActiveTabCssClass ActiveTabCssClass} - the CSS class name for the active tab div element (defaults to 'tab-active');
36  * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'tab-view');
37  *
38  * To use TTabPanel, write a template like following:
39  * <code>
40  * <com:TTabPanel>
41  *   <com:TTabView Caption="View 1">
42  *     content for view 1
43  *   </com:TTabView>
44  *   <com:TTabView Caption="View 2">
45  *     content for view 2
46  *   </com:TTabView>
47  *   <com:TTabView Caption="View 3">
48  *     content for view 3
49  *   </com:TTabView>
50  * </com:TTabPanel>
51  * </code>
52  *
53  * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
54  * @version $Id$
55  * @since 3.1.1
56  */
57 class TTabPanel extends TWebControl implements IPostBackDataHandler
58 {
59     private $_dataChanged=false;
60
61     /**
62      * @return string tag name for the control
63      */
64     protected function getTagName()
65     {
66         return 'div';
67     }
68
69     /**
70      * Adds object parsed from template to the control.
71      * This method adds only {@link TTabView} objects into the {@link getViews Views} collection.
72      * All other objects are ignored.
73      * @param mixed object parsed from template
74      */
75     public function addParsedObject($object)
76     {
77         if($object instanceof TTabView)
78             $this->getControls()->add($object);
79     }
80
81     /**
82      * Returns the index of the active tab view.
83      * Note, this property may not return the correct index.
84      * To ensure the correctness, call {@link getActiveView()} first.
85      * @return integer the zero-based index of the active tab view. If -1, it means no active tab view. Default is 0 (the first view is active).
86      */
87     public function getActiveViewIndex()
88     {
89         return $this->getViewState('ActiveViewIndex',0);
90     }
91
92     /**
93      * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
94      * @throws TInvalidDataValueException if the view index is invalid
95      */
96     public function setActiveViewIndex($value)
97     {
98         $this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0);
99     }
100
101     /**
102      * Returns the ID of the active tab view.
103      * Note, this property may not return the correct ID.
104      * To ensure the correctness, call {@link getActiveView()} first.
105      * @return string The ID of the active tab view. Defaults to '', meaning not set.
106      */
107     public function getActiveViewID()
108     {
109         return $this->getViewState('ActiveViewID','');
110     }
111
112     /**
113      * @param string The ID of the active tab view.
114      */
115     public function setActiveViewID($value)
116     {
117         $this->setViewState('ActiveViewID',$value,'');
118     }
119
120     /**
121      * Returns the currently active view.
122      * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to
123      * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly.
124      * @return TTabView the currently active view, null if no active view
125      * @throws TInvalidDataValueException if the active view ID or index set previously is invalid
126      */
127     public function getActiveView()
128     {
129         $activeView=null;
130         $views=$this->getViews();
131         if(($id=$this->getActiveViewID())!=='')
132         {
133             if(($index=$views->findIndexByID($id))>=0)
134                 $activeView=$views->itemAt($index);
135             else
136                 throw new TInvalidDataValueException('tabpanel_activeviewid_invalid',$id);
137         }
138         else if(($index=$this->getActiveViewIndex())>=0)
139         {
140             if($index<$views->getCount())
141                 $activeView=$views->itemAt($index);
142             else
143                 throw new TInvalidDataValueException('tabpanel_activeviewindex_invalid',$index);
144         }
145         else
146         {
147             foreach($views as $index=>$view)
148             {
149                 if($view->getActive())
150                 {
151                     $activeView=$view;
152                     break;
153                 }
154             }
155         }
156         if($activeView!==null)
157             $this->activateView($activeView);
158         return $activeView;
159     }
160
161     /**
162      * @param TTabView the view to be activated
163      * @throws TInvalidOperationException if the view is not in the view collection
164      */
165     public function setActiveView($view)
166     {
167         if($this->getViews()->indexOf($view)>=0)
168             $this->activateView($view);
169         else
170             throw new TInvalidOperationException('tabpanel_view_inexistent');
171     }
172
173     /**
174      * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''.
175      */
176     public function getCssUrl()
177     {
178         return $this->getViewState('CssUrl','');
179     }
180
181     /**
182      * @param string URL for the CSS file including all relevant CSS class definitions.
183      */
184     public function setCssUrl($value)
185     {
186         $this->setViewState('CssUrl',TPropertyValue::ensureString($value),'');
187     }
188
189     /**
190      * @return string CSS class for the whole tab control div. Defaults to 'tab-panel'.
191      */
192     public function getCssClass()
193     {
194         $cssClass=parent::getCssClass();
195         return $cssClass===''?'tab-panel':$cssClass;
196     }
197
198     /**
199      * @return string CSS class for the currently displayed view div. Defaults to 'tab-view'.
200      */
201     public function getViewCssClass()
202     {
203         return $this->getViewStyle()->getCssClass();
204     }
205
206     /**
207      * @param string CSS class for the currently displayed view div.
208      */
209     public function setViewCssClass($value)
210     {
211         $this->getViewStyle()->setCssClass($value);
212     }
213
214     /**
215      * @return TStyle the style for all the view div
216      */
217     public function getViewStyle()
218     {
219         if(($style=$this->getViewState('ViewStyle',null))===null)
220         {
221             $style=new TStyle;
222             $style->setCssClass('tab-view');
223             $this->setViewState('ViewStyle',$style,null);
224         }
225         return $style;
226     }
227
228     /**
229      * @return string CSS class for non-active tabs. Defaults to 'tab-normal'.
230      */
231     public function getTabCssClass()
232     {
233         return $this->getTabStyle()->getCssClass();
234     }
235
236     /**
237      * @param string CSS class for non-active tabs.
238      */
239     public function setTabCssClass($value)
240     {
241         $this->getTabStyle()->setCssClass($value);
242     }
243
244     /**
245      * @return TStyle the style for all the inactive tab div
246      */
247     public function getTabStyle()
248     {
249         if(($style=$this->getViewState('TabStyle',null))===null)
250         {
251             $style=new TStyle;
252             $style->setCssClass('tab-normal');
253             $this->setViewState('TabStyle',$style,null);
254         }
255         return $style;
256     }
257
258     /**
259      * @return string CSS class for the active tab. Defaults to 'tab-active'.
260      */
261     public function getActiveTabCssClass()
262     {
263         return $this->getActiveTabStyle()->getCssClass();
264     }
265
266     /**
267      * @param string CSS class for the active tab.
268      */
269     public function setActiveTabCssClass($value)
270     {
271         $this->getActiveTabStyle()->setCssClass($value);
272     }
273
274     /**
275      * @return TStyle the style for the active tab div
276      */
277     public function getActiveTabStyle()
278     {
279         if(($style=$this->getViewState('ActiveTabStyle',null))===null)
280         {
281             $style=new TStyle;
282             $style->setCssClass('tab-active');
283             $this->setViewState('ActiveTabStyle',$style,null);
284         }
285         return $style;
286     }
287
288     /**
289      * Activates the specified view.
290      * If there is any other view currently active, it will be deactivated.
291      * @param TTabView the view to be activated. If null, all views will be deactivated.
292      */
293     protected function activateView($view)
294     {
295         $this->setActiveViewIndex(-1);
296         $this->setActiveViewID('');
297         foreach($this->getViews() as $index=>$v)
298         {
299             if($view===$v)
300             {
301                 $this->setActiveViewIndex($index);
302                 $this->setActiveViewID($view->getID(false));
303                 $view->setActive(true);
304             }
305             else
306                 $v->setActive(false);
307         }
308     }
309
310     /**
311      * Loads user input data.
312      * This method is primarly used by framework developers.
313      * @param string the key that can be used to retrieve data from the input data collection
314      * @param array the input data collection
315      * @return boolean whether the data of the control has been changed
316      */
317     public function loadPostData($key,$values)
318     {
319         if(($index=$values[$this->getClientID().'_1'])!==null)
320         {
321             $index=(int)$index;
322             $currentIndex=$this->getActiveViewIndex();
323             if($currentIndex!==$index)
324             {
325                 $this->setActiveViewID(''); // clear up view ID
326                 $this->setActiveViewIndex($index);
327                 return $this->_dataChanged=true;
328             }
329         }
330         return false;
331     }
332
333     /**
334      * Raises postdata changed event.
335      * This method is required by {@link IPostBackDataHandler} interface.
336      * It is invoked by the framework when {@link getActiveViewIndex ActiveViewIndex} property
337      * is changed on postback.
338      * This method is primarly used by framework developers.
339      */
340     public function raisePostDataChangedEvent()
341     {
342         // do nothing
343     }
344
345     /**
346      * Returns a value indicating whether postback has caused the control data change.
347      * This method is required by the IPostBackDataHandler interface.
348      * @return boolean whether postback has caused the control data change. False if the page is not in postback mode.
349      */
350     public function getDataChanged()
351     {
352         return $this->_dataChanged;
353     }
354
355     /**
356      * Adds attributes to renderer.
357      * @param THtmlWriter the renderer
358      */
359     protected function addAttributesToRender($writer)
360     {
361         $writer->addAttribute('id',$this->getClientID());
362         $this->setCssClass($this->getCssClass());
363         parent::addAttributesToRender($writer);
364     }
365
366     /**
367      * Registers CSS and JS.
368      * This method is invoked right before the control rendering, if the control is visible.
369      * @param mixed event parameter
370      */
371     public function onPreRender($param)
372     {
373         parent::onPreRender($param);
374         $this->getActiveView();  // determine the active view
375         $this->registerStyleSheet();
376         $this->registerClientScript();
377     }
378
379     /**
380      * Registers the CSS relevant to the TTabControl.
381      * It will register the CSS file specified by {@link getCssUrl CssUrl}.
382      * If that is not set, it will use the default CSS.
383      */
384     protected function registerStyleSheet()
385     {
386         if(($url=$this->getCssUrl())==='')
387             $url=$this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'tabpanel.css');
388         $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
389     }
390
391     /**
392      * Registers the relevant JavaScript.
393      */
394     protected function registerClientScript()
395     {
396         $id=$this->getClientID();
397         $options=TJavaScript::encode($this->getClientOptions());
398         $className=$this->getClientClassName();
399         $page=$this->getPage();
400         $cs=$page->getClientScript();
401         $cs->registerPradoScript('tabpanel');
402         $code="new $className($options);";
403         $cs->registerEndScript("prado:$id", $code);
404         $cs->registerHiddenField($id.'_1',$this->getActiveViewIndex());
405         $page->registerRequiresPostData($this);
406     }
407
408     /**
409      * Gets the name of the javascript class responsible for performing postback for this control.
410      * This method overrides the parent implementation.
411      * @return string the javascript class name
412      */
413     protected function getClientClassName()
414     {
415         return 'Prado.WebUI.TTabPanel';
416     }
417
418     /**
419      * @return array the options for JavaScript
420      */
421     protected function getClientOptions()
422     {
423         $options['ID']=$this->getClientID();
424         $options['ActiveCssClass']=$this->getActiveTabCssClass();
425         $options['NormalCssClass']=$this->getTabCssClass();
426         $viewIDs=array();
427         foreach($this->getViews() as $view)
428         {
429             if($view->getVisible())
430                 $viewIDs[]=$view->getClientID();
431         }
432         $options['Views']='[\''.implode('\',\'',$viewIDs).'\']';
433
434         return $options;
435     }
436
437     /**
438      * Creates a control collection object that is to be used to hold child controls
439      * @return TTabViewCollection control collection
440      */
441     protected function createControlCollection()
442     {
443         return new TTabViewCollection($this);
444     }
445
446     /**
447      * @return TTabViewCollection list of {@link TTabView} controls
448      */
449     public function getViews()
450     {
451         return $this->getControls();
452     }
453
454     /**
455      * Renders body contents of the tab control.
456      * @param THtmlWriter the writer used for the rendering purpose.
457      */
458     public function renderContents($writer)
459     {
460         $views=$this->getViews();
461         if($views->getCount()>0)
462         {
463             $writer->writeLine();
464             // render tab bar
465             foreach($views as $view)
466             {
467                 $view->renderTab($writer);
468                 $writer->writeLine();
469             }
470             // render tab views
471             foreach($views as $view)
472             {
473                 $view->renderControl($writer);
474                 $writer->writeLine();
475             }
476         }
477     }
478 }
479
480 /**
481  * TTabView class.
482  *
483  * TTabView represents a view in a {@link TTabPanel} control.
484  *
485  * The content in a TTabView can be specified by the {@link setText Text} property
486  * or its child controls. In template syntax, the latter means enclosing the content
487  * within the TTabView component element. If both are set, {@link getText Text} takes precedence.
488  *
489  * Each TTabView is associated with a tab in the tab bar of the TTabPanel control.
490  * The tab caption is specified by {@link setCaption Caption}. If {@link setNavigateUrl NavigateUrl}
491  * is set, the tab will contain a hyperlink pointing to the specified URL. In this case,
492  * clicking on the tab will redirect the browser to the specified URL.
493  *
494  * TTabView may be toggled between visible (active) and invisible (inactive) by
495  * setting the {@link setActive Active} property.
496  *
497  * @author Tomasz Wolny <tomasz.wolny@polecam.to.pl> and Qiang Xue <qiang.xue@gmail.com>
498  * @version $Id$
499  * @since 3.1.1
500  */
501 class TTabView extends TWebControl
502 {
503     private $_active=false;
504
505     /**
506      * @return the tag name for the view element
507      */
508     protected function getTagName()
509     {
510         return 'div';
511     }
512
513     /**
514      * Adds attributes to renderer.
515      * @param THtmlWriter the renderer
516      */
517     protected function addAttributesToRender($writer)
518     {
519         if(!$this->getActive() && $this->getPage()->getClientSupportsJavaScript())
520             $this->getStyle()->setStyleField('display','none');
521
522         $this->getStyle()->mergeWith($this->getParent()->getViewStyle());
523
524         parent::addAttributesToRender($writer);
525
526         $writer->addAttribute('id',$this->getClientID());
527     }
528
529     /**
530      * @return string the caption displayed on this tab. Defaults to ''.
531      */
532     public function getCaption()
533     {
534         return $this->getViewState('Caption','');
535     }
536
537     /**
538      * @param string the caption displayed on this tab
539      */
540     public function setCaption($value)
541     {
542         $this->setViewState('Caption',TPropertyValue::ensureString($value),'');
543     }
544
545     /**
546      * @return string the URL of the target page. Defaults to ''.
547      */
548     public function getNavigateUrl()
549     {
550         return $this->getViewState('NavigateUrl','');
551     }
552
553     /**
554      * Sets the URL of the target page.
555      * If not empty, clicking on this tab will redirect the browser to the specified URL.
556      * @param string the URL of the target page.
557      */
558     public function setNavigateUrl($value)
559     {
560         $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),'');
561     }
562
563     /**
564      * @return string the text content displayed on this view. Defaults to ''.
565      */
566     public function getText()
567     {
568         return $this->getViewState('Text','');
569     }
570
571     /**
572      * Sets the text content to be displayed on this view.
573      * If this is not empty, the child content of the view will be ignored.
574      * @param string the text content displayed on this view
575      */
576     public function setText($value)
577     {
578         $this->setViewState('Text',TPropertyValue::ensureString($value),'');
579     }
580
581     /**
582      * @return boolean whether this tab view is active. Defaults to false.
583      */
584     public function getActive()
585     {
586         return $this->_active;
587     }
588
589     /**
590      * @param boolean whether this tab view is active.
591      */
592     public function setActive($value)
593     {
594         $this->_active=TPropertyValue::ensureBoolean($value);
595     }
596
597     /**
598      * Renders body contents of the tab view.
599      * @param THtmlWriter the writer used for the rendering purpose.
600      */
601     public function renderContents($writer)
602     {
603         if(($text=$this->getText())!=='')
604             $writer->write($text);
605         else if($this->getHasControls())
606             parent::renderContents($writer);
607     }
608
609     /**
610      * Renders the tab associated with the tab view.
611      * @param THtmlWriter the writer for rendering purpose.
612      */
613     public function renderTab($writer)
614     {
615         if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript())
616         {
617             $writer->addAttribute('id',$this->getClientID().'_0');