diff options
Diffstat (limited to 'framework/Web')
| -rw-r--r-- | framework/Web/Javascripts/source/packages.php | 17 | ||||
| -rw-r--r-- | framework/Web/Javascripts/source/prado/controls/accordion.js | 149 | ||||
| -rw-r--r-- | framework/Web/UI/WebControls/TAccordion.php | 690 | ||||
| -rw-r--r-- | framework/Web/UI/WebControls/assets/accordion.css | 26 | 
4 files changed, 876 insertions, 6 deletions
| diff --git a/framework/Web/Javascripts/source/packages.php b/framework/Web/Javascripts/source/packages.php index cc66a700..5dfbe2c5 100644 --- a/framework/Web/Javascripts/source/packages.php +++ b/framework/Web/Javascripts/source/packages.php @@ -73,7 +73,11 @@ $packages = array(  	'activefileupload' => array(
  		'prado/activefileupload/activefileupload.js'
  	),
 -	
 +
 +	'accordion'=>array(
 +		'prado/controls/accordion.js'
 +	),
 +
  );
 @@ -83,16 +87,17 @@ $dependencies = array(  		'effects'			=> array('prado', 'effects'),
  		'validator'			=> array('prado', 'validator'),
  		'logger'			=> array('prado', 'logger'),
 -		'datepicker'		=> array('prado', 'datepicker'),
 -		'colorpicker'		=> array('prado', 'colorpicker'),
 +		'datepicker'			=> array('prado', 'datepicker'),
 +		'colorpicker'			=> array('prado', 'colorpicker'),
  		'ajax'				=> array('prado', 'effects', 'ajax'),
  		'dragdrop'			=> array('prado', 'effects', 'ajax', 'dragdrop'),
  		'slider'			=> array('prado', 'slider'),
  		'keyboard'			=> array('prado', 'keyboard'),
  		'tabpanel'			=> array('prado', 'tabpanel'),
 -		'activedatepicker' 	=> array('datepicker', 'ajax', 'activedatepicker'),
 -		'activefileupload' 	=> array('prado', 'ajax', 'activefileupload'),
 -		'dragdropextra'		=> array('prado', 'effects', 'ajax', 'dragdrop','dragdropextra'),
 +		'activedatepicker'		=> array('datepicker', 'ajax', 'activedatepicker'),
 +		'activefileupload'		=> array('prado', 'ajax', 'activefileupload'),
 +		'dragdropextra'			=> array('prado', 'effects', 'ajax', 'dragdrop','dragdropextra'),
 +		'accordion'			=> array('prado', 'effects', 'accordion'),
  );
  return array($packages, $dependencies);
 diff --git a/framework/Web/Javascripts/source/prado/controls/accordion.js b/framework/Web/Javascripts/source/prado/controls/accordion.js new file mode 100644 index 00000000..e7d84694 --- /dev/null +++ b/framework/Web/Javascripts/source/prado/controls/accordion.js @@ -0,0 +1,149 @@ +/* Simple Accordion Script  + * Requires Prototype and Script.aculo.us Libraries + * By: Brian Crescimanno <brian.crescimanno@gmail.com> + * http://briancrescimanno.com + * Adapted to Prado & minor improvements: Gabor Berczi <gabor.berczi@devworx.hu> + * This work is licensed under the Creative Commons Attribution-Share Alike 3.0 + * http://creativecommons.org/licenses/by-sa/3.0/us/ + */ + +if (typeof Effect == 'undefined') +  throw("You must have the script.aculo.us Effect library to use this accordion"); + +Prado.WebUI.TAccordion = Class.create(); +Prado.WebUI.TAccordion.prototype = +{ +    initialize : function(options) +    { +	this.element = $(options.ID); +	this.onInit(options); +	Prado.Registry.set(options.ID, this); +    }, + +    onInit : function(options) +    { +        this.accordion = $(options.ID); +        this.options = options; +        this.contents = this.accordion.select('div.'+this.options.contentClass); +        this.isAnimating = false; +        this.current = this.options.defaultExpandedCount ? this.contents[this.options.defaultExpandedCount-1] : this.contents[0]; +        this.toExpand = null; + +        if (options.maxHeight) +                this.maxHeight = options.maxHeight; +        else +                { +                        this.maxHeight = 0; +                        this.checkMaxHeight(); +                } +             +        this.initialHide(); +        this.attachInitialMaxHeight(); + +        var clickHandler =  this.clickHandler.bindAsEventListener(this); +        this.accordion.observe('click', clickHandler); +    }, + +    expand: function(el) { +        this.toExpand = el.next('div.'+this.options.contentClass); +        if(this.current != this.toExpand){ +			this.toExpand.show(); +            this.animate(); +        } +    }, + +    checkMaxHeight: function() { +        for(var i=0; i<this.contents.length; i++) { +            if(this.contents[i].getHeight() > this.maxHeight) { +                this.maxHeight = this.contents[i].getHeight(); +            } +        } +    }, + +    attachInitialMaxHeight: function() { +	this.current.previous('div.'+this.options.toggleClass).addClassName(this.options.toggleActive); +        if(this.current.getHeight() != this.maxHeight) this.current.setStyle({height: this.maxHeight+"px"}); +    }, + +    clickHandler: function(e) { +        var el = e.element(); +        if(el.hasClassName(this.options.toggleClass) && !this.isAnimating) { +            this.expand(el); +        } +    }, + +    initialHide: function(){ +        for(var i=0; i<this.contents.length; i++){ +            if(this.contents[i] != this.current) { +                this.contents[i].hide(); +                this.contents[i].setStyle({height: 0}); +            } +        } +    }, + + +    enableOverflows: function(enable) { +        if (enable) val = ''; else val = 'hidden'; +        this.current.style.overflow = val; +        this.toExpand.style.overflow = val; +    }, + +    animate: function() { +        var effects = new Array(); +        var options = { +            sync: true, +            scaleFrom: 0, +            scaleContent: false, +            transition: Effect.Transitions.sinoidal, +            scaleMode: { +                originalHeight: this.maxHeight, +                originalWidth: this.accordion.getWidth() +            }, +            scaleX: false, +            scaleY: true +        }; + +        effects.push(new Effect.Scale(this.toExpand, 100, options)); + +        options = { +            sync: true, +            scaleContent: false, +            transition: Effect.Transitions.sinoidal, +            scaleX: false, +            scaleY: true +        }; + +        effects.push(new Effect.Scale(this.current, 0, options)); + +        var myDuration = 0.75; + +        new Effect.Parallel(effects, { +            duration: myDuration, +            fps: 35, +            queue: { +                position: 'end', +                scope: 'accordion' +            }, +            beforeStart: function() { +                this.isAnimating = true; +                this.enableOverflows(false); +                this.current.previous('div.'+this.options.toggleClass).removeClassName(this.options.toggleActive); +                this.toExpand.previous('div.'+this.options.toggleClass).addClassName(this.options.toggleActive); +            }.bind(this), +            afterFinish: function() { +                this.current.hide(); +                this.toExpand.setStyle({ height: this.maxHeight+"px" }); +                this.current = this.toExpand; +                this.isAnimating = false; +                this.enableOverflows(true); +            }.bind(this) +        }); +    } + +}; + +/* +document.observe("dom:loaded", function(){ +    accordion = new Accordion("test-accordion", 2); +}) +*/
