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