PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/php-pear-HTML-QuickForm-advmultiselect-1.5.1/HTML_QuickForm_advmultiselect-1.5.1/advmultiselect.php

#
PHP | 1178 lines | 670 code | 90 blank | 418 comment | 81 complexity | cc19c38a8530cdd4cbdfb76fa186f4e4 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright (c) 2005-2009, Laurent Laville <pear@laurent-laville.org>
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * * Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * * Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * * Neither the name of the authors nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  24. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  25. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  26. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  27. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  29. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30. * POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. * PHP versions 4 and 5
  33. *
  34. * @category HTML
  35. * @package HTML_QuickForm_advmultiselect
  36. * @author Laurent Laville <pear@laurent-laville.org>
  37. * @copyright 2005-2009 Laurent Laville
  38. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  39. * @version CVS: $Id: advmultiselect.php,v 1.36 2009/04/05 07:03:39 farell Exp $
  40. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  41. * @since File available since Release 0.4.0
  42. */
  43. require_once 'HTML/QuickForm/select.php';
  44. /**
  45. * Basic error codes
  46. *
  47. * @var integer
  48. * @since 1.5.0
  49. */
  50. define('HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT', 1);
  51. /**
  52. * Element for HTML_QuickForm that emulate a multi-select.
  53. *
  54. * The HTML_QuickForm_advmultiselect package adds an element to the
  55. * HTML_QuickForm package that is two select boxes next to each other
  56. * emulating a multi-select.
  57. *
  58. * @category HTML
  59. * @package HTML_QuickForm_advmultiselect
  60. * @author Laurent Laville <pear@laurent-laville.org>
  61. * @copyright 2005-2009 Laurent Laville
  62. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  63. * @version Release: @package_version@
  64. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  65. * @since Class available since Release 0.4.0
  66. */
  67. class HTML_QuickForm_advmultiselect extends HTML_QuickForm_select
  68. {
  69. /**
  70. * Prefix function name in javascript move selections
  71. *
  72. * @var string
  73. * @access private
  74. * @since 0.4.0
  75. */
  76. var $_jsPrefix;
  77. /**
  78. * Postfix function name in javascript move selections
  79. *
  80. * @var string
  81. * @access private
  82. * @since 0.4.0
  83. */
  84. var $_jsPostfix;
  85. /**
  86. * Associative array of the multi select container attributes
  87. *
  88. * @var array
  89. * @access private
  90. * @since 0.4.0
  91. */
  92. var $_tableAttributes;
  93. /**
  94. * Associative array of the add button attributes
  95. *
  96. * @var array
  97. * @access private
  98. * @since 0.4.0
  99. */
  100. var $_addButtonAttributes;
  101. /**
  102. * Associative array of the remove button attributes
  103. *
  104. * @var array
  105. * @access private
  106. * @since 0.4.0
  107. */
  108. var $_removeButtonAttributes;
  109. /**
  110. * Associative array of the select all button attributes
  111. *
  112. * @var array
  113. * @access private
  114. * @since 1.1.0
  115. */
  116. var $_allButtonAttributes;
  117. /**
  118. * Associative array of the select none button attributes
  119. *
  120. * @var array
  121. * @access private
  122. * @since 1.1.0
  123. */
  124. var $_noneButtonAttributes;
  125. /**
  126. * Associative array of the toggle selection button attributes
  127. *
  128. * @var array
  129. * @access private
  130. * @since 1.1.0
  131. */
  132. var $_toggleButtonAttributes;
  133. /**
  134. * Associative array of the move up button attributes
  135. *
  136. * @var array
  137. * @access private
  138. * @since 0.5.0
  139. */
  140. var $_upButtonAttributes;
  141. /**
  142. * Associative array of the move up button attributes
  143. *
  144. * @var array
  145. * @access private
  146. * @since 0.5.0
  147. */
  148. var $_downButtonAttributes;
  149. /**
  150. * Associative array of the move top button attributes
  151. *
  152. * @var array
  153. * @access private
  154. * @since 1.5.0
  155. */
  156. var $_topButtonAttributes;
  157. /**
  158. * Associative array of the move bottom button attributes
  159. *
  160. * @var array
  161. * @access private
  162. * @since 1.5.0
  163. */
  164. var $_bottomButtonAttributes;
  165. /**
  166. * Defines if both list (unselected, selected) will have their elements be
  167. * arranged from lowest to highest (or reverse)
  168. * depending on comparaison function.
  169. *
  170. * SORT_ASC is used to sort in ascending order
  171. * SORT_DESC is used to sort in descending order
  172. *
  173. * @var string ('none' == false, 'asc' == SORT_ASC, 'desc' == SORT_DESC)
  174. * @access private
  175. * @since 0.5.0
  176. */
  177. var $_sort;
  178. /**
  179. * Associative array of the unselected item box attributes
  180. *
  181. * @var array
  182. * @access private
  183. * @since 0.4.0
  184. */
  185. var $_attributesUnselected;
  186. /**
  187. * Associative array of the selected item box attributes
  188. *
  189. * @var array
  190. * @access private
  191. * @since 0.4.0
  192. */
  193. var $_attributesSelected;
  194. /**
  195. * Associative array of the internal hidden box attributes
  196. *
  197. * @var array
  198. * @access private
  199. * @since 0.4.0
  200. */
  201. var $_attributesHidden;
  202. /**
  203. * Default Element template string
  204. *
  205. * @var string
  206. * @access private
  207. * @since 0.4.0
  208. */
  209. var $_elementTemplate;
  210. /**
  211. * Default Element stylesheet string
  212. *
  213. * @var string
  214. * @access private
  215. * @since 0.4.0
  216. */
  217. var $_elementCSS = '
  218. #qfams_{id} {
  219. font: 13.3px sans-serif;
  220. background-color: #fff;
  221. overflow: auto;
  222. height: 14.3em;
  223. width: 12em;
  224. border-left: 1px solid #404040;
  225. border-top: 1px solid #404040;
  226. border-bottom: 1px solid #d4d0c8;
  227. border-right: 1px solid #d4d0c8;
  228. }
  229. #qfams_{id} label {
  230. padding-right: 3px;
  231. display: block;
  232. }
  233. ';
  234. /**
  235. * Class constructor
  236. *
  237. * Class constructors :
  238. * Zend Engine 1 uses HTML_QuickForm_advmultiselect, while
  239. * Zend Engine 2 uses __construct
  240. *
  241. * @param string $elementName Dual Select name attribute
  242. * @param mixed $elementLabel Label(s) for the select boxes
  243. * @param mixed $options Data to be used to populate options
  244. * @param mixed $attributes Either a typical HTML attribute string or
  245. * an associative array
  246. * @param integer $sort Either SORT_ASC for auto ascending arrange,
  247. * SORT_DESC for auto descending arrange, or
  248. * NULL for no sort (append at end: default)
  249. *
  250. * @access public
  251. * @return void
  252. * @since version 0.4.0 (2005-06-25)
  253. */
  254. function HTML_QuickForm_advmultiselect($elementName = null, $elementLabel = null,
  255. $options = null, $attributes = null,
  256. $sort = null)
  257. {
  258. $opts = $options;
  259. $options = null; // prevent to use the default select element load options
  260. $this->HTML_QuickForm_select($elementName, $elementLabel,
  261. $options, $attributes);
  262. // allow to load options at once and take care of fancy attributes
  263. $this->load($opts);
  264. // add multiple selection attribute by default if missing
  265. $this->updateAttributes(array('multiple' => 'multiple'));
  266. if (is_null($this->getAttribute('size'))) {
  267. // default size is ten item on each select box (left and right)
  268. $this->updateAttributes(array('size' => 10));
  269. }
  270. if (is_null($this->getAttribute('style'))) {
  271. // default width of each select box
  272. $this->updateAttributes(array('style' => 'width:100px;'));
  273. }
  274. $this->_tableAttributes = $this->getAttribute('class');
  275. if (is_null($this->_tableAttributes)) {
  276. // default table layout
  277. $attr = array('border' => '0',
  278. 'cellpadding' => '10', 'cellspacing' => '0');
  279. } else {
  280. $attr = array('class' => $this->_tableAttributes);
  281. $this->_removeAttr('class', $this->_attributes);
  282. }
  283. $this->_tableAttributes = $this->_getAttrString($attr);
  284. // set default add button attributes
  285. $this->setButtonAttributes('add');
  286. // set default remove button attributes
  287. $this->setButtonAttributes('remove');
  288. // set default selectall button attributes
  289. $this->setButtonAttributes('all');
  290. // set default selectnone button attributes
  291. $this->setButtonAttributes('none');
  292. // set default toggle selection button attributes
  293. $this->setButtonAttributes('toggle');
  294. // set default move up button attributes
  295. $this->setButtonAttributes('moveup');
  296. // set default move up button attributes
  297. $this->setButtonAttributes('movedown');
  298. // set default move top button attributes
  299. $this->setButtonAttributes('movetop');
  300. // set default move bottom button attributes
  301. $this->setButtonAttributes('movebottom');
  302. // defines javascript functions names
  303. $this->_jsPrefix = 'QFAMS.';
  304. $this->_jsPostfix = 'moveSelection';
  305. // set select boxes sort order (none by default)
  306. if (!isset($sort)) {
  307. $sort = false;
  308. }
  309. if ($sort === SORT_ASC) {
  310. $this->_sort = 'asc';
  311. } elseif ($sort === SORT_DESC) {
  312. $this->_sort = 'desc';
  313. } else {
  314. $this->_sort = 'none';
  315. }
  316. // set the default advmultiselect element template (with javascript embedded)
  317. $this->setElementTemplate();
  318. }
  319. /**
  320. * Sets the button attributes
  321. *
  322. * In <b>custom example 1</b>, the <i>add</i> and <i>remove</i> buttons
  323. * have look set by the css class <i>inputCommand</i>.
  324. *
  325. * In <b>custom example 2</b>, the basic text <i>add</i> and <i>remove</i>
  326. * buttons are now replaced by images.
  327. *
  328. * In <b>custom example 5</b>, we have ability to sort the selection list
  329. * (on right side) by :
  330. * <pre>
  331. * - <b>user-end</b>: with <i>Up</i> and <i>Down</i> buttons
  332. * - <b>programming</b>: with the QF element constructor $sort option
  333. * </pre>
  334. *
  335. * @param string $button Button identifier, either 'add', 'remove',
  336. * 'all', 'none', 'toggle',
  337. * 'movetop', 'movebottom'
  338. * 'moveup' or 'movedown'
  339. * @param mixed $attributes (optional) Either a typical HTML attribute string
  340. * or an associative array
  341. *
  342. * @return void
  343. * @throws PEAR_Error $button argument
  344. * is not a string, or not in range
  345. * (add, remove, all, none, toggle,
  346. * movetop, movebottom, moveup, movedown)
  347. * @access public
  348. * @since version 0.4.0 (2005-06-25)
  349. *
  350. * @example examples/qfams_custom_5.php
  351. * Custom example 5: source code
  352. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom5.png
  353. * Custom example 5: screenshot
  354. *
  355. * @example examples/qfams_custom_2.php
  356. * Custom example 2: source code
  357. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom2.png
  358. * Custom example 2: screenshot
  359. *
  360. * @example examples/qfams_custom_1.php
  361. * Custom example 1: source code
  362. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom1.png
  363. * Custom example 1: screenshot
  364. */
  365. function setButtonAttributes($button, $attributes = null)
  366. {
  367. if (!is_string($button)) {
  368. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  369. 'setButtonAttributes is not a string',
  370. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  371. array('level' => 'exception'));
  372. }
  373. switch ($button) {
  374. case 'add':
  375. if (is_null($attributes)) {
  376. $this->_addButtonAttributes
  377. = array('name' => 'add',
  378. 'value' => ' >> ',
  379. 'type' => 'button');
  380. } else {
  381. $this->_updateAttrArray($this->_addButtonAttributes,
  382. $this->_parseAttributes($attributes));
  383. }
  384. break;
  385. case 'remove':
  386. if (is_null($attributes)) {
  387. $this->_removeButtonAttributes
  388. = array('name' => 'remove',
  389. 'value' => ' << ',
  390. 'type' => 'button');
  391. } else {
  392. $this->_updateAttrArray($this->_removeButtonAttributes,
  393. $this->_parseAttributes($attributes));
  394. }
  395. break;
  396. case 'all':
  397. if (is_null($attributes)) {
  398. $this->_allButtonAttributes
  399. = array('name' => 'all',
  400. 'value' => ' Select All ',
  401. 'type' => 'button');
  402. } else {
  403. $this->_updateAttrArray($this->_allButtonAttributes,
  404. $this->_parseAttributes($attributes));
  405. }
  406. break;
  407. case 'none':
  408. if (is_null($attributes)) {
  409. $this->_noneButtonAttributes
  410. = array('name' => 'none',
  411. 'value' => ' Select None ',
  412. 'type' => 'button');
  413. } else {
  414. $this->_updateAttrArray($this->_noneButtonAttributes,
  415. $this->_parseAttributes($attributes));
  416. }
  417. break;
  418. case 'toggle':
  419. if (is_null($attributes)) {
  420. $this->_toggleButtonAttributes
  421. = array('name' => 'toggle',
  422. 'value' => ' Toggle Selection ',
  423. 'type' => 'button');
  424. } else {
  425. $this->_updateAttrArray($this->_toggleButtonAttributes,
  426. $this->_parseAttributes($attributes));
  427. }
  428. break;
  429. case 'moveup':
  430. if (is_null($attributes)) {
  431. $this->_upButtonAttributes
  432. = array('name' => 'up',
  433. 'value' => ' Up ',
  434. 'type' => 'button');
  435. } else {
  436. $this->_updateAttrArray($this->_upButtonAttributes,
  437. $this->_parseAttributes($attributes));
  438. }
  439. break;
  440. case 'movedown':
  441. if (is_null($attributes)) {
  442. $this->_downButtonAttributes
  443. = array('name' => 'down',
  444. 'value' => ' Down ',
  445. 'type' => 'button');
  446. } else {
  447. $this->_updateAttrArray($this->_downButtonAttributes,
  448. $this->_parseAttributes($attributes));
  449. }
  450. break;
  451. case 'movetop':
  452. if (is_null($attributes)) {
  453. $this->_topButtonAttributes
  454. = array('name' => 'top',
  455. 'value' => ' Top ',
  456. 'type' => 'button');
  457. } else {
  458. $this->_updateAttrArray($this->_topButtonAttributes,
  459. $this->_parseAttributes($attributes));
  460. }
  461. break;
  462. case 'movebottom':
  463. if (is_null($attributes)) {
  464. $this->_bottomButtonAttributes
  465. = array('name' => 'bottom',
  466. 'value' => ' Bottom ',
  467. 'type' => 'button');
  468. } else {
  469. $this->_updateAttrArray($this->_bottomButtonAttributes,
  470. $this->_parseAttributes($attributes));
  471. }
  472. break;
  473. default;
  474. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  475. 'setButtonAttributes has unexpected value',
  476. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  477. array('level' => 'error'));
  478. }
  479. }
  480. /**
  481. * Sets element template
  482. *
  483. * @param string $html (optional) The HTML surrounding select boxes and buttons
  484. * @param string $js (optional) if we need to include qfams javascript handler
  485. *
  486. * @access public
  487. * @return string
  488. * @since version 0.4.0 (2005-06-25)
  489. */
  490. function setElementTemplate($html = null, $js = true)
  491. {
  492. $oldTemplate = $this->_elementTemplate;
  493. if (isset($html) && is_string($html)) {
  494. $this->_elementTemplate = $html;
  495. } else {
  496. $this->_elementTemplate = '
  497. {javascript}
  498. <table{class}>
  499. <!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
  500. <!-- BEGIN label_3 --><th>&nbsp;</th><th>{label_3}</th></tr><!-- END label_3 -->
  501. <tr>
  502. <td valign="top">{unselected}</td>
  503. <td align="center">{add}{remove}</td>
  504. <td valign="top">{selected}</td>
  505. </tr>
  506. </table>
  507. ';
  508. }
  509. if ($js == false) {
  510. $this->_elementTemplate = str_replace('{javascript}', '',
  511. $this->_elementTemplate);
  512. }
  513. return $oldTemplate;
  514. }
  515. /**
  516. * Gets default element stylesheet for a single multi-select shape render
  517. *
  518. * In <b>custom example 4</b>, the template defined allows
  519. * a single multi-select checkboxes shape. Useful when javascript is disabled
  520. * (or when browser is not js compliant). In our example, no need to add
  521. * javascript code, but css is mandatory.
  522. *
  523. * @param boolean $raw (optional) html output with style tags or just raw data
  524. *
  525. * @access public
  526. * @return string
  527. * @since version 0.4.0 (2005-06-25)
  528. *
  529. * @example qfams_custom_4.php
  530. * Custom example 4: source code
  531. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom4.png
  532. * Custom example 4: screenshot
  533. */
  534. function getElementCss($raw = true)
  535. {
  536. $id = $this->getAttribute('name');
  537. $css = str_replace('{id}', $id, $this->_elementCSS);
  538. if ($raw !== true) {
  539. $css = '<style type="text/css">' . PHP_EOL
  540. . '<!--' . $css . ' -->' . PHP_EOL
  541. . '</style>';
  542. }
  543. return $css;
  544. }
  545. /**
  546. * Returns the HTML generated for the advanced mutliple select component
  547. *
  548. * @access public
  549. * @return string
  550. * @since version 0.4.0 (2005-06-25)
  551. */
  552. function toHtml()
  553. {
  554. if ($this->_flagFrozen) {
  555. return $this->getFrozenHtml();
  556. }
  557. $tabs = $this->_getTabs();
  558. $tab = $this->_getTab();
  559. $selectId = $this->getName();
  560. $selectName = $this->getName() . '[]';
  561. $selectNameFrom = $this->getName() . '-f[]';
  562. $selectNameTo = $this->getName() . '-t[]';
  563. $selected_count = 0;
  564. // placeholder {unselected} existence determines if we will render
  565. if (strpos($this->_elementTemplate, '{unselected}') === false) {
  566. // ... a single multi-select with checkboxes
  567. $this->_jsPostfix = 'editSelection';
  568. $id = $this->getAttribute('name');
  569. $strHtmlSelected = $tab . '<div id="qfams_'.$id.'">' . PHP_EOL;
  570. $unselected_count = count($this->_options);
  571. $checkbox_id_suffix = 0;
  572. foreach ($this->_options as $option) {
  573. $_labelAttributes
  574. = array('style', 'class', 'onmouseover', 'onmouseout');
  575. $labelAttributes = array();
  576. foreach ($_labelAttributes as $attr) {
  577. if (isset($option['attr'][$attr])) {
  578. $labelAttributes[$attr] = $option['attr'][$attr];
  579. unset($option['attr'][$attr]);
  580. }
  581. }
  582. if (is_array($this->_values)
  583. && in_array((string)$option['attr']['value'], $this->_values)) {
  584. // The items is *selected*
  585. $checked = ' checked="checked"';
  586. $selected_count++;
  587. } else {
  588. // The item is *unselected* so we want to put it
  589. $checked = '';
  590. }
  591. $checkbox_id_suffix++;
  592. $strHtmlSelected .= $tab
  593. . '<label'
  594. . $this->_getAttrString($labelAttributes) .'>'
  595. . '<input type="checkbox"'
  596. . ' id="'.$selectId . $checkbox_id_suffix.'"'
  597. . ' name="'.$selectName.'"'
  598. . $checked
  599. . $this->_getAttrString($option['attr'])
  600. . ' />' . $option['text'] . '</label>'
  601. . PHP_EOL;
  602. }
  603. $strHtmlSelected .= $tab . '</div>'. PHP_EOL;
  604. $strHtmlHidden = '';
  605. $strHtmlUnselected = '';
  606. $strHtmlAdd = '';
  607. $strHtmlRemove = '';
  608. // build the select all button with all its attributes
  609. $jsName = $this->_jsPrefix . $this->_jsPostfix;
  610. $attributes = array('onclick' => $jsName .
  611. "('". $selectId ."', 1);");
  612. $this->_allButtonAttributes
  613. = array_merge($this->_allButtonAttributes, $attributes);
  614. $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
  615. $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
  616. // build the select none button with all its attributes
  617. $attributes = array('onclick' => $jsName .
  618. "('". $selectId ."', 0);");
  619. $this->_noneButtonAttributes
  620. = array_merge($this->_noneButtonAttributes, $attributes);
  621. $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
  622. $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
  623. // build the toggle selection button with all its attributes
  624. $attributes = array('onclick' => $jsName .
  625. "('". $selectId ."', 2);");
  626. $this->_toggleButtonAttributes
  627. = array_merge($this->_toggleButtonAttributes,
  628. $attributes);
  629. $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
  630. $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
  631. $strHtmlMoveUp = '';
  632. $strHtmlMoveDown = '';
  633. $strHtmlMoveTop = '';
  634. $strHtmlMoveBottom = '';
  635. // default selection counters
  636. $strHtmlSelectedCount = $selected_count . '/' . $unselected_count;
  637. } else {
  638. // ... or a dual multi-select
  639. $this->_jsPostfix = 'moveSelection';
  640. $jsName = $this->_jsPrefix . $this->_jsPostfix;
  641. // set name of Select From Box
  642. $this->_attributesUnselected
  643. = array('id' => $selectId . '-f',
  644. 'name' => $selectNameFrom,
  645. 'ondblclick' => $jsName .
  646. "('{$selectId}', ".
  647. "this.form.elements['" . $selectNameFrom . "'], " .
  648. "this.form.elements['" . $selectNameTo . "'], " .
  649. "this.form.elements['" . $selectName . "'], " .
  650. "'add', '{$this->_sort}')");
  651. $this->_attributesUnselected
  652. = array_merge($this->_attributes, $this->_attributesUnselected);
  653. $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
  654. // set name of Select To Box
  655. $this->_attributesSelected
  656. = array('id' => $selectId . '-t',
  657. 'name' => $selectNameTo,
  658. 'ondblclick' => $jsName .
  659. "('{$selectId}', " .
  660. "this.form.elements['" . $selectNameFrom . "'], " .
  661. "this.form.elements['" . $selectNameTo . "'], ".
  662. "this.form.elements['" . $selectName . "'], " .
  663. "'remove', '{$this->_sort}')");
  664. $this->_attributesSelected
  665. = array_merge($this->_attributes, $this->_attributesSelected);
  666. $attrSelected = $this->_getAttrString($this->_attributesSelected);
  667. // set name of Select hidden Box
  668. $this->_attributesHidden
  669. = array('name' => $selectName,
  670. 'style' => 'overflow: hidden; visibility: hidden; ' .
  671. 'width: 1px; height: 0;');
  672. $this->_attributesHidden
  673. = array_merge($this->_attributes, $this->_attributesHidden);
  674. $attrHidden = $this->_getAttrString($this->_attributesHidden);
  675. // prepare option tables to be displayed as in POST order
  676. $append = count($this->_values);
  677. if ($append > 0) {
  678. $arrHtmlSelected = array_fill(0, $append, ' ');
  679. } else {
  680. $arrHtmlSelected = array();
  681. }
  682. $options = count($this->_options);
  683. $arrHtmlUnselected = array();
  684. if ($options > 0) {
  685. $arrHtmlHidden = array_fill(0, $options, ' ');
  686. foreach ($this->_options as $option) {
  687. if (is_array($this->_values)
  688. && in_array((string)$option['attr']['value'],
  689. $this->_values)) {
  690. // Get the post order
  691. $key = array_search($option['attr']['value'],
  692. $this->_values);
  693. /** The items is *selected* so we want to put it
  694. in the 'selected' multi-select */
  695. $arrHtmlSelected[$key] = $option;
  696. /** Add it to the 'hidden' multi-select
  697. and set it as 'selected' */
  698. if (isset($option['attr']['disabled'])) {
  699. unset($option['attr']['disabled']);
  700. }
  701. $option['attr']['selected'] = 'selected';
  702. $arrHtmlHidden[$key] = $option;
  703. } else {
  704. /** The item is *unselected* so we want to put it
  705. in the 'unselected' multi-select */
  706. $arrHtmlUnselected[] = $option;
  707. // Add it to the hidden multi-select as 'unselected'
  708. $arrHtmlHidden[$append] = $option;
  709. $append++;
  710. }
  711. }
  712. } else {
  713. $arrHtmlHidden = array();
  714. }
  715. // The 'unselected' multi-select which appears on the left
  716. $unselected_count = count($arrHtmlUnselected);
  717. if ($unselected_count == 0) {
  718. $this->_attributesUnselected['disabled'] = 'disabled';
  719. $this->_attributesUnselected
  720. = array_merge($this->_attributes, $this->_attributesUnselected);
  721. $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
  722. }
  723. $strHtmlUnselected = "<select$attrUnselected>". PHP_EOL;
  724. if ($unselected_count > 0) {
  725. foreach ($arrHtmlUnselected as $data) {
  726. $strHtmlUnselected
  727. .= $tabs . $tab
  728. . '<option' . $this->_getAttrString($data['attr']) . '>'
  729. . $data['text'] . '</option>' . PHP_EOL;
  730. }
  731. } else {
  732. $strHtmlUnselected .= '<option value="">&nbsp;</option>';
  733. }
  734. $strHtmlUnselected .= '</select>';
  735. // The 'selected' multi-select which appears on the right
  736. $selected_count = count($arrHtmlSelected);
  737. if ($selected_count == 0) {
  738. $this->_attributesSelected['disabled'] = 'disabled';
  739. $this->_attributesSelected
  740. = array_merge($this->_attributes, $this->_attributesSelected);
  741. $attrSelected = $this->_getAttrString($this->_attributesSelected);
  742. }
  743. $strHtmlSelected = "<select$attrSelected>". PHP_EOL;
  744. if ($selected_count > 0) {
  745. foreach ($arrHtmlSelected as $data) {
  746. $strHtmlSelected
  747. .= $tabs . $tab
  748. . '<option' . $this->_getAttrString($data['attr']) . '>'
  749. . $data['text'] . '</option>' . PHP_EOL;
  750. }
  751. } else {
  752. $strHtmlSelected .= '<option value="">&nbsp;</option>';
  753. }
  754. $strHtmlSelected .= '</select>';
  755. // The 'hidden' multi-select
  756. $strHtmlHidden = "<select$attrHidden>". PHP_EOL;
  757. if (count($arrHtmlHidden) > 0) {
  758. foreach ($arrHtmlHidden as $data) {
  759. $strHtmlHidden
  760. .= $tabs . $tab
  761. . '<option' . $this->_getAttrString($data['attr']) . '>'
  762. . $data['text'] . '</option>' . PHP_EOL;
  763. }
  764. }
  765. $strHtmlHidden .= '</select>';
  766. // build the remove button with all its attributes
  767. $attributes
  768. = array('onclick' => $jsName .
  769. "('{$selectId}', " .
  770. "this.form.elements['" . $selectNameFrom . "'], " .
  771. "this.form.elements['" . $selectNameTo . "'], " .
  772. "this.form.elements['" . $selectName . "'], " .
  773. "'remove', '{$this->_sort}'); return false;");
  774. $this->_removeButtonAttributes
  775. = array_merge($this->_removeButtonAttributes, $attributes);
  776. $attrStrRemove = $this->_getAttrString($this->_removeButtonAttributes);
  777. $strHtmlRemove = "<input$attrStrRemove />". PHP_EOL;
  778. // build the add button with all its attributes
  779. $attributes
  780. = array('onclick' => $jsName .
  781. "('{$selectId}', " .
  782. "this.form.elements['" . $selectNameFrom . "'], " .
  783. "this.form.elements['" . $selectNameTo . "'], " .
  784. "this.form.elements['" . $selectName . "'], " .
  785. "'add', '{$this->_sort}'); return false;");
  786. $this->_addButtonAttributes
  787. = array_merge($this->_addButtonAttributes, $attributes);
  788. $attrStrAdd = $this->_getAttrString($this->_addButtonAttributes);
  789. $strHtmlAdd = "<input$attrStrAdd />". PHP_EOL;
  790. // build the select all button with all its attributes
  791. $attributes
  792. = array('onclick' => $jsName .
  793. "('{$selectId}', " .
  794. "this.form.elements['" . $selectNameFrom . "'], " .
  795. "this.form.elements['" . $selectNameTo . "'], " .
  796. "this.form.elements['" . $selectName . "'], " .
  797. "'all', '{$this->_sort}'); return false;");
  798. $this->_allButtonAttributes
  799. = array_merge($this->_allButtonAttributes, $attributes);
  800. $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
  801. $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
  802. // build the select none button with all its attributes
  803. $attributes
  804. = array('onclick' => $jsName .
  805. "('{$selectId}', " .
  806. "this.form.elements['" . $selectNameFrom . "'], " .
  807. "this.form.elements['" . $selectNameTo . "'], " .
  808. "this.form.elements['" . $selectName . "'], " .
  809. "'none', '{$this->_sort}'); return false;");
  810. $this->_noneButtonAttributes
  811. = array_merge($this->_noneButtonAttributes, $attributes);
  812. $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
  813. $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
  814. // build the toggle button with all its attributes
  815. $attributes
  816. = array('onclick' => $jsName .
  817. "('{$selectId}', " .
  818. "this.form.elements['" . $selectNameFrom . "'], " .
  819. "this.form.elements['" . $selectNameTo . "'], " .
  820. "this.form.elements['" . $selectName . "'], " .
  821. "'toggle', '{$this->_sort}'); return false;");
  822. $this->_toggleButtonAttributes
  823. = array_merge($this->_toggleButtonAttributes, $attributes);
  824. $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
  825. $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
  826. // build the move up button with all its attributes
  827. $attributes
  828. = array('onclick' => "{$this->_jsPrefix}moveUp" .
  829. "(this.form.elements['" . $selectNameTo . "'], " .
  830. "this.form.elements['" . $selectName . "']); " .
  831. "return false;");
  832. $this->_upButtonAttributes
  833. = array_merge($this->_upButtonAttributes, $attributes);
  834. $attrStrUp = $this->_getAttrString($this->_upButtonAttributes);
  835. $strHtmlMoveUp = "<input$attrStrUp />". PHP_EOL;
  836. // build the move down button with all its attributes
  837. $attributes
  838. = array('onclick' => "{$this->_jsPrefix}moveDown" .
  839. "(this.form.elements['" . $selectNameTo . "'], " .
  840. "this.form.elements['" . $selectName . "']); " .
  841. "return false;");
  842. $this->_downButtonAttributes
  843. = array_merge($this->_downButtonAttributes, $attributes);
  844. $attrStrDown = $this->_getAttrString($this->_downButtonAttributes);
  845. $strHtmlMoveDown = "<input$attrStrDown />". PHP_EOL;
  846. // build the move top button with all its attributes
  847. $attributes
  848. = array('onclick' => "{$this->_jsPrefix}moveTop" .
  849. "(this.form.elements['" . $selectNameTo . "'], " .
  850. "this.form.elements['" . $selectName . "']); " .
  851. "return false;");
  852. $this->_topButtonAttributes
  853. = array_merge($this->_topButtonAttributes, $attributes);
  854. $attrStrTop = $this->_getAttrString($this->_topButtonAttributes);
  855. $strHtmlMoveTop = "<input$attrStrTop />". PHP_EOL;
  856. // build the move bottom button with all its attributes
  857. $attributes
  858. = array('onclick' => "{$this->_jsPrefix}moveBottom" .
  859. "(this.form.elements['" . $selectNameTo . "'], " .
  860. "this.form.elements['" . $selectName . "']); " .
  861. "return false;");
  862. $this->_bottomButtonAttributes
  863. = array_merge($this->_bottomButtonAttributes, $attributes);
  864. $attrStrBottom = $this->_getAttrString($this->_bottomButtonAttributes);
  865. $strHtmlMoveBottom = "<input$attrStrBottom />". PHP_EOL;
  866. // default selection counters
  867. $strHtmlSelectedCount = $selected_count;
  868. }
  869. $strHtmlUnselectedCount = $unselected_count;
  870. $strHtmlSelectedCountId = $selectId .'_selected';
  871. $strHtmlUnselectedCountId = $selectId .'_unselected';
  872. // render all part of the multi select component with the template
  873. $strHtml = $this->_elementTemplate;
  874. // Prepare multiple labels
  875. $labels = $this->getLabel();
  876. if (is_array($labels)) {
  877. array_shift($labels);
  878. }
  879. // render extra labels, if any
  880. if (is_array($labels)) {
  881. foreach ($labels as $key => $text) {
  882. $key = is_int($key)? $key + 2: $key;
  883. $strHtml = str_replace("{label_{$key}}", $text, $strHtml);
  884. $strHtml = str_replace("<!-- BEGIN label_{$key} -->", '', $strHtml);
  885. $strHtml = str_replace("<!-- END label_{$key} -->", '', $strHtml);
  886. }
  887. }
  888. // clean up useless label tags
  889. if (strpos($strHtml, '{label_')) {
  890. $strHtml = preg_replace('/\s*<!-- BEGIN label_(\S+) -->'.
  891. '.*<!-- END label_\1 -->\s*/i', '', $strHtml);
  892. }
  893. $placeHolders = array(
  894. '{stylesheet}', '{javascript}',
  895. '{class}',
  896. '{unselected_count_id}', '{selected_count_id}',
  897. '{unselected_count}', '{selected_count}',
  898. '{unselected}', '{selected}',
  899. '{add}', '{remove}',
  900. '{all}', '{none}', '{toggle}',
  901. '{moveup}', '{movedown}',
  902. '{movetop}', '{movebottom}'
  903. );
  904. $htmlElements = array(
  905. $this->getElementCss(false), $this->getElementJs(false),
  906. $this->_tableAttributes,
  907. $strHtmlUnselectedCountId, $strHtmlSelectedCountId,
  908. $strHtmlUnselectedCount, $strHtmlSelectedCount,
  909. $strHtmlUnselected, $strHtmlSelected . $strHtmlHidden,
  910. $strHtmlAdd, $strHtmlRemove,
  911. $strHtmlAll, $strHtmlNone, $strHtmlToggle,
  912. $strHtmlMoveUp, $strHtmlMoveDown,
  913. $strHtmlMoveTop, $strHtmlMoveBottom
  914. );
  915. $strHtml = str_replace($placeHolders, $htmlElements, $strHtml);
  916. $comment = $this->getComment();
  917. if (!empty($comment)) {
  918. $strHtml = $tabs . '<!-- ' . $comment . " //-->" . PHP_EOL . $strHtml;
  919. }
  920. return $strHtml;
  921. }
  922. /**
  923. * Returns the javascript code generated to handle this element
  924. *
  925. * @param boolean $raw (optional) html output with script tags or just raw data
  926. * @param boolean $min (optional) uses javascript compressed version
  927. *
  928. * @access public
  929. * @return string
  930. * @since version 0.4.0 (2005-06-25)
  931. */
  932. function getElementJs($raw = true, $min = false)
  933. {
  934. $js = '@data_dir@' . DIRECTORY_SEPARATOR
  935. . '@package_name@' . DIRECTORY_SEPARATOR;
  936. if ($min) {
  937. $js .= 'qfamsHandler-min.js';
  938. } else {
  939. $js .= 'qfamsHandler.js';
  940. }
  941. if (file_exists($js)) {
  942. $js = file_get_contents($js);
  943. } else {
  944. $js = '';
  945. }
  946. if ($raw !== true) {
  947. $js = '<script type="text/javascript">'
  948. . PHP_EOL . '//<![CDATA['
  949. . PHP_EOL . $js
  950. . PHP_EOL . '//]]>'
  951. . PHP_EOL . '</script>'
  952. . PHP_EOL;
  953. }
  954. return $js;
  955. }
  956. /**
  957. * Loads options from different types of data sources
  958. *
  959. * This method overloaded parent method of select element, to allow
  960. * loading options with fancy attributes.
  961. *
  962. * @param mixed &$options Options source currently supports assoc array or DB_result
  963. * @param mixed $param1 (optional) See function detail
  964. * @param mixed $param2 (optional) See function detail
  965. * @param mixed $param3 (optional) See function detail
  966. * @param mixed $param4 (optional) See function detail
  967. *
  968. * @access public
  969. * @since version 1.5.0 (2009-02-15)
  970. * @return PEAR_Error|NULL on error and TRUE on success
  971. * @throws PEAR_Error
  972. * @see loadArray()
  973. */
  974. function load(&$options,
  975. $param1 = null, $param2 = null, $param3 = null, $param4 = null)
  976. {
  977. if (is_array($options)) {
  978. $ret = $this->loadArray($options, $param1);
  979. } else {
  980. $ret = parent::load($options, $param1, $param2, $param3, $param4);
  981. }
  982. return $ret;
  983. }
  984. /**
  985. * Loads the options from an associative array
  986. *
  987. * This method overloaded parent method of select element, to allow to load
  988. * array of options with fancy attributes.
  989. *
  990. * @param array $arr Associative array of options
  991. * @param mixed $values (optional) Array or comma delimited string of selected values
  992. *
  993. * @since version 1.5.0 (2009-02-15)
  994. * @access public
  995. * @return PEAR_Error on error and TRUE on success
  996. * @throws PEAR_Error
  997. * @see load()
  998. */
  999. function loadArray($arr, $values = null)
  1000. {
  1001. if (!is_array($arr)) {
  1002. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  1003. 'loadArray is not a valid array',
  1004. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1005. array('level' => 'exception'));
  1006. }
  1007. if (isset($values)) {
  1008. $this->setSelected($values);
  1009. }
  1010. if (is_array($arr)) {
  1011. foreach ($arr as $key => $val) {
  1012. if (is_array($val)) {
  1013. $this->addOption($val[0], $key, $val[1]);
  1014. } else {
  1015. $this->addOption($val, $key);
  1016. }
  1017. }
  1018. }
  1019. return true;
  1020. }
  1021. /**
  1022. * Sets which items should be persistant
  1023. *
  1024. * Sets which items should have the disabled attribute
  1025. * to keep it persistant
  1026. *
  1027. * @param mixed $optionValues Options (key-values) that should be persistant
  1028. * @param bool $persistant (optional) TRUE if persistant, FALSE otherwise
  1029. *
  1030. * @since version 1.5.0 (2009-02-15)
  1031. * @access public
  1032. * @return PEAR_Error on error and TRUE on success
  1033. * @throws PEAR_Error
  1034. */
  1035. function setPersistantOptions($optionValues, $persistant = true)
  1036. {
  1037. if (!is_bool($persistant)) {
  1038. return PEAR::throwError('Argument 2 of HTML_QuickForm_advmultiselect::' .
  1039. 'setPersistantOptions is not a boolean',
  1040. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1041. array('level' => 'exception'));
  1042. }
  1043. if (is_string($optionValues)) {
  1044. $optionValues = array($optionValues);
  1045. }
  1046. if (!is_array($optionValues)) {
  1047. return PEAR::throwError('Argument 1 of HTML_QuickForm_advmultiselect::' .
  1048. 'setPersistantOptions is not a valid array',
  1049. HTML_QUICKFORM_ADVMULTISELECT_ERROR_INVALID_INPUT,
  1050. array('level' => 'exception'));
  1051. }
  1052. foreach ($this->_options as $k => $v) {
  1053. if (in_array($v['attr']['value'], $optionValues)) {
  1054. if ($persistant) {
  1055. $this->_options[$k]['attr']['disabled'] = 'disabled';
  1056. } else {
  1057. unset($this->_options[$k]['attr']['disabled']);
  1058. }
  1059. }
  1060. }
  1061. return true;
  1062. }
  1063. /**
  1064. * Returns list of persistant options
  1065. *
  1066. * Returns list of persistant options (key-values) that could not
  1067. * be selected or unselected.
  1068. *
  1069. * @since version 1.5.0 (2009-02-15)
  1070. * @access public
  1071. * @return array
  1072. */
  1073. function getPersistantOptions()
  1074. {
  1075. $options = array();
  1076. foreach ($this->_options as $k => $v) {
  1077. if (isset($v['attr']['disabled'])) {
  1078. $options[] = $this->_options[$k]['attr']['value'];
  1079. }
  1080. }
  1081. return $options;
  1082. }
  1083. }
  1084. if (class_exists('HTML_QuickForm')) {
  1085. HTML_QuickForm::registerElementType('advmultiselect',
  1086. 'HTML/QuickForm/advmultiselect.php', 'HTML_QuickForm_advmultiselect');
  1087. }
  1088. ?>