\ No newline at end of file diff --git a/framework/Web/UI/WebControls/TAccordion.php b/framework/Web/UI/WebControls/TAccordion.php new file mode 100644 index 00000000..df598982 --- /dev/null +++ b/framework/Web/UI/WebControls/TAccordion.php @@ -0,0 +1,690 @@ +<?php
 +/**
 + * TAccordion class file.
 + *
 + * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2005-2008 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Id: TAccordion.php 2915 2011-05-15 16:26:11Z ctrlaltca@gmail.com $
 + * @package System.Web.UI.WebControls
 + * @since 3.2
 + */
 +
 +/**
 + * Class TAccordion.
 + *
 + * TAccordion displays an accordion control. Users can click on the view headers to switch among
 + * different accordion views. Each accordion view is an independent panel that can contain arbitrary content.
 + *
 + * A TAccordion control consists of one or several {@link TAccordionView} controls representing the possible
 + * accordion views. At any time, only one accordion view is visible (active), which is specified by any of
 + * the following properties:
 + * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection.
 + * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view.
 + * - {@link setActiveView ActiveView} - the visible view instance.
 + * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID}
 + * are set, the latter takes precedence.
 + *
 + * TAccordion uses CSS to specify the appearance of the accordion headers and panel. By default,
 + * an embedded CSS file will be published which contains the default CSS for TTabPanel.
 + * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property.
 + * The following properties specify the CSS classes used for elements in a TAccordion:
 + * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'accordion');
 + * - {@link setHeaderCssClass HeaderCssClass} - the CSS class name for nonactive accordion div elements (defaults to 'accordion-header');
 + * - {@link setActiveHeaderCssClass ActiveHeaderCssClass} - the CSS class name for the active accordion div element (defaults to 'accordion-header-active');
 + * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'accordion-view');
 + *
 + * To use TAccordion, write a template like following:
 + * <code>
 + * <com:TAccordion>
 + *   <com:TAccordionView Caption="View 1">
 + *     content for view 1
 + *   </com:TAccordionView>
 + *   <com:TAccordionView Caption="View 2">
 + *     content for view 2
 + *   </com:TAccordionView>
 + *   <com:TAccordionView Caption="View 3">
 + *     content for view 3
 + *   </com:TAccordionView>
 + * </com:TAccordion>
 + * </code>
 + *
 + * @author Gabor Berczi, DevWorx Hungary <gabor.berczi@devworx.hu>
 + * @version $Id: TAccordion.php 2915 2011-05-15 16:26:11Z ctrlaltca@gmail.com $
 + * @package System.Web.UI.WebControls
 + * @since 3.2
 + */
 +
 +class TAccordion extends TWebControl implements IPostBackDataHandler
 +{
 +	private $_dataChanged=false;
 +
 +	/**
 +	 * @return string tag name for the control
 +	 */
 +	protected function getTagName()
 +	{
 +		return 'div';
 +	}
 +
 +	/**
 +	 * Adds object parsed from template to the control.
 +	 * This method adds only {@link TAccordionView} objects into the {@link getViews Views} collection.
 +	 * All other objects are ignored.
 +	 * @param mixed object parsed from template
 +	 */
 +	public function addParsedObject($object)
 +	{
 +		if($object instanceof TAccordionView)
 +			$this->getControls()->add($object);
 +	}
 +
 +	/**
 +	 * Returns the index of the active accordion view.
 +	 * Note, this property may not return the correct index.
 +	 * To ensure the correctness, call {@link getActiveView()} first.
 +	 * @return integer the zero-based index of the active accordion view. If -1, it means no active accordion view. Default is 0 (the first view is active).
 +	 */
 +	public function getActiveViewIndex()
 +	{
 +		return $this->getViewState('ActiveViewIndex',0);
 +	}
 +
 +	/**
 +	 * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
 +	 * @throws TInvalidDataValueException if the view index is invalid
 +	 */
 +	public function setActiveViewIndex($value)
 +	{
 +		$this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0);
 +	}
 +
 +	/**
 +	 * Returns the ID of the active accordion view.
 +	 * Note, this property may not return the correct ID.
 +	 * To ensure the correctness, call {@link getActiveView()} first.
 +	 * @return string The ID of the active accordion view. Defaults to '', meaning not set.
 +	 */
 +	public function getActiveViewID()
 +	{
 +		return $this->getViewState('ActiveViewID','');
 +	}
 +
 +	/**
 +	 * @param string The ID of the active accordion view.
 +	 */
 +	public function setActiveViewID($value)
 +	{
 +		$this->setViewState('ActiveViewID',$value,'');
 +	}
 +
 +	/**
 +	 * Returns the currently active view.
 +	 * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to
 +	 * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly.
 +	 * @return TAccordionView the currently active view, null if no active view
 +	 * @throws TInvalidDataValueException if the active view ID or index set previously is invalid
 +	 */
 +	public function getActiveView()
 +	{
 +		$activeView=null;
 +		$views=$this->getViews();
 +		if(($id=$this->getActiveViewID())!=='')
 +		{
 +			if(($index=$views->findIndexByID($id))>=0)
 +				$activeView=$views->itemAt($index);
 +			else
 +				throw new TInvalidDataValueException('tabpanel_activeviewid_invalid',$id);
 +		}
 +		else if(($index=$this->getActiveViewIndex())>=0)
 +		{
 +			if($index<$views->getCount())
 +				$activeView=$views->itemAt($index);
 +			else
 +				throw new TInvalidDataValueException('tabpanel_activeviewindex_invalid',$index);
 +		}
 +		else
 +		{
 +			foreach($views as $index=>$view)
 +			{
 +				if($view->getActive())
 +				{
 +					$activeView=$view;
 +					break;
 +				}
 +			}
 +		}
 +		if($activeView!==null)
 +			$this->activateView($activeView);
 +		return $activeView;
 +	}
 +
 +	/**
 +	 * @param TAccordionView the view to be activated
 +	 * @throws TInvalidOperationException if the view is not in the view collection
 +	 */
 +	public function setActiveView($view)
 +	{
 +		if($this->getViews()->indexOf($view)>=0)
 +			$this->activateView($view);
 +		else
 +			throw new TInvalidOperationException('tabpanel_view_inexistent');
 +	}
 +
 +	/**
 +	 * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''.
 +	 */
 +	public function getCssUrl()
 +	{
 +		return $this->getViewState('CssUrl','default');
 +	}
 +
 +	/**
 +	 * @param string URL for the CSS file including all relevant CSS class definitions.
 +	 */
 +	public function setCssUrl($value)
 +	{
 +		$this->setViewState('CssUrl',TPropertyValue::ensureString($value),'');
 +	}
 +
 +	/**
 +	 * @return string CSS class for the whole accordion control div. 
 +	 */
 +	public function getCssClass()
 +	{
 +		$cssClass=parent::getCssClass();
 +		return $cssClass===''?'accordion':$cssClass;
 +	}
 +
 +	/**
 +	 * @return string CSS class for the currently displayed view div. Defaults to 'accordion-view'.
 +	 */
 +	public function getViewCssClass()
 +	{
 +		return $this->getViewStyle()->getCssClass();
 +	}
 +
 +	/**
 +	 * @param string CSS class for the currently displayed view div.
 +	 */
 +	public function setViewCssClass($value)
 +	{
 +		$this->getViewStyle()->setCssClass($value);
 +	}
 +
 +	/**
 +	 * @return TStyle the style for all the view div
 +	 */
 +	public function getViewStyle()
 +	{
 +		if(($style=$this->getViewState('ViewStyle',null))===null)
 +		{
 +			$style=new TStyle;
 +			$style->setCssClass('accordion-view');
 +			$this->setViewState('ViewStyle',$style,null);
 +		}
 +		return $style;
 +	}
 +
 +	/**
 +	 * @return string CSS class for view headers. Defaults to 'accordion-header'.
 +	 */
 +	public function getHeaderCssClass()
 +	{
 +		return $this->getHeaderStyle()->getCssClass();
 +	}
 +
 +	/**
 +	 * @param string CSS class for view headers.
 +	 */
 +	public function setHeaderCssClass($value)
 +	{
 +		$this->getHeaderStyle()->setCssClass($value);
 +	}
 +
 +	/**
 +	 * @return TStyle the style for all the inactive header div
 +	 */
 +	public function getHeaderStyle()
 +	{
 +		if(($style=$this->getViewState('HeaderStyle',null))===null)
 +		{
 +			$style=new TStyle;
 +			$style->setCssClass('accordion-header');
 +			$this->setViewState('HeaderStyle',$style,null);
 +		}
 +		return $style;
 +	}
 +
 +	/**
 +	 * @return string Extra CSS class for the active header. Defaults to 'accordion-header-active'.
 +	 */
 +	public function getActiveHeaderCssClass()
 +	{
 +		return $this->getActiveHeaderStyle()->getCssClass();
 +	}
 +
 +	/**
 +	 * @param string Extra CSS class for the active header. Will be added to the normal header specified by HeaderCssClass.
 +	 */
 +	public function setActiveHeaderCssClass($value)
 +	{
 +		$this->getActiveHeaderStyle()->setCssClass($value);
 +	}
 +
 +
 +	/**
 +	 * @return TStyle the style for the active header div
 +	 */
 +	public function getActiveHeaderStyle()
 +	{
 +		if(($style=$this->getViewState('ActiveHeaderStyle',null))===null)
 +		{
 +			$style=new TStyle;
 +			$style->setCssClass('accordion-header-active');
 +			$this->setViewState('ActiveHeaderStyle',$style,null);
 +		}
 +		return $style;
 +	}
 +
 +	/**
 +	 * @return integer Maximum height for the accordion views. If non specified, the accordion will auto-sized to the largest of all views, so it can encompass all of them without scrolling
 +	 */
 +	public function getViewHeight()
 +	{
 +		return TPropertyValue::ensureInteger($this->getViewState('ViewHeight'));
 +	}
 +
 +	/**
 +	 * @param integer Maximum height for the accordion views. If any of the accordion's views' content is larger, those views will be made scrollable when activated
 +	 */
 +	public function setViewHeight($value)
 +	{
 +		$this->setViewState('ViewHeight', TPropertyValue::ensureInteger($value));
 +	}
 +
 +	/**
 +	 * Activates the specified view.
 +	 * If there is any other view currently active, it will be deactivated.
 +	 * @param TAccordionView the view to be activated. If null, all views will be deactivated.
 +	 */
 +	protected function activateView($view)
 +	{
 +		$this->setActiveViewIndex(-1);
 +		$this->setActiveViewID('');
 +		foreach($this->getViews() as $index=>$v)
 +		{
 +			if($view===$v)
 +			{
 +				$this->setActiveViewIndex($index);
 +				$this->setActiveViewID($view->getID(false));
 +				$view->setActive(true);
 +			}
 +			else
 +				$v->setActive(false);
 +		}
 +	}
 +
 +	/**
 +	 * Loads user input data.
 +	 * This method is primarly used by framework developers.
 +	 * @param string the key that can be used to retrieve data from the input data collection
 +	 * @param array the input data collection
 +	 * @return boolean whether the data of the control has been changed
 +	 */
 +	public function loadPostData($key,$values)
 +	{
 +		if(($index=$values[$this->getClientID().'_1'])!==null)
 +		{
 +			$index=(int)$index;
 +			$currentIndex=$this->getActiveViewIndex();
 +			if($currentIndex!==$index)
 +			{
 +				$this->setActiveViewID(''); // clear up view ID
 +				$this->setActiveViewIndex($index);
 +				return $this->_dataChanged=true;
 +			}
 +		}
 +		return false;
 +	}
 +
 +	/**
 +	 * Raises postdata changed event.
 +	 * This method is required by {@link IPostBackDataHandler} interface.
 +	 * It is invoked by the framework when {@link getActiveViewIndex ActiveViewIndex} property
 +	 * is changed on postback.
 +	 * This method is primarly used by framework developers.
 +	 */
 +	public function raisePostDataChangedEvent()
 +	{
 +		// do nothing
 +	}
 +
 +	/**
 +	 * Returns a value indicating whether postback has caused the control data change.
 +	 * This method is required by the IPostBackDataHandler interface.
 +	 * @return boolean whether postback has caused the control data change. False if the page is not in postback mode.
 +	 */
 +	public function getDataChanged()
 +	{
 +		return $this->_dataChanged;
 +	}
 +
 +	/**
 +	 * Adds attributes to renderer.
 +	 * @param THtmlWriter the renderer
 +	 */
 +	protected function addAttributesToRender($writer)
 +	{
 +		$writer->addAttribute('id',$this->getClientID());
 +		$this->setCssClass($this->getCssClass());
 +		parent::addAttributesToRender($writer);
 +	}
 +
 +	/**
 +	 * Registers CSS and JS.
 +	 * This method is invoked right before the control rendering, if the control is visible.
 +	 * @param mixed event parameter
 +	 */
 +	public function onPreRender($param)
 +	{
 +		parent::onPreRender($param);
 +		$this->getActiveView();  // determine the active view
 +		$this->registerStyleSheet();
 +		$this->registerClientScript();
 +	}
 +
 +	/**
 +	 * Registers the CSS relevant to the TAccordion.
 +	 * It will register the CSS file specified by {@link getCssUrl CssUrl}.
 +	 * If that is not set, it will use the default CSS.
 +	 */
 +	protected function registerStyleSheet()
 +	{
 +		$url = $this->getCssUrl();
 +
 +		if($url === '') {
 +			return;
 +		}
 +
 +		if($url === 'default') {
 +			$url = $this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'accordion.css');
 +		}
 +
 +		if($url !== '') {
 +			$this->getPage()->getClientScript()->registerStyleSheetFile($url, $url);
 +		}
 +	}
 +
 +	/**
 +	 * Registers the relevant JavaScript.
 +	 */
 +	protected function registerClientScript()
 +	{
 +		$id=$this->getClientID();
 +		$options = TJavaScript::encode($this->getClientOptions());
 +		$className=$this->getClientClassName();
 +		$page=$this->getPage();
 +		$cs=$page->getClientScript();
 +		$cs->registerPradoScript('accordion');
 +		$code = "new $className($options);";
 +		$cs->registerEndScript("prado:$id", $code);
 +		$cs->registerHiddenField($id.'_1',$this->getActiveViewIndex());
 +		$page->registerRequiresPostData($this);
 +	}
 +
 +	/**
 +	 * Gets the name of the javascript class responsible for performing postback for this control.
 +	 * This method overrides the parent implementation.
 +	 * @return string the javascript class name
 +	 */
 +	protected function getClientClassName()
 +	{
 +		return 'Prado.WebUI.TAccordion';
 +	}
 +
 +	/**
 +	 * @return array the options for JavaScript
 +	 */
 +	protected function getClientOptions()
 +	{
 +		$options['ID']=$this->getClientID();
 +		$options['toggleActive']=$this->getActiveHeaderCssClass();
 +		$options['toggleClass']=$this->getHeaderCssClass();
 +		$options['contentClass']=$this->getViewCssClass();
 +		$options['defaultExpandedCount']=$this->getActiveViewIndex()+1;
 +		if (($viewheight = $this->getViewHeight())>0)
 +			$options['maxHeight'] = $viewheight;
 +		/*
 +		$viewIDs=array();
 +		foreach($this->getViews() as $view)
 +		{
 +			if($view->getVisible())
 +				$viewIDs[]=$view->getClientID();
 +		}
 +		$options['Views']='[\''.implode('\',\'',$viewIDs).'\']';
 +		*/
 +
 +		return $options;
 +	}
 +
 +	/**
 +	 * Creates a control collection object that is to be used to hold child controls
 +	 * @return TAccordionViewCollection control collection
 +	 */
 +	protected function createControlCollection()
 +	{
 +		return new TAccordionViewCollection($this);
 +	}
 +
 +	/**
 +	 * @return TAccordionViewCollection list of {@link TAccordionView} controls
 +	 */
 +	public function getViews()
 +	{
 +		return $this->getControls();
 +	}
 +
 +	/**
 +	 * Renders body contents of the accordion control.
 +	 * @param THtmlWriter the writer used for the rendering purpose.
 +	 */
 +	public function renderContents($writer)
 +	{
 +		$views=$this->getViews();
 +		if($views->getCount()>0)
 +		{
 +			$writer->writeLine();
 +			foreach($views as $view)
 +			{
 +				$view->renderHeader($writer);
 +				$view->renderControl($writer);
 +				$writer->writeLine();
 +			}
 +		}
 +	}
 +
 +}
 +
 +class TAccordionView extends TWebControl
 +{
 +	private $_active=false;
 +
 +	/**
 +	 * @return the tag name for the view element
 +	 */
 +	protected function getTagName()
 +	{
 +		return 'div';
 +	}
 +
 +	/**
 +	 * Adds attributes to renderer.
 +	 * @param THtmlWriter the renderer
 +	 */
 +	protected function addAttributesToRender($writer)
 +	{
 +		$this->getStyle()->mergeWith($this->getParent()->getViewStyle());
 +
 +		parent::addAttributesToRender($writer);
 +
 +		$writer->addAttribute('id',$this->getClientID());
 +	}
 +
 +	/**
 +	 * @return string the caption displayed on this header. Defaults to ''.
 +	 */
 +	public function getCaption()
 +	{
 +		return $this->getViewState('Caption','');
 +	}
 +
 +	/**
 +	 * @param string the caption displayed on this header
 +	 */
 +	public function setCaption($value)
 +	{
 +		$this->setViewState('Caption',TPropertyValue::ensureString($value),'');
 +	}
 +
 +	/**
 +	 * @return string the URL of the target page. Defaults to ''.
 +	 */
 +	public function getNavigateUrl()
 +	{
 +		return $this->getViewState('NavigateUrl','');
 +	}
 +
 +	/**
 +	 * Sets the URL of the target page.
 +	 * If not empty, clicking on this header will redirect the browser to the specified URL.
 +	 * @param string the URL of the target page.
 +	 */
 +	public function setNavigateUrl($value)
 +	{
 +		$this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),'');
 +	}
 +
 +	/**
 +	 * @return string the text content displayed on this view. Defaults to ''.
 +	 */
 +	public function getText()
 +	{
 +		return $this->getViewState('Text','');
 +	}
 +
 +	/**
 +	 * Sets the text content to be displayed on this view.
 +	 * If this is not empty, the child content of the view will be ignored.
 +	 * @param string the text content displayed on this view
 +	 */
 +	public function setText($value)
 +	{
 +		$this->setViewState('Text',TPropertyValue::ensureString($value),'');
 +	}
 +
 +	/**
 +	 * @return boolean whether this accordion view is active. Defaults to false.
 +	 */
 +	public function getActive()
 +	{
 +		return $this->_active;
 +	}
 +
 +	/**
 +	 * @param boolean whether this accordion view is active.
 +	 */
 +	public function setActive($value)
 +	{
 +		$this->_active=TPropertyValue::ensureBoolean($value);
 +	}
 +
 +	/**
 +	 * Renders body contents of the accordion view.
 +	 * @param THtmlWriter the writer used for the rendering purpose.
 +	 */
 +	public function renderContents($writer)
 +	{
 +		if(($text=$this->getText())!=='')
 +			$writer->write($text);
 +		else if($this->getHasControls())
 +			parent::renderContents($writer);
 +	}
 +
 +	/**
 +	 * Renders the header associated with the accordion view.
 +	 * @param THtmlWriter the writer for rendering purpose.
 +	 */
 +	public function renderHeader($writer)
 +	{
 +		if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript())
 +		{
 +			$writer->addAttribute('id',$this->getClientID().'_0');
 +
 +//			$style=$this->getActive()?$this->getParent()->getActiveHeaderStyle():$this->getParent()->getHeaderStyle();
 +			$style=$this->getParent()->getHeaderStyle();
 +			
 +			$style->addAttributesToRender($writer);
 +
 +			$writer->renderBeginTag($this->getTagName());
 +
 +			$this->renderHeaderContent($writer);
 +
 +			$writer->renderEndTag();
 +		}
 +	}
 +
 +	/**
 +	 * Renders the content in the header.
 +	 * By default, a hyperlink is displayed.
 +	 * @param THtmlWriter the HTML writer
 +	 */
 +	protected function renderHeaderContent($writer)
 +	{
 +		$url = $this->getNavigateUrl();
 +		if(($caption=$this->getCaption())==='')
 +			$caption=' ';
 +			
 +		if ($url!='')
 +			$writer->write("<a href=\"{$url}\">");
 +		$writer->write("{$caption}");
 +		if ($url!='')
 +			$writer->write("</a>");
 +	}
 +}
 +
 +class TAccordionViewCollection extends TControlCollection
 +{
 +	/**
 +	 * Inserts an item at the specified position.
 +	 * This overrides the parent implementation by performing sanity check on the type of new item.
 +	 * @param integer the speicified position.
 +	 * @param mixed new item
 +	 * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TAccordionView} object.
 +	 */
 +	public function insertAt($index,$item)
 +	{
 +		if($item instanceof TAccordionView)
 +			parent::insertAt($index,$item);
 +		else
 +			throw new TInvalidDataTypeException('tabviewcollection_tabview_required');
 +	}
 +
 +	/**
 +	 * Finds the index of the accordion view whose ID is the same as the one being looked for.
 +	 * @param string the explicit ID of the accordion view to be looked for
 +	 * @return integer the index of the accordion view found, -1 if not found.
 +	 */
 +	public function findIndexByID($id)
 +	{
 +		foreach($this as $index=>$view)
 +		{
 +			if($view->getID(false)===$id)
 +				return $index;
 +		}
 +		return -1;
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Web/UI/WebControls/assets/accordion.css b/framework/Web/UI/WebControls/assets/accordion.css new file mode 100644 index 00000000..09ac764d --- /dev/null +++ b/framework/Web/UI/WebControls/assets/accordion.css @@ -0,0 +1,26 @@ +
 +div.accordion {
 +	position: relative;
 +}
 +
 +div.accordion-header {
 +	padding: 3px; 
 +	z-index: 10; position: relative; 
 +	cursor: pointer;
 +}
 +
 +div.accordion-header {
 +	background-color: #5577aa;
 +	color: white; font-weight: bold;
 +	border: 1px solid;
 +}
 +
 +div.accordion-header-active {
 +	background-color: #334477;
 +}
 +
 +div.accordion-view {
 +	overflow: auto;
 +	padding-left: 3px; padding-right: 3px;
 +	margin-bottom: 2px;
 +}
 | 
