<?php /** * TSlider class file. * * @author Christophe Boulain <Christophe.Boulain@gmail.com> * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2011 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id$ * @package System.Web.UI.WebControls * @since 3.1.1 */ /** * TSlider class * * TSlider displays a slider for numeric input purpose. A slider consists of a 'track', * which define the range of possible value, and a 'handle' which can slide on the track, to select * a value in the range. The track can be either Horizontal or Vertical, depending of the {@link SetDirection Direction} * property. By default, it's horizontal. * * The range boundaries are defined by {@link SetMinValue MinValue} and {@link SetMaxValue MaxValue} properties. * The default range is from 0 to 100. * The {@link SetStepSize StepSize} property can be used to define the <b>step</b> between 2 values inside the range. * Notice that this step will be recomputed if there is more than 200 values between the range boundaries. * You can also provide the allowed values by setting the {@link SetValues Values} array. * * A 'Progress Indicator' can be displayed within the track with the {@link SetProgressIndicator ProgressIndicator} property. * * The TSlider control can be easily customized using CssClasses. You can provide your own css file, using the * {@link SetCssUrl CssUrl} property. * The css class for TSlider can be set by the {@link setCssClass CssClass} property. Default value is "Slider HorizontalSlider" * for an horizontal slider, and "Slider VerticalSlider" for a vertical one. * * If {@link SetAutoPostBack AutoPostBack} property is true, postback is sent as soon as the value changed. * * TSlider raises the {@link onValueChanged} event when the value of the slider has changed during postback. * * You can also attach ClientSide javascript events handler to the slider : * - ClientSide.onSlide is called when the handle is slided on the track. You can get the current value in the <b>value</b> * javascript variable. You can use this event to update on client side a label with the current value * - ClientSide.onChange is called when the slider value has changed (at the end of a move). * * @author Christophe Boulain <Christophe.Boulain@gmail.com> * @version $Id$ * @package System.Web.UI.WebControls * @since 3.1.1 */ class TSlider extends TWebControl implements IPostBackDataHandler, IDataRenderer { const MAX_STEPS=200; /** * @var TSliderHandle handle component */ private $_handle; /* * @var boolean Wether the data has changed during postback */ private $_dataChanged=false; /** * @var TSliderClientScript Clients side javascripts */ private $_clientScript=null; /** * @return TSliderDirection Direction of slider (Horizontal or Vertical). Defaults to Horizontal. */ public function getDirection() { return $this->getViewState('Direction', TSliderDirection::Horizontal); } /** * @param TSliderDirection Direction of slider (Horizontal or Vertical) */ public function setDirection($value) { $this->setViewState('Direction', TPropertyValue::ensureEnum($value,'TSliderDirection'),TSliderDirection::Horizontal); } /** * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to '' (a default CSS file will be applied in this case.) */ public function getCssUrl() { return $this->getViewState('CssUrl',''); } /** * @param string URL for the CSS file including all relevant CSS class definitions. */ public function setCssUrl($value) { $this->setViewState('CssUrl',TPropertyValue::ensureString($value),''); } /** * @return float Maximum value for the slider. Defaults to 100.0. */ public function getMaxValue() { return $this->getViewState('MaxValue',100.0); } /** * @param float Maximum value for slider */ public function setMaxValue($value) { $this->setViewState('MaxValue', TPropertyValue::ensureFloat($value),100.0); } /** * @return float Minimum value for slider. Defaults to 0.0. */ public function getMinValue() { return $this->getViewState('MinValue',0.0); } /** * @param float Minimum value for slider */ public function setMinValue($value) { $this->setViewState('MinValue', TPropertyValue::ensureFloat($value),0.0); } /** * @return float Step size. Defaults to 1.0. */ public function getStepSize() { return $this->getViewState('StepSize', 1.0); } /** * Sets the step size used to determine the places where the slider handle can stop at. * An evenly distributed stop marks will be generated according to * {@link getMinValue MinValue}, {@link getMaxValue MaxValue} and StepSize. * To use uneven stop marks, set {@link setValues Values}. * @param float Step size. */ public function setStepSize($value) { $this->setViewState('StepSize', $value, 1.0); } /** * @return boolean wether to display a progress indicator or not. Defaults to true. */ public function getProgressIndicator () { return $this->getViewState('ProgressIndicator', true); } /** * @param boolean wether to display a progress indicator or not. Defaults to true. */ public function setProgressIndicator ($value) { $this->setViewState('ProgressIndicator', TPropertyValue::ensureBoolean($value), true); } /** * @return float current value of slider */ public function getValue() { return $this->getViewState('Value',0.0); } /** * @param float current value of slider */ public function setValue($value) { $this->setViewState('Value', TPropertyValue::ensureFloat($value),0.0); } /** * Returns the value of the TSlider control. * This method is required by {@link IDataRenderer}. * It is the same as {@link getValue()}. * @return string the value of the TSlider control. * @see getValue */ public function getData() { return $this->getValue(); } /** * Sets the value of the TSlider control. * This method is required by {@link IDataRenderer}. * It is the same as {@link setValue()}. * @param string the value of the TSlider control. * @see setValue */ public function setData($value) { $this->setValue($value); } /** * @return array list of allowed values the slider can take. Defaults to an empty array. */ public function getValues() { return $this->getViewState('Values', array()); } /** * Sets the possible values that the slider can take. * If this is set, {@link setStepSize StepSize} will be ignored. The latter * generates a set of evenly distributed candidate values. * @param array list of allowed values the slider can take */ public function setValues($value) { $this->setViewState('Values', TPropertyValue::ensureArray($value), array()); } /** * @return boolean a value indicating whether an automatic postback to the server * will occur whenever the user modifies the slider value. Defaults to false. */ public function getAutoPostBack() { return $this->getViewState('AutoPostBack',false); } /** * Sets the value indicating if postback automatically. * An automatic postback to the server will occur whenever the user * modifies the slider value. * @param boolean the value indicating if postback automatically */ public function setAutoPostBack($value) { $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); } /** * Gets the name of the javascript class responsible for performing postback for this control. * This method overrides the parent implementation. * @return string the javascript class name */ protected function getClientClassName() { return 'Prado.WebUI.TSlider'; } /** * Returns a value indicating whether postback has caused the control data change. * This method is required by the IPostBackDataHandler interface. * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. */ public function getDataChanged() { return $this->_dataChanged; } /** * Raises postdata changed event. * This method is required by {@link IPostBackDataHandler} interface. * It is invoked by the framework when {@link getValue Value} property * is changed on postback. * This method is primarly used by framework developers. */ public function raisePostDataChangedEvent() { $this->onValueChanged(null); } /** * Raises <b>OnValueChanged</b> event. * This method is invoked when the {@link getValue Value} * property changes on postback. * If you override this method, be sure to call the parent implementation to ensure * the invocation of the attached event handlers. * @param TEventParameter event parameter to be passed to the event handlers */ public function onValueChanged($param) { $this->raiseEvent('OnValueChanged',$this,$param); } /** * Loads user input data. * This method is primarly used by framework developers. * @param string the key that can be used to retrieve data from the input data collection * @param array the input data collection * @return boolean whether the data of the component has been changed */ public function loadPostData($key,$values) { $value=(float)$values[$this->getClientID().'_1']; if($this->getValue()!==$value) { $this->setValue($value); return $this->_dataChanged=true; } else return false; } /** * Gets the TSliderClientScript to set the TSlider event handlers. * * The slider on the client-side supports the following events. * # <tt>OnSliderMove</tt> -- raised when the slider is moved. * # <tt>OnSliderChanged</tt> -- raised when the slider value is changed * * You can attach custom javascript code to each of these events * * @return TSliderClientScript javascript validator event options. */ public function getClientSide() { if($this->_clientScript===null) $this->_clientScript = $this->createClientScript(); return $this->_clientScript; } /** * @return TSliderClientScript javascript event options. */ protected function createClientScript() { return new TSliderClientScript; } /** * @return string the HTML tag name for slider. Defaults to div. */ public function getTagName () { return "div"; } /** * Add the specified css classes to the track * @param THtmlWriter writer */ protected function addAttributesToRender($writer) { parent::addAttributesToRender($writer); $writer->addAttribute('id',$this->getClientID()); if ($this->getCssClass()==='') { $class=($this->getDirection()==TSliderDirection::Horizontal)?'HorizontalSlider':'VerticalSlider'; $writer->addAttribute('class', 'Slider '.$class); } } /** * Render the body content */ public function renderContents($writer) { // Render the 'Track' $writer->addAttribute('class', 'Track'); $writer->addAttribute('id', $this->getClientID().'_track'); $writer->renderBeginTag('div'); // Render the 'Progress Indicator' if ($this->getProgressIndicator()) { $writer->addAttribute('class', 'Progress'); $writer->addAttribute('id', $this->getClientID().'_progress'); $writer->renderBeginTag('div'); $writer->renderEndTag(); } // Render the 'Ruler' /* * Removing for now $writer->addAttribute('class', 'RuleContainer'); $writer->addAttribute('id', $this->getClientID()."_rule"); $writer->renderBeginTag('div'); for ($i=0;$i<=100;$i+=10) { $writer->addAttribute('class', 'RuleMark'); $attr=($this->getDirection()===TSliderDirection::Horizontal)?"left":"top"; $writer->addStyleAttribute($attr, $i.'%'); $writer->renderBeginTag('div'); $writer->renderEndTag(); } $writer->renderEndTag(); */ $writer->renderEndTag(); // Render the 'Handle' $writer->addAttribute('class', 'Handle'); $writer->addAttribute('id', $this->getClientID().'_handle'); $writer->renderBeginTag('div'); $writer->renderEndTag(); } /** * Registers CSS and JS. * This method is invoked right before the control rendering, if the control is visible. * @param mixed event parameter */ public function onPreRender ($param) { parent::onPreRender($param); $this->registerStyleSheet(); $this->registerSliderClientScript(); } /** * Registers the CSS relevant to the TSlider. * It will register the CSS file specified by {@link getCssUrl CssUrl}. * If that is not set, it will use the default CSS. */ protected function registerStyleSheet() { if(($url=$this->getCssUrl())==='') { $manager=$this->getApplication()->getAssetManager(); // publish the assets $url=$manager->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'TSlider'); $url.='/TSlider.css'; } $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); } /** * Registers the javascript code to initialize the slider. */ protected function registerSliderClientScript() { $page=$this->getPage(); $cs = $page->getClientScript(); $cs->registerPradoScript("slider"); $id=$this->getClientID(); $cs->registerHiddenField($id.'_1',$this->getValue()); $page->registerRequiresPostData($this); $cs->registerPostBackControl($this->getClientClassName(),$this->getSliderOptions()); } /** * Get javascript sliderr options. * @return array slider client-side options */ protected function getSliderOptions() { // PostBack Options : $options['ID'] = $this->getClientID(); $options['EventTarget'] = $this->getUniqueID(); $options['AutoPostBack'] = $this->getAutoPostBack(); // Slider Control options $minValue=$this->getMinValue(); $maxValue=$this->getMaxValue(); $options['axis'] = strtolower($this->getDirection()); $options['maximum'] = $maxValue; $options['minimum'] = $minValue; $options['range'] = 'javascript:$R('.$minValue.",".$maxValue.")"; $options['sliderValue'] = $this->getValue(); $options['disabled'] = !$this->getEnabled(); $values=$this->getValues(); if (!empty($values)) { // Values are provided. Check if min/max are present in them if (!in_array($minValue, $values)) $values[]=$minValue; if (!in_array($maxValue, $values)) $values[]=$maxValue; // Remove all values outsize the range [min..max] foreach ($values as $idx=>$value) { if ($value < $minValue) unset ($values[$idx]); if ($value > $maxValue) unset ($values[$idx]); } } else { // Values are not provided, generate automatically using stepsize $step=$this->getStepSize(); // We want at most self::MAX_STEPS values, so, change the step if necessary if (($maxValue-$minValue)/$step > self::MAX_STEPS) { $step=($maxValue-$minValue)/self::MAX_STEPS; } $values=array(); for ($i=$minValue;$i<=$maxValue;$i+=$step) $values[]=$i; // Add max if it's not in the array because of step if (!in_array($maxValue, $values)) $values[]=$maxValue; } $options['values'] = TJavaScript::Encode($values,false); if($this->_clientScript!==null) $options = array_merge($options,$this->_clientScript->getOptions()->toArray()); return $options; } } /** * TSliderClientScript class. * * Client-side slider events {@link setOnChange OnChange} and {@line setOnMove OnMove} * can be modified through the {@link TSlider:: getClientSide ClientSide} * property of a slider. * * The current value of the slider can be get in the 'value' js variable * * The <tt>OnMove</tt> event is raised when the slider moves * The <tt>OnChange</tt> event is raised when the slider value is changed (or at the end of a move) * * @author Christophe Boulain <Christophe.Boulain@gmail.com> * @version $Id$ * @package System.Web.UI.WebControls * @since 3.1.1 */ class TSliderClientScript extends TClientSideOptions { /** * Javascript code to execute when the slider value is changed. * @param string javascript code */ public function setOnChange($javascript) { $code="javascript: function (value) { {$javascript} }"; $this->setFunction('onChange', $code); } /** * @return string javascript code to execute when the slider value is changed. */ public function getOnChange() { return $this->getOption('onChange'); } /* Javascript code to execute when the slider moves. * @param string javascript code */ public function setOnSlide($javascript) { $code="javascript: function (value) { {$javascript} }"; $this->setFunction('onSlide', $code); } /** * @return string javascript code to execute when the slider moves. */ public function getOnSlide() { return $this->getOption('onSlide'); } } /** * TSliderDirection class. * * TSliderDirection defines the enumerable type for the possible direction that can be used in a {@link TSlider} * * The following enumerable values are defined : * - Horizontal : Horizontal slider * - Vertical : Vertical slider * * @author Christophe Boulain <Christophe.Boulain@gmail.com> * @version $Id$ * @package System.Web.UI.WebControls * @since 3.1.1 */ class TSliderDirection extends TEnumerable { const Horizontal='Horizontal'; const Vertical='Vertical'; }