]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/UI/WebControls/TAccordion.php
baculum: New Baculum API and Baculum Web
[bacula/bacula] / gui / baculum / framework / Web / UI / WebControls / TAccordion.php
1 <?php
2 /**
3  * TAccordion class file.
4  *
5  * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
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.WebControls
10  * @since 3.2
11  */
12
13 /**
14  * Class TAccordion.
15  *
16  * TAccordion displays an accordion control. Users can click on the view headers to switch among
17  * different accordion views. Each accordion view is an independent panel that can contain arbitrary content.
18  *
19  * A TAccordion control consists of one or several {@link TAccordionView} controls representing the possible
20  * accordion views. At any time, only one accordion view is visible (active), which is specified by any of
21  * the following properties:
22  * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection.
23  * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view.
24  * - {@link setActiveView ActiveView} - the visible view instance.
25  * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID}
26  * are set, the latter takes precedence.
27  *
28  * TAccordion uses CSS to specify the appearance of the accordion headers and panel. By default,
29  * an embedded CSS file will be published which contains the default CSS for TTabPanel.
30  * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property.
31  * The following properties specify the CSS classes used for elements in a TAccordion:
32  * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'accordion');
33  * - {@link setHeaderCssClass HeaderCssClass} - the CSS class name for nonactive accordion div elements (defaults to 'accordion-header');
34  * - {@link setActiveHeaderCssClass ActiveHeaderCssClass} - the CSS class name for the active accordion div element (defaults to 'accordion-header-active');
35  * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'accordion-view');
36  *
37  * When the user clicks on a view header, the switch between the old visible view and the clicked one is animated.
38  * You can use the {@link setAnimationDuration AnimationDuration} property to set the animation length in seconds;
39  * it defaults to 1 second, and when set to 0 it will produce an immediate switch with no animation.
40  *
41  * The TAccordion auto-sizes itself to the largest of all views, so it can encompass all of them without scrolling.
42  * If you want to specify a fixed height (in pixels), use the {@link setViewHeight ViewHeight} property.
43  * When a TAccordion is nested inside another, it's adviced to manually specify a {@link setViewHeight ViewHeight} for the internal TAccordion
44  *
45  * To use TAccordion, write a template like following:
46  * <code>
47  * <com:TAccordion>
48  *   <com:TAccordionView Caption="View 1">
49  *     content for view 1
50  *   </com:TAccordionView>
51  *   <com:TAccordionView Caption="View 2">
52  *     content for view 2
53  *   </com:TAccordionView>
54  *   <com:TAccordionView Caption="View 3">
55  *     content for view 3
56  *   </com:TAccordionView>
57  * </com:TAccordion>
58  * </code>
59  *
60  * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
61  * @package System.Web.UI.WebControls
62  * @since 3.2
63  */
64
65 class TAccordion extends TWebControl implements IPostBackDataHandler
66 {
67         private $_dataChanged=false;
68
69         /**
70          * @return string tag name for the control
71          */
72         protected function getTagName()
73         {
74                 return 'div';
75         }
76
77         /**
78          * Adds object parsed from template to the control.
79          * This method adds only {@link TAccordionView} objects into the {@link getViews Views} collection.
80          * All other objects are ignored.
81          * @param mixed object parsed from template
82          */
83         public function addParsedObject($object)
84         {
85                 if($object instanceof TAccordionView)
86                         $this->getControls()->add($object);
87         }
88
89         /**
90          * Returns the index of the active accordion view.
91          * Note, this property may not return the correct index.
92          * To ensure the correctness, call {@link getActiveView()} first.
93          * @return integer the zero-based index of the active accordion view. If -1, it means no active accordion view. Default is 0 (the first view is active).
94          */
95         public function getActiveViewIndex()
96         {
97                 return $this->getViewState('ActiveViewIndex',0);
98         }
99
100         /**
101          * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
102          * @throws TInvalidDataValueException if the view index is invalid
103          */
104         public function setActiveViewIndex($value)
105         {
106                 $this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0);
107                 $this->setActiveViewID('');
108         }
109
110         /**
111          * Returns the ID of the active accordion view.
112          * Note, this property may not return the correct ID.
113          * To ensure the correctness, call {@link getActiveView()} first.
114          * @return string The ID of the active accordion view. Defaults to '', meaning not set.
115          */
116         public function getActiveViewID()
117         {
118                 return $this->getViewState('ActiveViewID','');
119         }
120
121         /**
122          * @param string The ID of the active accordion view.
123          */
124         public function setActiveViewID($value)
125         {
126                 $this->setViewState('ActiveViewID',$value,'');
127         }
128
129         /**
130          * Returns the currently active view.
131          * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to
132          * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly.
133          * @return TAccordionView the currently active view, null if no active view
134          * @throws TInvalidDataValueException if the active view ID or index set previously is invalid
135          */
136         public function getActiveView()
137         {
138                 $activeView=null;
139                 $views=$this->getViews();
140                 if(($id=$this->getActiveViewID())!=='')
141                 {
142                         if(($index=$views->findIndexByID($id))>=0)
143                                 $activeView=$views->itemAt($index);
144                         else
145                                 throw new TInvalidDataValueException('accordion_activeviewid_invalid',$id);
146                 }
147                 else if(($index=$this->getActiveViewIndex())>=0)
148                 {
149                         if($index<$views->getCount())
150                                 $activeView=$views->itemAt($index);
151                         else
152                                 throw new TInvalidDataValueException('accordion_activeviewindex_invalid',$index);
153                 }
154                 else
155                 {
156                         foreach($views as $index=>$view)
157                         {
158                                 if($view->getActive())
159                                 {
160                                         $activeView=$view;
161                                         break;
162                                 }
163                         }
164                 }
165                 if($activeView!==null)
166                         $this->activateView($activeView);
167                 return $activeView;
168         }
169
170         /**
171          * @param TAccordionView the view to be activated
172          * @throws TInvalidOperationException if the view is not in the view collection
173          */
174         public function setActiveView($view)
175         {
176                 if($this->getViews()->indexOf($view)>=0)
177                         $this->activateView($view);
178                 else
179                         throw new TInvalidOperationException('accordion_view_inexistent');
180         }
181
182         /**
183         * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''.
184         */
185         public function getCssUrl()
186         {
187                 return $this->getViewState('CssUrl','default');
188         }
189
190         /**
191         * @param string URL for the CSS file including all relevant CSS class definitions.
192         */
193         public function setCssUrl($value)
194         {
195                 $this->setViewState('CssUrl',TPropertyValue::ensureString($value),'');
196         }
197
198         /**
199                 * @return string CSS class for the whole accordion control div.
200         */
201         public function getCssClass()
202         {
203                 $cssClass=parent::getCssClass();
204                         return $cssClass===''?'accordion':$cssClass;
205         }
206
207         /**
208                 * @return string CSS class for the currently displayed view div. Defaults to 'accordion-view'.
209         */
210         public function getViewCssClass()
211         {
212                 return $this->getViewStyle()->getCssClass();
213         }
214
215         /**
216         * @param string CSS class for the currently displayed view div.
217         */
218         public function setViewCssClass($value)
219         {
220                 $this->getViewStyle()->setCssClass($value);
221         }
222
223         /**
224                 * @return string CSS class for the currently displayed view div. Defaults to 'accordion-view'.
225         */
226         public function getAnimationDuration()
227         {
228                 return $this->getViewState('AnimationDuration','1');
229         }
230
231         /**
232         * @param string CSS class for the currently displayed view div.
233         */
234         public function setAnimationDuration($value)
235         {
236                 $this->setViewState('AnimationDuration',$value);
237         }
238
239         /**
240          * @return TStyle the style for all the view div
241          */
242         public function getViewStyle()
243         {
244                 if(($style=$this->getViewState('ViewStyle',null))===null)
245                 {
246                         $style=new TStyle;
247                         $style->setCssClass('accordion-view');
248                         $this->setViewState('ViewStyle',$style,null);
249                 }
250                 return $style;
251         }
252
253         /**
254          * @return string CSS class for view headers. Defaults to 'accordion-header'.
255          */
256         public function getHeaderCssClass()
257         {
258                 return $this->getHeaderStyle()->getCssClass();
259         }
260
261         /**
262          * @param string CSS class for view headers.
263          */
264         public function setHeaderCssClass($value)
265         {
266                 $this->getHeaderStyle()->setCssClass($value);
267         }
268
269         /**
270          * @return TStyle the style for all the inactive header div
271          */
272         public function getHeaderStyle()
273         {
274                 if(($style=$this->getViewState('HeaderStyle',null))===null)
275                 {
276                         $style=new TStyle;
277                         $style->setCssClass('accordion-header');
278                         $this->setViewState('HeaderStyle',$style,null);
279                 }
280                 return $style;
281         }
282
283         /**
284          * @return string Extra CSS class for the active header. Defaults to 'accordion-header-active'.
285          */
286         public function getActiveHeaderCssClass()
287         {
288                 return $this->getActiveHeaderStyle()->getCssClass();
289         }
290
291         /**
292          * @param string Extra CSS class for the active header. Will be added to the normal header specified by HeaderCssClass.
293          */
294         public function setActiveHeaderCssClass($value)
295         {
296                 $this->getActiveHeaderStyle()->setCssClass($value);
297         }
298
299         /**
300          * @return TStyle the style for the active header div
301          */
302         public function getActiveHeaderStyle()
303         {
304                 if(($style=$this->getViewState('ActiveHeaderStyle',null))===null)
305                 {
306                         $style=new TStyle;
307                         $style->setCssClass('accordion-header-active');
308                         $this->setViewState('ActiveHeaderStyle',$style,null);
309                 }
310                 return $style;
311         }
312
313         /**
314          * @return integer Maximum height for the accordion views. If non specified, the accordion will auto-sized to the largest of all views, so it can encompass all of them without scrolling
315          */
316         public function getViewHeight()
317         {
318                 return TPropertyValue::ensureInteger($this->getViewState('ViewHeight'));
319         }
320
321         /**
322          * @param integer Maximum height for the accordion views. If any of the accordion's views' content is larger, those views will be made scrollable when activated
323          */
324         public function setViewHeight($value)
325         {
326                 $this->setViewState('ViewHeight', TPropertyValue::ensureInteger($value));
327         }
328
329         /**
330          * Activates the specified view.
331          * If there is any other view currently active, it will be deactivated.
332          * @param TAccordionView the view to be activated. If null, all views will be deactivated.
333          */
334         protected function activateView($view)
335         {
336                 $this->setActiveViewIndex(-1);
337                 $this->setActiveViewID('');
338                 foreach($this->getViews() as $index=>$v)
339                 {
340                         if($view===$v)
341                         {
342                                 $this->setActiveViewIndex($index);
343                                 $this->setActiveViewID($view->getID(false));
344                                 $view->setActive(true);
345                         }
346                         else
347                                 $v->setActive(false);
348                 }
349         }
350
351         /**
352          * Loads user input data.
353          * This method is primarly used by framework developers.
354          * @param string the key that can be used to retrieve data from the input data collection
355          * @param array the input data collection
356          * @return boolean whether the data of the control has been changed
357          */
358         public function loadPostData($key,$values)
359         {
360                 if(($index=$values[$this->getClientID().'_1'])!==null)
361                 {
362                         $index=(int)$index;
363                         $currentIndex=$this->getActiveViewIndex();
364                         if($currentIndex!==$index)
365                         {
366                                 $this->setActiveViewID(''); // clear up view ID
367                                 $this->setActiveViewIndex($index);
368                                 return $this->_dataChanged=true;
369                         }
370                 }
371                 return false;
372         }
373
374         /**
375          * Raises postdata changed event.
376          * This method is required by {@link IPostBackDataHandler} interface.
377          * It is invoked by the framework when {@link getActiveViewIndex ActiveViewIndex} property
378          * is changed on postback.
379          * This method is primarly used by framework developers.
380          */
381         public function raisePostDataChangedEvent()
382         {
383                 // do nothing
384         }
385
386         /**
387          * Returns a value indicating whether postback has caused the control data change.
388          * This method is required by the IPostBackDataHandler interface.
389          * @return boolean whether postback has caused the control data change. False if the page is not in postback mode.
390          */
391         public function getDataChanged()
392         {
393                 return $this->_dataChanged;
394         }
395
396         /**
397          * Adds attributes to renderer.
398          * @param THtmlWriter the renderer
399          */
400         protected function addAttributesToRender($writer)
401         {
402                 $writer->addAttribute('id',$this->getClientID());
403                 $this->setCssClass($this->getCssClass());
404                 parent::addAttributesToRender($writer);
405         }
406
407         /**
408          * Registers CSS and JS.
409          * This method is invoked right before the control rendering, if the control is visible.
410          * @param mixed event parameter
411          */
412         public function onPreRender($param)
413         {
414                 parent::onPreRender($param);
415                 $this->getActiveView();  // determine the active view
416                 $this->registerStyleSheet();
417         }
418
419         /**
420          * Registers the CSS relevant to the TAccordion.
421          * It will register the CSS file specified by {@link getCssUrl CssUrl}.
422          * If that is not set, it will use the default CSS.
423          */
424         protected function registerStyleSheet()
425         {
426                 $url = $this->getCssUrl();
427
428                 if($url === '') {
429                         return;
430                 }
431
432                 if($url === 'default') {
433                         $url = $this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'accordion.css');
434                 }
435
436                 if($url !== '') {
437                         $this->getPage()->getClientScript()->registerStyleSheetFile($url, $url);
438                 }
439         }
440
441         /**
442          * Registers the relevant JavaScript.
443          */
444         protected function registerClientScript()
445         {
446                 $id=$this->getClientID();
447                 $options=TJavaScript::encode($this->getClientOptions());
448                 $className=$this->getClientClassName();
449                 $page=$this->getPage();
450                 $cs=$page->getClientScript();
451                 $cs->registerPradoScript('accordion');
452                 $code="new $className($options);";
453                 $cs->registerEndScript("prado:$id", $code);
454                 // ensure an item is always active and visible
455                 $index = $this->getActiveViewIndex();
456                 if(!$this->getViews()->itemAt($index)->Visible)
457                         $index=0;
458                 $cs->registerHiddenField($id.'_1', $index);
459                 $page->registerRequiresPostData($this);
460                 $page->registerRequiresPostData($id."_1");
461         }
462
463         /**
464          * Gets the name of the javascript class responsible for performing postback for this control.
465          * This method overrides the parent implementation.
466          * @return string the javascript class name
467          */
468         protected function getClientClassName()
469         {
470                 return 'Prado.WebUI.TAccordion';
471         }
472
473         /**
474          * @return array the options for JavaScript
475          */
476         protected function getClientOptions()
477         {
478                 $options['ID'] = $this->getClientID();
479                 $options['ActiveHeaderCssClass'] = $this->getActiveHeaderCssClass();
480                 $options['HeaderCssClass'] = $this->getHeaderCssClass();
481                 $options['Duration'] = $this->getAnimationDuration();
482
483                 if (($viewheight = $this->getViewHeight())>0)
484                         $options['maxHeight'] = $viewheight;
485                 $views = array();
486                 foreach($this->getViews() as $view)
487                         $views[$view->getClientID()] = $view->getVisible() ? '1': '0';
488                 $options['Views'] = $views;
489
490                 return $options;
491         }
492
493         /**
494          * Creates a control collection object that is to be used to hold child controls
495          * @return TAccordionViewCollection control collection
496          */
497         protected function createControlCollection()
498         {
499                 return new TAccordionViewCollection($this);
500         }
501
502         /**
503          * @return TAccordionViewCollection list of {@link TAccordionView} controls
504          */
505         public function getViews()
506         {
507                 return $this->getControls();
508         }
509
510         public function render($writer)
511         {
512                 $this->registerClientScript();
513                 parent::render($writer);
514         }
515
516         /**
517          * Renders body contents of the accordion control.
518          * @param THtmlWriter the writer used for the rendering purpose.
519          */
520         public function renderContents($writer)
521         {
522                 $views=$this->getViews();
523                 if($views->getCount()>0)
524                 {
525                         $writer->writeLine();
526                         foreach($views as $view)
527                         {
528                                 $view->renderHeader($writer);
529                                 $view->renderControl($writer);
530                                 $writer->writeLine();
531                         }
532                 }
533         }
534
535 }
536
537 /**
538  * Class TAccordionView.
539  *
540  * TAccordionView represents a single view in a {@link TAccordion}.
541  *
542  * TAccordionView is represented inside the {@link TAccordion} with an header label whose text is defined by
543  * the {@link setCaption Caption} property; optionally the label can be an hyperlink: use the
544  * {@link setNavigateUrl NavigateUrl} property to define the destination url.
545  *
546  * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
547  * @package System.Web.UI.WebControls
548  * @since 3.2
549  */
550 class TAccordionView extends TWebControl
551 {
552         private $_active=false;
553
554         /**
555          * @return the tag name for the view element
556          */
557         protected function getTagName()
558         {
559                 return 'div';
560         }
561
562         /**
563          * Adds attributes to renderer.
564          * @param THtmlWriter the renderer
565          */
566         protected function addAttributesToRender($writer)
567         {
568                 if(!$this->getActive() && $this->getPage()->getClientSupportsJavaScript())
569                         $this->getStyle()->setStyleField('display','none');
570
571                 $this->getStyle()->mergeWith($this->getParent()->getViewStyle());
572
573                 parent::addAttributesToRender($writer);
574
575                 $writer->addAttribute('id',$this->getClientID());
576         }
577
578         /**
579          * @return string the caption displayed on this header. Defaults to ''.
580          */
581         public function getCaption()
582         {
583                 return $this->getViewState('Caption','');
584         }
585
586         /**
587          * @param string the caption displayed on this header
588          */
589         public function setCaption($value)
590         {
591                 $this->setViewState('Caption',TPropertyValue::ensureString($value),'');
592         }
593
594         /**
595          * @return string the URL of the target page. Defaults to ''.
596          */
597         public function getNavigateUrl()
598         {
599                 return $this->getViewState('NavigateUrl','');
600         }
601
602         /**
603          * Sets the URL of the target page.
604          * If not empty, clicking on this header will redirect the browser to the specified URL.
605          * @param string the URL of the target page.
606          */
607         public function setNavigateUrl($value)
608         {
609                 $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),'');
610         }
611
612         /**
613          * @return string the text content displayed on this view. Defaults to ''.
614          */
615         public function getText()
616         {
617                 return $this->getViewState('Text','');
618         }
619
620         /**
621          * Sets the text content to be displayed on this view.
622          * If this is not empty, the child content of the view will be ignored.
623          * @param string the text content displayed on this view
624          */
625         public function setText($value)
626         {
627                 $this->setViewState('Text',TPropertyValue::ensureString($value),'');
628         }
629
630         /**
631          * @return boolean whether this accordion view is active. Defaults to false.
632          */
633         public function getActive()
634         {
635                 return $this->_active;
636         }
637
638         /**
639          * @param boolean whether this accordion view is active.
640          */
641         public function setActive($value)
642         {
643                 $this->_active=TPropertyValue::ensureBoolean($value);
644         }
645
646         /**
647          * Renders body contents of the accordion view.
648          * @param THtmlWriter the writer used for the rendering purpose.
649          */
650         public function renderContents($writer)
651         {
652                 if(($text=$this->getText())!=='')
653                         $writer->write($text);
654                 else if($this->getHasControls())
655                         parent::renderContents($writer);
656         }
657
658         /**
659          * Renders the header associated with the accordion view.
660          * @param THtmlWriter the writer for rendering purpose.
661          */
662         public function renderHeader($writer)
663         {
664                 if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript())
665                 {
666                         $writer->addAttribute('id',$this->getClientID().'_0');
667
668                         $style=$this->getActive()?$this->getParent()->getActiveHeaderStyle():$this->getParent()->getHeaderStyle();
669
670                         $style->addAttributesToRender($writer);
671
672                         $writer->renderBeginTag($this->getTagName());
673
674                         $this->renderHeaderContent($writer);
675
676                         $writer->renderEndTag();
677                 }
678         }
679
680         /**
681          * Renders the content in the header.
682          * By default, a hyperlink is displayed.
683          * @param THtmlWriter the HTML writer
684          */
685         protected function renderHeaderContent($writer)
686         {
687                 $url = $this->getNavigateUrl();
688                 if(($caption=$this->getCaption())==='')
689                         $caption='&nbsp;';
690
691                 if ($url!='')
692                         $writer->write("<a href=\"{$url}\">");
693                 $writer->write("{$caption}");
694                 if ($url!='')
695                         $writer->write("</a>");
696         }
697 }
698
699 /**
700  * Class TAccordionViewCollection.
701  *
702  * TAccordionViewCollection is a collection of {@link TAccordionView} to be used inside a {@link TAccordion}.
703  *
704  * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
705  * @package System.Web.UI.WebControls
706  * @since 3.2
707  */
708 class TAccordionViewCollection extends TControlCollection
709 {
710         /**
711          * Inserts an item at the specified position.
712          * This overrides the parent implementation by performing sanity check on the type of new item.
713          * @param integer the speicified position.
714          * @param mixed new item
715          * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TAccordionView} object.
716          */
717         public function insertAt($index,$item)
718         {
719                 if($item instanceof TAccordionView)
720                         parent::insertAt($index,$item);
721                 else
722                         throw new TInvalidDataTypeException('tabviewcollection_tabview_required');
723         }
724
725         /**
726          * Finds the index of the accordion view whose ID is the same as the one being looked for.
727          * @param string the explicit ID of the accordion view to be looked for
728          * @return integer the index of the accordion view found, -1 if not found.
729          */
730         public function findIndexByID($id)
731         {
732                 foreach($this as $index=>$view)
733                 {
734                         if($view->getID(false)===$id)
735                                 return $index;
736                 }
737                 return -1;
738         }
739 }
740