PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/system/library/PEAR/HTML/QuickForm2/Element/Select.php

https://bitbucket.org/spekkionu/passworddb
PHP | 585 lines | 263 code | 41 blank | 281 comment | 41 complexity | c24f6007ee052f72e742de1bbb03d168 MD5 | raw file
Possible License(s): BSD-2-Clause
  1. <?php
  2. /**
  3. * Classes for <select> elements
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2006-2012, Alexey Borzov <avb@php.net>,
  10. * Bertrand Mansion <golgote@mamasam.com>
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTML
  38. * @package HTML_QuickForm2
  39. * @author Alexey Borzov <avb@php.net>
  40. * @author Bertrand Mansion <golgote@mamasam.com>
  41. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42. * @version SVN: $Id: Select.php 323363 2012-02-19 15:09:07Z avb $
  43. * @link http://pear.php.net/package/HTML_QuickForm2
  44. */
  45. /**
  46. * Base class for simple HTML_QuickForm2 elements
  47. */
  48. require_once 'HTML/QuickForm2/Element.php';
  49. /**
  50. * Collection of <option>s and <optgroup>s
  51. *
  52. * This class handles the output of <option> tags. The class is not intended to
  53. * be used directly.
  54. *
  55. * @category HTML
  56. * @package HTML_QuickForm2
  57. * @author Alexey Borzov <avb@php.net>
  58. * @author Bertrand Mansion <golgote@mamasam.com>
  59. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  60. * @version Release: 2.0.0
  61. * @link http://pear.php.net/package/HTML_QuickForm2
  62. */
  63. class HTML_QuickForm2_Element_Select_OptionContainer extends HTML_Common2
  64. implements IteratorAggregate, Countable
  65. {
  66. /**
  67. * List of options and optgroups in this container
  68. *
  69. * Options are stored as arrays (for performance reasons), optgroups as
  70. * instances of Optgroup class.
  71. *
  72. * @var array
  73. */
  74. protected $options = array();
  75. /**
  76. * Reference to parent <select>'s values
  77. * @var array
  78. */
  79. protected $values;
  80. /**
  81. * Reference to parent <select>'s possible values
  82. * @var array
  83. */
  84. protected $possibleValues;
  85. /**
  86. * Class constructor
  87. *
  88. * @param array &$values Reference to values of parent <select> element
  89. * @param array &$possibleValues Reference to possible values of parent <select> element
  90. */
  91. public function __construct(&$values, &$possibleValues)
  92. {
  93. $this->values =& $values;
  94. $this->possibleValues =& $possibleValues;
  95. }
  96. /**
  97. * Adds a new option
  98. *
  99. * Please note that if you pass 'selected' attribute in the $attributes
  100. * parameter then this option's value will be added to <select>'s values.
  101. *
  102. * @param string $text Option text
  103. * @param string $value 'value' attribute for <option> tag
  104. * @param string|array $attributes Additional attributes for <option> tag
  105. * (either as a string or as an associative array)
  106. */
  107. public function addOption($text, $value, $attributes = null)
  108. {
  109. if (null === $attributes) {
  110. $attributes = array('value' => (string)$value);
  111. } else {
  112. $attributes = self::prepareAttributes($attributes);
  113. if (isset($attributes['selected'])) {
  114. // the 'selected' attribute will be set in __toString()
  115. unset($attributes['selected']);
  116. if (!in_array($value, $this->values)) {
  117. $this->values[] = $value;
  118. }
  119. }
  120. $attributes['value'] = (string)$value;
  121. }
  122. if (!isset($attributes['disabled'])) {
  123. $this->possibleValues[(string)$value] = true;
  124. }
  125. $this->options[] = array('text' => $text, 'attr' => $attributes);
  126. }
  127. /**
  128. * Adds a new optgroup
  129. *
  130. * @param string $label 'label' attribute for optgroup tag
  131. * @param string|array $attributes Additional attributes for <optgroup> tag
  132. * (either as a string or as an associative array)
  133. *
  134. * @return HTML_QuickForm2_Element_Select_Optgroup
  135. */
  136. public function addOptgroup($label, $attributes = null)
  137. {
  138. $optgroup = new HTML_QuickForm2_Element_Select_Optgroup(
  139. $this->values, $this->possibleValues, $label, $attributes
  140. );
  141. $this->options[] = $optgroup;
  142. return $optgroup;
  143. }
  144. /**
  145. * Returns an array of contained options
  146. *
  147. * @return array
  148. */
  149. public function getOptions()
  150. {
  151. return $this->options;
  152. }
  153. public function __toString()
  154. {
  155. $indentLvl = $this->getIndentLevel();
  156. $indent = $this->getIndent() . self::getOption('indent');
  157. $linebreak = self::getOption('linebreak');
  158. $html = '';
  159. $strValues = array_map('strval', $this->values);
  160. foreach ($this->options as $option) {
  161. if (is_array($option)) {
  162. if (in_array($option['attr']['value'], $strValues, true)) {
  163. $option['attr']['selected'] = 'selected';
  164. }
  165. $html .= $indent . '<option' .
  166. self::getAttributesString($option['attr']) .
  167. '>' . $option['text'] . '</option>' . $linebreak;
  168. } elseif ($option instanceof HTML_QuickForm2_Element_Select_OptionContainer) {
  169. $option->setIndentLevel($indentLvl + 1);
  170. $html .= $option->__toString();
  171. }
  172. }
  173. return $html;
  174. }
  175. /**
  176. * Returns an iterator over contained elements
  177. *
  178. * @return HTML_QuickForm2_Element_Select_OptionIterator
  179. */
  180. public function getIterator()
  181. {
  182. return new HTML_QuickForm2_Element_Select_OptionIterator($this->options);
  183. }
  184. /**
  185. * Returns a recursive iterator over contained elements
  186. *
  187. * @return RecursiveIteratorIterator
  188. */
  189. public function getRecursiveIterator()
  190. {
  191. return new RecursiveIteratorIterator(
  192. new HTML_QuickForm2_Element_Select_OptionIterator($this->options),
  193. RecursiveIteratorIterator::SELF_FIRST
  194. );
  195. }
  196. /**
  197. * Returns the number of options in the container
  198. *
  199. * @return int
  200. */
  201. public function count()
  202. {
  203. return count($this->options);
  204. }
  205. }
  206. /**
  207. * Class representing an <optgroup> tag
  208. *
  209. * Do not instantiate this class yourself, use
  210. * {@link HTML_QuickForm2_Element_Select::addOptgroup()} method
  211. *
  212. * @category HTML
  213. * @package HTML_QuickForm2
  214. * @author Alexey Borzov <avb@php.net>
  215. * @author Bertrand Mansion <golgote@mamasam.com>
  216. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  217. * @version Release: 2.0.0
  218. * @link http://pear.php.net/package/HTML_QuickForm2
  219. */
  220. class HTML_QuickForm2_Element_Select_Optgroup
  221. extends HTML_QuickForm2_Element_Select_OptionContainer
  222. {
  223. /**
  224. * Class constructor
  225. *
  226. * @param array &$values Reference to values of parent <select> element
  227. * @param array &$possibleValues Reference to possible values of parent <select> element
  228. * @param string $label 'label' attribute for optgroup tag
  229. * @param string|array $attributes Additional attributes for <optgroup> tag
  230. * (either as a string or as an associative array)
  231. */
  232. public function __construct(&$values, &$possibleValues, $label, $attributes = null)
  233. {
  234. parent::__construct($values, $possibleValues);
  235. $this->setAttributes($attributes);
  236. $this->attributes['label'] = (string)$label;
  237. }
  238. public function __toString()
  239. {
  240. $indent = $this->getIndent();
  241. $linebreak = self::getOption('linebreak');
  242. return $indent . '<optgroup' . $this->getAttributes(true) . '>' .
  243. $linebreak . parent::__toString() . $indent . '</optgroup>' . $linebreak;
  244. }
  245. }
  246. /**
  247. * Implements a recursive iterator for options arrays
  248. *
  249. * @category HTML
  250. * @package HTML_QuickForm2
  251. * @author Alexey Borzov <avb@php.net>
  252. * @author Bertrand Mansion <golgote@mamasam.com>
  253. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  254. * @version Release: 2.0.0
  255. * @link http://pear.php.net/package/HTML_QuickForm2
  256. */
  257. class HTML_QuickForm2_Element_Select_OptionIterator extends RecursiveArrayIterator
  258. implements RecursiveIterator
  259. {
  260. public function hasChildren()
  261. {
  262. return $this->current() instanceof HTML_QuickForm2_Element_Select_OptionContainer;
  263. }
  264. public function getChildren()
  265. {
  266. return new HTML_QuickForm2_Element_Select_OptionIterator(
  267. $this->current()->getOptions()
  268. );
  269. }
  270. }
  271. /**
  272. * Class representing a <select> element
  273. *
  274. * @category HTML
  275. * @package HTML_QuickForm2
  276. * @author Alexey Borzov <avb@php.net>
  277. * @author Bertrand Mansion <golgote@mamasam.com>
  278. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  279. * @version Release: 2.0.0
  280. * @link http://pear.php.net/package/HTML_QuickForm2
  281. */
  282. class HTML_QuickForm2_Element_Select extends HTML_QuickForm2_Element
  283. {
  284. protected $persistent = true;
  285. /**
  286. * Values for the select element (i.e. values of the selected options)
  287. * @var array
  288. */
  289. protected $values = array();
  290. /**
  291. * Possible values for select elements
  292. *
  293. * A value is considered possible if it is present as a value attribute of
  294. * some option and that option is not disabled.
  295. * @var array
  296. */
  297. protected $possibleValues = array();
  298. /**
  299. * Object containing options for the <select> element
  300. * @var HTML_QuickForm2_Element_Select_OptionContainer
  301. */
  302. protected $optionContainer;
  303. /**
  304. * Enable intrinsic validation by default
  305. * @var array
  306. */
  307. protected $data = array('intrinsic_validation' => true);
  308. /**
  309. * Class constructor
  310. *
  311. * Select element can understand the following keys in $data parameter:
  312. * - 'options': data to populate element's options with. Passed to
  313. * {@link loadOptions()} method.
  314. * - 'intrinsic_validation': setting this to false will disable
  315. * that validation, {@link getValue()} will then return all submit
  316. * values, not just those corresponding to options present in the
  317. * element. May be useful in AJAX scenarios.
  318. *
  319. * @param string $name Element name
  320. * @param string|array $attributes Attributes (either a string or an array)
  321. * @param array $data Additional element data
  322. *
  323. * @throws HTML_QuickForm2_InvalidArgumentException if junk is given in $options
  324. */
  325. public function __construct($name = null, $attributes = null, array $data = array())
  326. {
  327. $options = isset($data['options'])? $data['options']: array();
  328. unset($data['options']);
  329. parent::__construct($name, $attributes, $data);
  330. $this->loadOptions($options);
  331. }
  332. public function getType()
  333. {
  334. return 'select';
  335. }
  336. public function __toString()
  337. {
  338. if ($this->frozen) {
  339. return $this->getFrozenHtml();
  340. } else {
  341. if (empty($this->attributes['multiple'])) {
  342. $attrString = $this->getAttributes(true);
  343. } else {
  344. $this->attributes['name'] .= '[]';
  345. $attrString = $this->getAttributes(true);
  346. $this->attributes['name'] = substr($this->attributes['name'], 0, -2);
  347. }
  348. $indent = $this->getIndent();
  349. return $indent . '<select' . $attrString . '>' .
  350. self::getOption('linebreak') .
  351. $this->optionContainer->__toString() .
  352. $indent . '</select>';
  353. }
  354. }
  355. protected function getFrozenHtml()
  356. {
  357. if (null === ($value = $this->getValue())) {
  358. return '&nbsp;';
  359. }
  360. $valueHash = is_array($value)? array_flip($value): array($value => true);
  361. $options = array();
  362. foreach ($this->optionContainer->getRecursiveIterator() as $child) {
  363. if (is_array($child) && isset($valueHash[$child['attr']['value']])
  364. && empty($child['attr']['disabled'])
  365. ) {
  366. $options[] = $child['text'];
  367. }
  368. }
  369. $html = implode('<br />', $options);
  370. if ($this->persistent) {
  371. $name = $this->attributes['name'] .
  372. (empty($this->attributes['multiple'])? '': '[]');
  373. // Only use id attribute if doing single hidden input
  374. $idAttr = (1 == count($valueHash))? array('id' => $this->getId()): array();
  375. foreach ($valueHash as $key => $item) {
  376. $html .= '<input type="hidden"' . self::getAttributesString(array(
  377. 'name' => $name,
  378. 'value' => $key
  379. ) + $idAttr) . ' />';
  380. }
  381. }
  382. return $html;
  383. }
  384. /**
  385. * Returns the value of the <select> element
  386. *
  387. * Please note that the returned value may not necessarily be equal to that
  388. * passed to {@link setValue()}. It passes "intrinsic validation" confirming
  389. * that such value could possibly be submitted by this <select> element.
  390. * Specifically, this method will return null if the elements "disabled"
  391. * attribute is set, it will not return values if there are no options having
  392. * such a "value" attribute or if such options' "disabled" attribute is set.
  393. * It will also only return a scalar value for single selects, mimicking
  394. * the common browsers' behaviour.
  395. *
  396. * @return mixed "value" attribute of selected option in case of single
  397. * select, array of selected options' "value" attributes in
  398. * case of multiple selects, null if no options selected
  399. */
  400. public function getRawValue()
  401. {
  402. if (!empty($this->attributes['disabled']) || 0 == count($this->values)
  403. || ($this->data['intrinsic_validation']
  404. && (0 == count($this->optionContainer) || 0 == count($this->possibleValues)))
  405. ) {
  406. return null;
  407. }
  408. $values = array();
  409. foreach ($this->values as $value) {
  410. if (!$this->data['intrinsic_validation'] || !empty($this->possibleValues[$value])) {
  411. $values[] = $value;
  412. }
  413. }
  414. if (0 == count($values)) {
  415. return null;
  416. } elseif (!empty($this->attributes['multiple'])) {
  417. return $values;
  418. } elseif (1 == count($values)) {
  419. return $values[0];
  420. } else {
  421. // The <select> is not multiple, but several options are to be
  422. // selected. At least IE and Mozilla select the last selected
  423. // option in this case, we should do the same
  424. foreach ($this->optionContainer->getRecursiveIterator() as $child) {
  425. if (is_array($child) && in_array($child['attr']['value'], $values)) {
  426. $lastValue = $child['attr']['value'];
  427. }
  428. }
  429. return $lastValue;
  430. }
  431. }
  432. public function setValue($value)
  433. {
  434. if (is_array($value)) {
  435. $this->values = array_values($value);
  436. } else {
  437. $this->values = array($value);
  438. }
  439. return $this;
  440. }
  441. /**
  442. * Loads <option>s (and <optgroup>s) for select element
  443. *
  444. * The method expects a array of options and optgroups:
  445. * <pre>
  446. * array(
  447. * 'option value 1' => 'option text 1',
  448. * ...
  449. * 'option value N' => 'option text N',
  450. * 'optgroup label 1' => array(
  451. * 'option value' => 'option text',
  452. * ...
  453. * ),
  454. * ...
  455. * )
  456. * </pre>
  457. * If value is a scalar, then array key is treated as "value" attribute of
  458. * <option> and value as this <option>'s text. If value is an array, then
  459. * key is treated as a "label" attribute of <optgroup> and value as an
  460. * array of <option>s for this <optgroup>.
  461. *
  462. * If you need to specify additional attributes for <option> and <optgroup>
  463. * tags, then you need to use {@link addOption()} and {@link addOptgroup()}
  464. * methods instead of this one.
  465. *
  466. * @param array $options
  467. *
  468. * @throws HTML_QuickForm2_InvalidArgumentException if junk is given in $options
  469. * @return HTML_QuickForm2_Element_Select
  470. */
  471. public function loadOptions(array $options)
  472. {
  473. $this->possibleValues = array();
  474. $this->optionContainer = new HTML_QuickForm2_Element_Select_OptionContainer(
  475. $this->values, $this->possibleValues
  476. );
  477. $this->loadOptionsFromArray($this->optionContainer, $options);
  478. return $this;
  479. }
  480. /**
  481. * Adds options from given array into given container
  482. *
  483. * @param HTML_QuickForm2_Element_Select_OptionContainer $container options will be
  484. * added to this container
  485. * @param array $options options array
  486. */
  487. protected function loadOptionsFromArray(
  488. HTML_QuickForm2_Element_Select_OptionContainer $container, $options
  489. ) {
  490. foreach ($options as $key => $value) {
  491. if (is_array($value)) {
  492. $optgroup = $container->addOptgroup($key);
  493. $this->loadOptionsFromArray($optgroup, $value);
  494. } else {
  495. $container->addOption($value, $key);
  496. }
  497. }
  498. }
  499. /**
  500. * Adds a new option
  501. *
  502. * Please note that if you pass 'selected' attribute in the $attributes
  503. * parameter then this option's value will be added to <select>'s values.
  504. *
  505. * @param string $text Option text
  506. * @param string $value 'value' attribute for <option> tag
  507. * @param string|array $attributes Additional attributes for <option> tag
  508. * (either as a string or as an associative array)
  509. */
  510. public function addOption($text, $value, $attributes = null)
  511. {
  512. return $this->optionContainer->addOption($text, $value, $attributes);
  513. }
  514. /**
  515. * Adds a new optgroup
  516. *
  517. * @param string $label 'label' attribute for optgroup tag
  518. * @param string|array $attributes Additional attributes for <optgroup> tag
  519. * (either as a string or as an associative array)
  520. *
  521. * @return HTML_QuickForm2_Element_Select_Optgroup
  522. */
  523. public function addOptgroup($label, $attributes = null)
  524. {
  525. return $this->optionContainer->addOptgroup($label, $attributes);
  526. }
  527. protected function updateValue()
  528. {
  529. if (!$this->getAttribute('multiple')) {
  530. parent::updateValue();
  531. } else {
  532. $name = $this->getName();
  533. foreach ($this->getDataSources() as $ds) {
  534. if (null !== ($value = $ds->getValue($name))
  535. || $ds instanceof HTML_QuickForm2_DataSource_Submit
  536. ) {
  537. $this->setValue(null === $value? array(): $value);
  538. return;
  539. }
  540. }
  541. }
  542. }
  543. }
  544. ?>