D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
lampp
/
lib
/
php
/
HTML
/
QuickForm2
/
Element
/
Filename :
Hierselect.php
back
Copy
<?php /** * Hierarchical select element * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2014, Alexey Borzov <avb@php.net>, * Bertrand Mansion <golgote@mamasam.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov <avb@php.net> * @author Bertrand Mansion <golgote@mamasam.com> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for HTML_QuickForm2 groups */ require_once 'HTML/QuickForm2/Container/Group.php'; /** * Classes for <select> elements */ require_once 'HTML/QuickForm2/Element/Select.php'; /** * Class for adding inline javascript to the form */ require_once 'HTML/QuickForm2/Element/Script.php'; /** * Hierarchical select element * * Class to dynamically create two or more HTML Select elements * The first select changes the content of the second select and so on. * This element is considered as a group. Selects will be named * groupName[0], groupName[1], groupName[2]... * * @category HTML * @package HTML_QuickForm2 * @author Herim Vasquez <vasquezh@iro.umontreal.ca> * @author Bertrand Mansion <bmansion@mamasam.com> * @author Alexey Borzov <avb@php.net> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: 2.0.2 * @link http://pear.php.net/package/HTML_QuickForm2 */ class HTML_QuickForm2_Element_Hierselect extends HTML_QuickForm2_Container_Group { /** * Options for all the select elements * * @see loadOptions() * @var array */ protected $options = array(); /** * PHP callback function for getting additional options * * @see loadOptions() * @var callback */ protected $callback = null; /** * Javascript callback function for getting additional options * * @see loadOptions() * @var string */ protected $jsCallback = null; /** * Number of select elements in hierselect * @var int */ protected $size = 0; /** * Values for child selects, needed for form reset handling in JS * @var array * @see _loadChildOptions() * @see _generateInlineScript() */ private $_values = array(); public function getType() { return 'hierselect'; } /** * Class constructor * * Hierselect element can understand the following keys in $data parameter: * - 'options': data to populate child elements' options with. Passed to * {@link loadOptions()} method. * - 'size': number of selects in hierselect. If not given will be set * from size of options array or size of array passed to setValue() * $data is propagated to created Select elements with these keys removed. * * @param string $name Element name * @param string|array $attributes Attributes (either a string or an array) * @param array $data Additional element data */ public function __construct($name = null, $attributes = null, array $data = array()) { if (!empty($data['size'])) { $this->size = $data['size']; } $options = isset($data['options'])? $data['options']: array(); unset($data['options'], $data['size']); parent::__construct($name, $attributes, $data); $this->loadOptions($options); } /** * Initializes the the options for each select element. * * Format is a bit more complex than for a simple select as we need to know * which options are related to the ones in the previous select: * * Ex: * <code> * // first select * $select1[0] = 'Pop'; * $select1[1] = 'Classical'; * $select1[2] = 'Funeral doom'; * * // second select * $select2[0][0] = 'Red Hot Chilly Peppers'; * $select2[0][1] = 'The Pixies'; * $select2[1][0] = 'Wagner'; * $select2[1][1] = 'Strauss'; * $select2[2][0] = 'Pantheist'; * $select2[2][1] = 'Skepticism'; * * // Two selects * $sel = $form->addElement('hierselect', 'cds')->setLabel('Choose CD:'); * $sel->loadOptions(array($select1, $select2)); * * // If you have a third select with prices for the cds * $select3[0][0][0] = '15.00$'; * $select3[0][0][1] = '17.00$'; * // etc * * // You can now use * $sel = $form->addElement('hierselect', 'cds')->setLabel('Choose CD:'); * $sel->loadOptions(array($select1, $select2, $select3)); * </code> * * @param array $options Array of options defining each element * @param callback $callback Callback function to load additional options. * It will receive an array of keys and should return associative * array ('option value' => 'option text') * @param string $jsCallback Javascript function to load additional options * (presumably via some sort of AJAX request). It will receive an * array of keys and should return {'values': [...], 'texts': [...]} * * @return $this * @throws HTML_QuickForm2_InvalidArgumentException */ public function loadOptions(array $options, $callback = null, $jsCallback = null) { if (null !== $callback && !is_callable($callback, false, $callbackName)) { throw new HTML_QuickForm2_InvalidArgumentException( 'Hierselect expects a valid callback for loading options, \'' . $callbackName . '\' was given' ); } $this->options = $options; $this->callback = $callback; $this->jsCallback = $jsCallback; $this->size = max($this->size, count($options)); $this->_createSelects(); $this->_loadChildOptions(); return $this; } /** * Populates hierselect with Select elements */ private function _createSelects() { for ($i = count($this); $i < $this->size; $i++) { $data = $this->getData(); unset($data['label']); $this->appendChild(new HTML_QuickForm2_Element_Select( $i, array('id' => self::generateId($this->getName() . "[{$i}]")) + $this->getAttributes(), $data )); } } /** * Loads options for child Select elements */ private function _loadChildOptions() { $idx = 0; $this->_values = array(); /* @var $select HTML_QuickForm2_Element_Select */ foreach ($this as $select) { if (empty($this->options[$idx])) { $this->options[$idx] = array(); } $keys = $this->_values; $array =& $this->options[$idx++]; while (!empty($keys)) { $key = array_shift($keys); if (!isset($array[$key])) { if (!empty($keys)) { $array[$key] = array(); } elseif (!empty($this->callback)) { $array[$key] = call_user_func($this->callback, $this->_values); } else { // Most probably called from constructor with neither // options nor callback provided return; } } $array =& $array[$key]; } $select->loadOptions($array); $this->_values[] = null !== ($v = $select->getValue())? $v: key($array); } } /** * Sets the element's value * * This also creates missing selects and loads their options, in addition * to {@link HTML_QuickForm2_Container_Group::setValue()} behaviour * * @param array $value * * @return $this */ public function setValue($value) { $this->size = max($this->size, count($value)); $this->_createSelects(); parent::setValue($value); $this->_loadChildOptions(); return $this; } /** * Sets the element's name * * Need to override group's implementation due to overridden updateValue() * * @param string $name * * @return $this */ public function setName($name) { parent::setName($name); $this->updateValue(); return $this; } /** * Called when the element needs to update its value from form's data sources * * Hierselect uses the Element's implementation of updateValue() since its * values need to be passed through setValue() to properly update options of * its child selects. */ protected function updateValue() { $name = $this->getName(); /* @var $ds HTML_QuickForm2_DataSource_NullAware */ foreach ($this->getDataSources() as $ds) { if (null !== ($value = $ds->getValue($name)) || $ds instanceof HTML_QuickForm2_DataSource_NullAware && $ds->hasValue($name) ) { $this->setValue($value); return; } } } /** * Prepares options for JS encoding * * We need to preserve order of options when adding them via javascript, so * cannot use object literal and for/in loop (see bug #16603). Therefore we * convert an associative array of options to two arrays of their values * and texts. * * @param array $ary Options array * @param int $depth Depth within options array * * @link http://pear.php.net/bugs/bug.php?id=16603 * @return array Array with separate options and texts */ private function _prepareOptions($ary, $depth) { if (!is_array($ary)) { $ret = $ary; } elseif (0 == $depth) { $ret = array('values' => array_keys($ary), 'texts' => array_values($ary)); } else { $ret = array(); foreach ($ary as $k => $v) { $ret[$k] = $this->_prepareOptions($v, $depth - 1); } } return $ret; } /** * Generates inline javascript containing element's defaults and (available) options * * @return string */ private function _generateInlineScript() { // we store values and options with id of first select rather than with // the element's name since the former has more chances to be unique $selectId = reset($this->elements)->getId(); $cr = HTML_Common2::getOption('linebreak'); $js = "qf.elements.hierselect.defaults['{$selectId}'] = " . HTML_QuickForm2_JavascriptBuilder::encode($this->_values) . ";{$cr}"; $jsParts = array(); for ($i = 1; $i < count($this->options); $i++) { $jsParts[] = empty($this->options[$i]) ? '{}' : HTML_QuickForm2_JavascriptBuilder::encode($this->_prepareOptions( $this->options[$i], $i )); } $js .= "qf.elements.hierselect.options['{$selectId}'] = [{$cr}" . implode(",{$cr}", $jsParts) . "{$cr}];"; return $js; } /** * Generates a javascript function call to initialize hierselect behaviour * * @return string */ private function _generateInitScript() { HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder'); $ids = array(); /* @var $element HTML_QuickForm2_Element */ foreach ($this as $element) { $ids[] = $element->getId(); } return 'qf.elements.hierselect.init(' . HTML_QuickForm2_JavascriptBuilder::encode($ids) . (empty($this->jsCallback)? '': ", {$this->jsCallback}") . ');'; } /** * Renders the hierselect using the given renderer * * @param HTML_QuickForm2_Renderer $renderer * * @return HTML_QuickForm2_Renderer * @throws HTML_QuickForm2_Exception if number of selects in hierselect cannot * be determined */ public function render(HTML_QuickForm2_Renderer $renderer) { if (0 == $this->size) { throw new HTML_QuickForm2_Exception( 'Unable to determine number of selects in hierselect' ); } if ($this->toggleFrozen()) { // frozen hierselect does not need any javascript return parent::render($renderer); } $jsBuilder = $renderer->getJavascriptBuilder(); $jsBuilder->addLibrary('hierselect', 'quickform-hierselect.js'); $jsBuilder->addElementJavascript($this->_generateInitScript()); $script = $this->appendChild(new HTML_QuickForm2_Element_Script('script')) ->setContent($this->_generateInlineScript()); parent::render($renderer); $this->removeChild($script); return $renderer; } } ?>