advmultiselect.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. <?php
  2. /**
  3. * Element for HTML_QuickForm that emulate a multi-select.
  4. *
  5. * The HTML_QuickForm_advmultiselect package adds an element to the
  6. * HTML_QuickForm package that is two select boxes next to each other
  7. * emulating a multi-select.
  8. *
  9. * PHP versions 4 and 5
  10. *
  11. * LICENSE: This source file is subject to version 3.0 of the PHP license
  12. * that is available through the world-wide-web at the following URI:
  13. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  14. * the PHP License and are unable to obtain it through the web, please
  15. * send a note to license@php.net so we can mail you a copy immediately.
  16. *
  17. * @category HTML
  18. * @package HTML_QuickForm_advmultiselect
  19. * @author Laurent Laville <pear@laurent-laville.org>
  20. * @copyright 1997-2005 The PHP Group
  21. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  22. * @version CVS: $Id: advmultiselect.php 20028 2009-04-23 19:32:35Z cfasanando $
  23. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  24. */
  25. require_once 'HTML/QuickForm/select.php';
  26. /**
  27. * Replace PHP_EOL constant
  28. *
  29. * category PHP
  30. * package PHP_Compat
  31. * @link http://php.net/reserved.constants.core
  32. * @author Aidan Lister <aidan@php.net>
  33. * @since PHP 5.0.2
  34. */
  35. if (!defined('PHP_EOL')) {
  36. switch (strtoupper(substr(PHP_OS, 0, 3))) {
  37. // Windows
  38. case 'WIN':
  39. define('PHP_EOL', "\r\n");
  40. break;
  41. // Mac
  42. case 'DAR':
  43. define('PHP_EOL', "\r");
  44. break;
  45. // Unix
  46. default:
  47. define('PHP_EOL', "\n");
  48. }
  49. }
  50. /**
  51. * Element for HTML_QuickForm that emulate a multi-select.
  52. *
  53. * The HTML_QuickForm_advmultiselect package adds an element to the
  54. * HTML_QuickForm package that is two select boxes next to each other
  55. * emulating a multi-select.
  56. *
  57. * PHP versions 4 and 5
  58. *
  59. * LICENSE: This source file is subject to version 3.0 of the PHP license
  60. * that is available through the world-wide-web at the following URI:
  61. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  62. * the PHP License and are unable to obtain it through the web, please
  63. * send a note to license@php.net so we can mail you a copy immediately.
  64. *
  65. * @category HTML
  66. * @package HTML_QuickForm_advmultiselect
  67. * @author Laurent Laville <pear@laurent-laville.org>
  68. * @copyright 1997-2005 The PHP Group
  69. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  70. * @version Release: 0.5.1
  71. * @link http://pear.php.net/package/HTML_QuickForm_advmultiselect
  72. */
  73. class HTML_QuickForm_advmultiselect extends HTML_QuickForm_select
  74. {
  75. /**
  76. * Prefix function name in javascript move selections
  77. *
  78. * @var string
  79. * @access private
  80. * @since 0.4.0
  81. */
  82. var $_jsPrefix;
  83. /**
  84. * Postfix function name in javascript move selections
  85. *
  86. * @var string
  87. * @access private
  88. * @since 0.4.0
  89. */
  90. var $_jsPostfix;
  91. /**
  92. * Associative array of the multi select container attributes
  93. *
  94. * @var array
  95. * @access private
  96. * @since 0.4.0
  97. */
  98. var $_tableAttributes;
  99. /**
  100. * Associative array of the add button attributes
  101. *
  102. * @var array
  103. * @access private
  104. * @since 0.4.0
  105. */
  106. var $_addButtonAttributes;
  107. /**
  108. * Associative array of the remove button attributes
  109. *
  110. * @var array
  111. * @access private
  112. * @since 0.4.0
  113. */
  114. var $_removeButtonAttributes;
  115. /**
  116. * Associative array of the move up button attributes
  117. *
  118. * @var array
  119. * @access private
  120. * @since 0.5.0
  121. */
  122. var $_upButtonAttributes;
  123. /**
  124. * Associative array of the move up button attributes
  125. *
  126. * @var array
  127. * @access private
  128. * @since 0.5.0
  129. */
  130. var $_downButtonAttributes;
  131. /**
  132. * Defines if both list (unselected, selected) will have their elements be
  133. * arranged from lowest to highest (or reverse) depending on comparaison function.
  134. *
  135. * SORT_ASC is used to sort in ascending order
  136. * SORT_DESC is used to sort in descending order
  137. *
  138. * @var integer
  139. * @access private
  140. * @since 0.5.0
  141. */
  142. var $_sort;
  143. /**
  144. * Associative array of the unselected item box attributes
  145. *
  146. * @var array
  147. * @access private
  148. * @since 0.4.0
  149. */
  150. var $_attributesUnselected;
  151. /**
  152. * Associative array of the selected item box attributes
  153. *
  154. * @var array
  155. * @access private
  156. * @since 0.4.0
  157. */
  158. var $_attributesSelected;
  159. /**
  160. * Associative array of the internal hidden box attributes
  161. *
  162. * @var array
  163. * @access private
  164. * @since 0.4.0
  165. */
  166. var $_attributesHidden;
  167. /**
  168. * Default Element template string
  169. *
  170. * @var string
  171. * @access private
  172. * @since 0.4.0
  173. */
  174. var $_elementTemplate = '
  175. {javascript}
  176. <table{class}>
  177. <!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
  178. <!-- BEGIN label_3 --><th>&nbsp;</th><th>{label_3}</th></tr><!-- END label_3 -->
  179. <tr>
  180. <td valign="top">{unselected}</td>
  181. <td align="center">{add}{remove}</td>
  182. <td valign="top">{selected}</td>
  183. </tr>
  184. </table>
  185. ';
  186. /**
  187. * Default Element stylesheet string
  188. *
  189. * @var string
  190. * @access private
  191. * @since 0.4.0
  192. */
  193. var $_elementCSS = '
  194. #{id}amsSelected {
  195. font: 13.3px sans-serif;
  196. background-color: #fff;
  197. overflow: auto;
  198. height: 14.3em;
  199. width: 12em;
  200. border-left: 1px solid #404040;
  201. border-top: 1px solid #404040;
  202. border-bottom: 1px solid #d4d0c8;
  203. border-right: 1px solid #d4d0c8;
  204. }
  205. #{id}amsSelected label {
  206. padding-right: 3px;
  207. display: block;
  208. }
  209. ';
  210. /**
  211. * Class constructor
  212. *
  213. * @param string $elementName Dual Select name attribute
  214. * @param mixed $elementLabel Label(s) for the select boxes
  215. * @param mixed $options Data to be used to populate options
  216. * @param mixed $attributes Either a typical HTML attribute string or an associative array
  217. * @param integer $sortOptions Either SORT_ASC for auto ascending arrange,
  218. * SORT_DESC for auto descending arrange, or
  219. * NULL for no sort (append at end: default)
  220. *
  221. * @access public
  222. * @return void
  223. * @since 0.4.0
  224. */
  225. function HTML_QuickForm_advmultiselect($elementName = null, $elementLabel = null,
  226. $options = null, $attributes = null,
  227. $sortOptions = null)
  228. {
  229. $this->HTML_QuickForm_select($elementName, $elementLabel, $options, $attributes);
  230. // add multiple selection attribute by default if missing
  231. $this->updateAttributes(array('multiple' => 'multiple'));
  232. if (is_null($this->getAttribute('size'))) {
  233. // default size is ten item on each select box (left and right)
  234. $this->updateAttributes(array('size' => 10));
  235. }
  236. if (is_null($this->getAttribute('style'))) {
  237. // default width of each select box
  238. $this->updateAttributes(array('style' => 'width:100px;'));
  239. }
  240. $this->_tableAttributes = $this->getAttribute('class');
  241. if (is_null($this->_tableAttributes)) {
  242. // default table layout
  243. $attr = array('border' => '0', 'cellpadding' => '10', 'cellspacing' => '0');
  244. } else {
  245. $attr = array('class' => $this->_tableAttributes);
  246. $this->_removeAttr('class', $this->_attributes);
  247. }
  248. $this->_tableAttributes = $this->_getAttrString($attr);
  249. // set default add button attributes
  250. $this->setButtonAttributes('add');
  251. // set default remove button attributes
  252. $this->setButtonAttributes('remove');
  253. // set default move up button attributes
  254. $this->setButtonAttributes('moveup');
  255. // set default move up button attributes
  256. $this->setButtonAttributes('movedown');
  257. // defines javascript functions names
  258. $this->setJsElement();
  259. // set select boxes sort order (none by default)
  260. if (isset($sortOptions)) {
  261. $this->_sort = $sortOptions;
  262. } else {
  263. $this->_sort = false;
  264. }
  265. }
  266. /**
  267. * Sets the button attributes
  268. *
  269. * In <b>custom example 1</b>, the <i>add</i> and <i>remove</i> buttons have look set
  270. * by the css class <i>inputCommand</i>. See especially lines 43-48 and 98-103.
  271. *
  272. * In <b>custom example 2</b>, the basic text <i>add</i> and <i>remove</i> buttons
  273. * are now replaced by images. See lines 43-44.
  274. *
  275. * In <b>custom example 5</b>, we have ability to sort the selection list (on right side)
  276. * by :
  277. * <pre>
  278. * - <b>user-end</b>: with <i>Up</i> and <i>Down</i> buttons
  279. * (see lines 65,65,76 and 128-130)
  280. * - <b>programming</b>: with the QF element constructor $sort option
  281. * (see lines 34,36,38 and 59)
  282. * </pre>
  283. *
  284. * @example examples/qfams_custom_5.php Custom example 5: source code
  285. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom5.png Custom example 5: screenshot
  286. *
  287. * @example examples/qfams_custom_2.php Custom example 2: source code
  288. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom2.png Custom example 2: screenshot
  289. *
  290. * @example examples/qfams_custom_1.php Custom example 1: source code
  291. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom1.png Custom example 1: screenshot
  292. *
  293. * @param string $button Button identifier, either 'add', 'remove', 'moveup' or 'movedown'
  294. * @param mixed $attributes (optional) Either a typical HTML attribute string
  295. * or an associative array
  296. * @access public
  297. * @since 0.4.0
  298. */
  299. function setButtonAttributes($button, $attributes = null)
  300. {
  301. if (!is_string($button)) {
  302. return PEAR::raiseError('Argument 1 of advmultiselect::setButtonAttributes'
  303. .' is not a string');
  304. }
  305. switch ($button) {
  306. case 'add':
  307. if (is_null($attributes)) {
  308. $this->_addButtonAttributes = array('name' => 'add',
  309. 'value' => ' >> ',
  310. 'type' => 'button'
  311. );
  312. } else {
  313. $this->_updateAttrArray($this->_addButtonAttributes,
  314. $this->_parseAttributes($attributes)
  315. );
  316. }
  317. break;
  318. case 'remove':
  319. if (is_null($attributes)) {
  320. $this->_removeButtonAttributes = array('name' => 'remove',
  321. 'value' => ' << ',
  322. 'type' => 'button'
  323. );
  324. } else {
  325. $this->_updateAttrArray($this->_removeButtonAttributes,
  326. $this->_parseAttributes($attributes)
  327. );
  328. }
  329. break;
  330. case 'moveup':
  331. if (is_null($attributes)) {
  332. $this->_upButtonAttributes = array('name' => 'up',
  333. 'value' => ' Up ',
  334. 'type' => 'button'
  335. );
  336. } else {
  337. $this->_updateAttrArray($this->_upButtonAttributes,
  338. $this->_parseAttributes($attributes)
  339. );
  340. }
  341. break;
  342. case 'movedown':
  343. if (is_null($attributes)) {
  344. $this->_downButtonAttributes = array('name' => 'down',
  345. 'value' => ' Down ',
  346. 'type' => 'button'
  347. );
  348. } else {
  349. $this->_updateAttrArray($this->_downButtonAttributes,
  350. $this->_parseAttributes($attributes)
  351. );
  352. }
  353. break;
  354. default;
  355. return PEAR::raiseError('Argument 1 of advmultiselect::setButtonAttributes'
  356. .' has unexpected value');
  357. }
  358. }
  359. /**
  360. * Sets element template
  361. *
  362. * @param string $html The HTML surrounding select boxes and buttons
  363. *
  364. * @access public
  365. * @return void
  366. * @since 0.4.0
  367. */
  368. function setElementTemplate($html)
  369. {
  370. $this->_elementTemplate = $html;
  371. }
  372. /**
  373. * Sets JavaScript function name parts. Maybe usefull to avoid conflict names
  374. *
  375. * In <b>multiple example 1</b>, the javascript function prefix is set to not null
  376. * (see line 60).
  377. *
  378. * @example examples/qfams_multiple_1.php Multiple example 1: source code
  379. * @link http://www.laurent-laville.org/img/qfams/screenshot/multiple1.png Multiple example 1: screenshot
  380. *
  381. * @param string $pref (optional) Prefix name
  382. * @param string $post (optional) Postfix name
  383. *
  384. * @access public
  385. * @return void
  386. * @see getElementJs()
  387. * @since 0.4.0
  388. */
  389. function setJsElement($pref = null, $post = 'moveSelections')
  390. {
  391. $this->_jsPrefix = $pref;
  392. $this->_jsPostfix = $post;
  393. }
  394. /**
  395. * Gets default element stylesheet for a single multi-select shape render
  396. *
  397. * In <b>custom example 4</b>, the template defined lines 80-87 allows
  398. * a single multi-select checkboxes shape. Useful when javascript is disabled
  399. * (or when browser is not js compliant). In our example, no need to add javascript code
  400. * (see lines 170-172), but css is mandatory (see line 142).
  401. *
  402. * @example qfams_custom_4.php Custom example 4: source code
  403. * @link http://www.laurent-laville.org/img/qfams/screenshot/custom4.png Custom example 4: screenshot
  404. *
  405. * @param boolean $raw (optional) html output with style tags or just raw data
  406. *
  407. * @access public
  408. * @return string
  409. * @since 0.4.0
  410. */
  411. function getElementCss($raw = true)
  412. {
  413. $id = $this->getAttribute('id');
  414. $css = str_replace('{id}', $id, $this->_elementCSS);
  415. if ($raw !== true) {
  416. $css = '<style type="text/css">' . PHP_EOL
  417. . '/*<![CDATA[*/' . PHP_EOL
  418. . $css . PHP_EOL
  419. . '/*]]>*/' . PHP_EOL
  420. . '</style>';
  421. }
  422. return $css;
  423. }
  424. /**
  425. * Returns the HTML generated for the advanced mutliple select component
  426. *
  427. * @access public
  428. * @return string
  429. * @since 0.4.0
  430. */
  431. function toHtml()
  432. {
  433. if ($this->_flagFrozen) {
  434. return $this->getFrozenHtml();
  435. }
  436. $tabs = $this->_getTabs();
  437. $tab = $this->_getTab();
  438. $strHtml = '';
  439. if ($this->getComment() != '') {
  440. $strHtml .= $tabs . '<!-- ' . $this->getComment() . " //-->" . PHP_EOL;
  441. }
  442. $selectName = $this->getName() . '[]';
  443. // placeholder {unselected} existence determines if we will render
  444. if (strpos($this->_elementTemplate, '{unselected}') === false) {
  445. // ... a single multi-select with checkboxes
  446. $id = $this->getAttribute('id');
  447. $strHtmlSelected = $tab . '<div id="'.$id.'amsSelected">' . PHP_EOL;
  448. foreach ($this->_options as $option) {
  449. $_labelAttributes = array('style', 'class', 'onmouseover', 'onmouseout');
  450. $labelAttributes = array();
  451. foreach ($_labelAttributes as $attr) {
  452. if (isset($option['attr'][$attr])) {
  453. $labelAttributes[$attr] = $option['attr'][$attr];
  454. unset($option['attr'][$attr]);
  455. }
  456. }
  457. if (is_array($this->_values) && in_array((string)$option['attr']['value'], $this->_values)) {
  458. // The items is *selected*
  459. $checked = ' checked="checked"';
  460. } else {
  461. // The item is *unselected* so we want to put it
  462. $checked = '';
  463. }
  464. $strHtmlSelected .= $tab
  465. . '<label'
  466. . $this->_getAttrString($labelAttributes) .'>'
  467. . '<input type="checkbox"'
  468. . ' name="'.$selectName.'"'
  469. . $checked
  470. . $this->_getAttrString($option['attr'])
  471. . ' />' . $option['text'] . '</label>'
  472. . PHP_EOL;
  473. }
  474. $strHtmlSelected .= $tab . '</div>'. PHP_EOL;
  475. $strHtmlHidden = '';
  476. $strHtmlUnselected = '';
  477. $strHtmlAdd = '';
  478. $strHtmlRemove = '';
  479. $strHtmlMoveUp = '';
  480. $strHtmlMoveDown = '';
  481. } else {
  482. // ... or a dual multi-select
  483. // set name of Select From Box
  484. $this->_attributesUnselected = array('name' => '__'.$selectName, 'ondblclick' => "{$this->_jsPrefix}{$this->_jsPostfix}(this.form.elements['__" . $selectName . "'], this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "'], 'add')");
  485. $this->_attributesUnselected = array_merge($this->_attributes, $this->_attributesUnselected);
  486. $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
  487. // set name of Select To Box
  488. $this->_attributesSelected = array('name' => '_'.$selectName, 'ondblclick' => "{$this->_jsPrefix}{$this->_jsPostfix}(this.form.elements['__" . $selectName . "'], this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "'], 'remove')");
  489. $this->_attributesSelected = array_merge($this->_attributes, $this->_attributesSelected);
  490. $attrSelected = $this->_getAttrString($this->_attributesSelected);
  491. // set name of Select hidden Box
  492. $this->_attributesHidden = array('name' => $selectName, 'style' => 'overflow: hidden; visibility: hidden; width: 1px; height: 0;');
  493. $this->_attributesHidden = array_merge($this->_attributes, $this->_attributesHidden);
  494. $attrHidden = $this->_getAttrString($this->_attributesHidden);
  495. // prepare option tables to be displayed as in POST order
  496. $append = count($this->_values);
  497. if ($append > 0) {
  498. $arrHtmlSelected = array_fill(0, $append, ' ');
  499. }
  500. $arrHtmlHidden = array_fill(0, count($this->_options), ' ');
  501. foreach ($this->_options as $option) {
  502. if (is_array($this->_values) &&
  503. in_array((string)$option['attr']['value'], $this->_values)) {
  504. // Get the post order
  505. $key = array_search($option['attr']['value'], $this->_values);
  506. // The items is *selected* so we want to put it in the 'selected' multi-select
  507. $arrHtmlSelected[$key] = $option;
  508. // Add it to the 'hidden' multi-select and set it as 'selected'
  509. $option['attr']['selected'] = 'selected';
  510. $arrHtmlHidden[$key] = $option;
  511. } else {
  512. // The item is *unselected* so we want to put it in the 'unselected' multi-select
  513. $arrHtmlUnselected[] = $option;
  514. // Add it to the hidden multi-select as 'unselected'
  515. $arrHtmlHidden[$append] = $option;
  516. $append++;
  517. }
  518. }
  519. // The 'unselected' multi-select which appears on the left
  520. $strHtmlUnselected = "<select$attrUnselected>". PHP_EOL;
  521. if (is_array($arrHtmlUnselected) && count($arrHtmlUnselected) > 0) {
  522. foreach ($arrHtmlUnselected as $data) {
  523. $strHtmlUnselected .= $tabs . $tab
  524. . '<option' . $this->_getAttrString($data['attr']) . '>'
  525. . $data['text'] . '</option>' . PHP_EOL;
  526. }
  527. }
  528. $strHtmlUnselected .= '</select>';
  529. // The 'selected' multi-select which appears on the right
  530. $strHtmlSelected = "<select$attrSelected>". PHP_EOL;
  531. if (isset($arrHtmlSelected)) {
  532. foreach ($arrHtmlSelected as $data) {
  533. $strHtmlSelected .= $tabs . $tab
  534. . '<option' . $this->_getAttrString($data['attr']) . '>'
  535. . $data['text'] . '</option>' . PHP_EOL;
  536. }
  537. }
  538. $strHtmlSelected .= '</select>';
  539. // The 'hidden' multi-select
  540. $strHtmlHidden = "<select$attrHidden>". PHP_EOL;
  541. foreach ($arrHtmlHidden as $data) {
  542. $strHtmlHidden .= $tabs . $tab
  543. . '<option' . $this->_getAttrString($data['attr']) . '>'
  544. . $data['text'] . '</option>' . PHP_EOL;
  545. }
  546. $strHtmlHidden .= '</select>';
  547. // build the remove button with all its attributes
  548. $attributes = array('onclick' => "{$this->_jsPrefix}{$this->_jsPostfix}(this.form.elements['__" . $selectName . "'], this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "'], 'remove'); return false;");
  549. $this->_removeButtonAttributes = array_merge($this->_removeButtonAttributes, $attributes);
  550. $attrStrRemove = $this->_getAttrString($this->_removeButtonAttributes);
  551. $strHtmlRemove = "<input$attrStrRemove />". PHP_EOL;
  552. // build the add button with all its attributes
  553. $attributes = array('onclick' => "{$this->_jsPrefix}{$this->_jsPostfix}(this.form.elements['__" . $selectName . "'], this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "'], 'add'); return false;");
  554. $this->_addButtonAttributes = array_merge($this->_addButtonAttributes, $attributes);
  555. $attrStrAdd = $this->_getAttrString($this->_addButtonAttributes);
  556. $strHtmlAdd = "<input$attrStrAdd />". PHP_EOL;
  557. // build the move up button with all its attributes
  558. $attributes = array('onclick' => "{$this->_jsPrefix}moveUp(this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "']); return false;");
  559. $this->_upButtonAttributes = array_merge($this->_upButtonAttributes, $attributes);
  560. $attrStrUp = $this->_getAttrString($this->_upButtonAttributes);
  561. $strHtmlMoveUp = "<input$attrStrUp />". PHP_EOL;
  562. // build the move down button with all its attributes
  563. $attributes = array('onclick' => "{$this->_jsPrefix}moveDown(this.form.elements['_" . $selectName . "'], this.form.elements['" . $selectName . "']); return false;");
  564. $this->_downButtonAttributes = array_merge($this->_downButtonAttributes, $attributes);
  565. $attrStrDown = $this->_getAttrString($this->_downButtonAttributes);
  566. $strHtmlMoveDown = "<input$attrStrDown />". PHP_EOL;
  567. }
  568. // render all part of the multi select component with the template
  569. $strHtml = $this->_elementTemplate;
  570. // Prepare multiple labels
  571. $labels = $this->getLabel();
  572. if (is_array($labels)) {
  573. array_shift($labels);
  574. }
  575. // render extra labels, if any
  576. if (is_array($labels)) {
  577. foreach($labels as $key => $text) {
  578. $key = is_int($key)? $key + 2: $key;
  579. $strHtml = str_replace("{label_{$key}}", $text, $strHtml);
  580. $strHtml = str_replace("<!-- BEGIN label_{$key} -->", '', $strHtml);
  581. $strHtml = str_replace("<!-- END label_{$key} -->", '', $strHtml);
  582. }
  583. }
  584. // clean up useless label tags
  585. if (strpos($strHtml, '{label_')) {
  586. $strHtml = preg_replace('/\s*<!-- BEGIN label_(\S+) -->.*<!-- END label_\1 -->\s*/i', '', $strHtml);
  587. }
  588. $placeHolders = array(
  589. '{stylesheet}', '{javascript}', '{class}',
  590. '{unselected}', '{selected}',
  591. '{add}', '{remove}',
  592. '{moveup}', '{movedown}'
  593. );
  594. $htmlElements = array(
  595. $this->getElementCss(false), $this->getElementJs(false), $this->_tableAttributes,
  596. $strHtmlUnselected, $strHtmlSelected . $strHtmlHidden,
  597. $strHtmlAdd, $strHtmlRemove,
  598. $strHtmlMoveUp, $strHtmlMoveDown
  599. );
  600. $strHtml = str_replace($placeHolders, $htmlElements, $strHtml);
  601. return $strHtml;
  602. }
  603. /**
  604. * Returns the javascript code generated to handle this element
  605. *
  606. * @param boolean $raw (optional) html output with script tags or just raw data
  607. *
  608. * @access public
  609. * @return string
  610. * @see setJsElement()
  611. * @since 0.4.0
  612. */
  613. function getElementJs($raw = true)
  614. {
  615. $js = '';
  616. $jsfuncName = $this->_jsPrefix . $this->_jsPostfix;
  617. if (!defined('HTML_QUICKFORM_ADVMULTISELECT_'.$jsfuncName.'_EXISTS')) {
  618. // We only want to include the javascript code once per form
  619. define('HTML_QUICKFORM_ADVMULTISELECT_'.$jsfuncName.'_EXISTS', true);
  620. $js .= "
  621. /* begin javascript for HTML_QuickForm_advmultiselect */
  622. function {$jsfuncName}(selectLeft, selectRight, selectHidden, action) {
  623. if (action == 'add') {
  624. menuFrom = selectLeft;
  625. menuTo = selectRight;
  626. }
  627. else {
  628. menuFrom = selectRight;
  629. menuTo = selectLeft;
  630. }
  631. // Don't do anything if nothing selected. Otherwise we throw javascript errors.
  632. if (menuFrom.selectedIndex == -1) {
  633. return;
  634. }
  635. // Add items to the 'TO' list.
  636. for (i=0; i < menuFrom.length; i++) {
  637. if (menuFrom.options[i].selected == true ) {
  638. menuTo.options[menuTo.length]= new Option(menuFrom.options[i].text, menuFrom.options[i].value);
  639. }
  640. }
  641. // Remove items from the 'FROM' list.
  642. for (i=(menuFrom.length - 1); i>=0; i--){
  643. if (menuFrom.options[i].selected == true ) {
  644. menuFrom.options[i] = null;
  645. }
  646. }
  647. ";
  648. if ($this->_sort === false) {
  649. $js .= "
  650. // Set the appropriate items as 'selected in the hidden select.
  651. // These are the values that will actually be posted with the form.
  652. {$this->_jsPrefix}updateHidden(selectHidden, selectRight);
  653. }
  654. ";
  655. } else {
  656. $reverse = ($this->_sort === SORT_DESC) ? 'options.reverse();' : '';
  657. $js .= "
  658. // Sort list if required
  659. {$this->_jsPrefix}sortList(menuTo, {$this->_jsPrefix}compareText);
  660. // Set the appropriate items as 'selected in the hidden select.
  661. // These are the values that will actually be posted with the form.
  662. {$this->_jsPrefix}updateHidden(selectHidden, selectRight);
  663. }
  664. function {$this->_jsPrefix}sortList(list, compareFunction) {
  665. var options = new Array (list.options.length);
  666. for (var i = 0; i < options.length; i++) {
  667. options[i] = new Option (
  668. list.options[i].text,
  669. list.options[i].value,
  670. list.options[i].defaultSelected,
  671. list.options[i].selected
  672. );
  673. }
  674. options.sort(compareFunction);
  675. {$reverse}
  676. list.options.length = 0;
  677. for (var i = 0; i < options.length; i++) {
  678. list.options[i] = options[i];
  679. }
  680. }
  681. function {$this->_jsPrefix}compareText(option1, option2) {
  682. if (option1.text == option2.text) {
  683. return 0;
  684. }
  685. return option1.text < option2.text ? -1 : 1;
  686. }
  687. ";
  688. }
  689. $js .= "
  690. function {$this->_jsPrefix}updateHidden(h,r) {
  691. for (i=0; i < h.length; i++) {
  692. h.options[i].selected = false;
  693. }
  694. for (i=0; i < r.length; i++) {
  695. h.options[h.length] = new Option(r.options[i].text, r.options[i].value);
  696. h.options[h.length-1].selected = true;
  697. }
  698. }
  699. function {$this->_jsPrefix}moveUp(l,h) {
  700. var indice = l.selectedIndex;
  701. if (indice < 0) {
  702. return;
  703. }
  704. if (indice > 0) {
  705. {$this->_jsPrefix}moveSwap(l, indice, indice-1);
  706. {$this->_jsPrefix}updateHidden(h, l);
  707. }
  708. }
  709. function {$this->_jsPrefix}moveDown(l,h) {
  710. var indice = l.selectedIndex;
  711. if (indice < 0) {
  712. return;
  713. }
  714. if (indice < l.options.length-1) {
  715. {$this->_jsPrefix}moveSwap(l, indice, indice+1);
  716. {$this->_jsPrefix}updateHidden(h, l);
  717. }
  718. }
  719. function {$this->_jsPrefix}moveSwap(l,i,j) {
  720. var valeur = l.options[i].value;
  721. var texte = l.options[i].text;
  722. l.options[i].value = l.options[j].value;
  723. l.options[i].text = l.options[j].text;
  724. l.options[j].value = valeur;
  725. l.options[j].text = texte;
  726. l.selectedIndex = j
  727. }
  728. /* end javascript for HTML_QuickForm_advmultiselect */
  729. ";
  730. if ($raw !== true) {
  731. $js = '<script type="text/javascript">' . PHP_EOL
  732. . '/* <![CDATA[ */' . $js . '/* ]]> */' . PHP_EOL
  733. . '</script>';
  734. }
  735. }
  736. return $js;
  737. }
  738. }
  739. if (class_exists('HTML_QuickForm')) {
  740. HTML_QuickForm::registerElementType('advmultiselect', 'HTML/QuickForm/advmultiselect.php', 'HTML_QuickForm_advmultiselect');
  741. }
  742. ?>