PageRenderTime 38ms CodeModel.GetById 2ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 1ms

/common/libraries/plugin/pear/HTML/QuickForm/advmultiselect.php

https://bitbucket.org/chamilo/chamilo/
PHP | 954 lines | 528 code | 74 blank | 352 comment | 56 complexity | 66024067ef64573797116b3b072cffd4 MD5 | raw file
  1<?php
  2/**
  3 * Copyright (c) 2005-2008, 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-2008 Laurent Laville
 38 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD
 39 * @version   CVS: $Id: advmultiselect.php 137 2009-11-09 13:24:37Z vanpouckesven $
 40 * @link      http://pear.php.net/package/HTML_QuickForm_advmultiselect
 41 * @since     File available since Release 0.4.0
 42 */
 43
 44require_once 'HTML/QuickForm/select.php';
 45
 46/**
 47 * Element for HTML_QuickForm that emulate a multi-select.
 48 *
 49 * The HTML_QuickForm_advmultiselect package adds an element to the
 50 * HTML_QuickForm package that is two select boxes next to each other
 51 * emulating a multi-select.
 52 *
 53 * @category  HTML
 54 * @package   HTML_QuickForm_advmultiselect
 55 * @author    Laurent Laville <pear@laurent-laville.org>
 56 * @copyright 2005-2008 Laurent Laville
 57 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD
 58 * @version   Release: @package_version@
 59 * @link      http://pear.php.net/package/HTML_QuickForm_advmultiselect
 60 * @since     Class available since Release 0.4.0
 61 */
 62class HTML_QuickForm_advmultiselect extends HTML_QuickForm_select
 63{
 64    /**
 65     * Prefix function name in javascript move selections
 66     *
 67     * @var        string
 68     * @access     private
 69     * @since      0.4.0
 70     */
 71    var $_jsPrefix;
 72
 73    /**
 74     * Postfix function name in javascript move selections
 75     *
 76     * @var        string
 77     * @access     private
 78     * @since      0.4.0
 79     */
 80    var $_jsPostfix;
 81
 82    /**
 83     * Associative array of the multi select container attributes
 84     *
 85     * @var        array
 86     * @access     private
 87     * @since      0.4.0
 88     */
 89    var $_tableAttributes;
 90
 91    /**
 92     * Associative array of the add button attributes
 93     *
 94     * @var        array
 95     * @access     private
 96     * @since      0.4.0
 97     */
 98    var $_addButtonAttributes;
 99
100    /**
101     * Associative array of the remove button attributes
102     *
103     * @var        array
104     * @access     private
105     * @since      0.4.0
106     */
107    var $_removeButtonAttributes;
108
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    /**
119     * Associative array of the select none button attributes
120     *
121     * @var        array
122     * @access     private
123     * @since      1.1.0
124     */
125    var $_noneButtonAttributes;
126
127    /**
128     * Associative array of the toggle selection button attributes
129     *
130     * @var        array
131     * @access     private
132     * @since      1.1.0
133     */
134    var $_toggleButtonAttributes;
135
136    /**
137     * Associative array of the move up button attributes
138     *
139     * @var        array
140     * @access     private
141     * @since      0.5.0
142     */
143    var $_upButtonAttributes;
144
145    /**
146     * Associative array of the move up button attributes
147     *
148     * @var        array
149     * @access     private
150     * @since      0.5.0
151     */
152    var $_downButtonAttributes;
153
154    /**
155     * Defines if both list (unselected, selected) will have their elements be
156     * arranged from lowest to highest (or reverse)
157     * depending on comparaison function.
158     *
159     * SORT_ASC  is used to sort in ascending order
160     * SORT_DESC is used to sort in descending order
161     *
162     * @var    string    ('none' == false, 'asc' == SORT_ASC, 'desc' == SORT_DESC)
163     * @access private
164     * @since  0.5.0
165     */
166    var $_sort;
167
168    /**
169     * Associative array of the unselected item box attributes
170     *
171     * @var        array
172     * @access     private
173     * @since      0.4.0
174     */
175    var $_attributesUnselected;
176
177    /**
178     * Associative array of the selected item box attributes
179     *
180     * @var        array
181     * @access     private
182     * @since      0.4.0
183     */
184    var $_attributesSelected;
185
186    /**
187     * Associative array of the internal hidden box attributes
188     *
189     * @var        array
190     * @access     private
191     * @since      0.4.0
192     */
193    var $_attributesHidden;
194
195    /**
196     * Default Element template string
197     *
198     * @var        string
199     * @access     private
200     * @since      0.4.0
201     */
202    var $_elementTemplate = '
203{javascript}
204<table{class}>
205<!-- BEGIN label_2 --><tr><th>{label_2}</th><!-- END label_2 -->
206<!-- BEGIN label_3 --><th>&nbsp;</th><th>{label_3}</th></tr><!-- END label_3 -->
207<tr>
208  <td valign="top">{unselected}</td>
209  <td align="center">{add}{remove}</td>
210  <td valign="top">{selected}</td>
211</tr>
212</table>
213';
214
215    /**
216     * Default Element stylesheet string
217     *
218     * @var        string
219     * @access     private
220     * @since      0.4.0
221     */
222    var $_elementCSS = '
223#qfams_{id} {
224  font: 13.3px sans-serif;
225  background-color: #fff;
226  overflow: auto;
227  height: 14.3em;
228  width: 12em;
229  border-left:   1px solid #404040;
230  border-top:    1px solid #404040;
231  border-bottom: 1px solid #d4d0c8;
232  border-right:  1px solid #d4d0c8;
233}
234#qfams_{id} label {
235  padding-right: 3px;
236  display: block;
237}
238';
239
240    /**
241     * Class constructor
242     *
243     * Class constructors :
244     * Zend Engine 1 uses HTML_QuickForm_advmultiselect, while
245     * Zend Engine 2 uses __construct
246     *
247     * @param string  $elementName  Dual Select name attribute
248     * @param mixed   $elementLabel Label(s) for the select boxes
249     * @param mixed   $options      Data to be used to populate options
250     * @param mixed   $attributes   Either a typical HTML attribute string or
251     *                              an associative array
252     * @param integer $sort         Either SORT_ASC for auto ascending arrange,
253     *                                     SORT_DESC for auto descending arrange, or
254     *                                     NULL for no sort (append at end: default)
255     *
256     * @access     public
257     * @return     void
258     * @since      0.4.0
259     */
260    function __construct($elementName = null, $elementLabel = null,
261                                           $options = null, $attributes = null,
262                                           $sort = null)
263    {
264        parent :: __construct($elementName, $elementLabel,
265            $options, $attributes);
266
267        // add multiple selection attribute by default if missing
268        $this->updateAttributes(array('multiple' => 'multiple'));
269
270        if (is_null($this->getAttribute('size'))) {
271            // default size is ten item on each select box (left and right)
272            $this->updateAttributes(array('size' => 10));
273        }
274        if (is_null($this->getAttribute('style'))) {
275            // default width of each select box
276            $this->updateAttributes(array('style' => 'width:100px;'));
277        }
278        $this->_tableAttributes = $this->getAttribute('class');
279        if (is_null($this->_tableAttributes)) {
280            // default table layout
281            $attr = array('border' => '0',
282                          'cellpadding' => '10', 'cellspacing' => '0');
283        } else {
284            $attr = array('class' => $this->_tableAttributes);
285            $this->_removeAttr('class', $this->_attributes);
286        }
287        $this->_tableAttributes = $this->_getAttrString($attr);
288
289        // set default add button attributes
290        $this->setButtonAttributes('add');
291        // set default remove button attributes
292        $this->setButtonAttributes('remove');
293        // set default selectall button attributes
294        $this->setButtonAttributes('all');
295        // set default selectnone button attributes
296        $this->setButtonAttributes('none');
297        // set default toggle selection button attributes
298        $this->setButtonAttributes('toggle');
299        // set default move up button attributes
300        $this->setButtonAttributes('moveup');
301        // set default move up button attributes
302        $this->setButtonAttributes('movedown');
303        // defines javascript functions names
304        $this->setJsElement();
305
306        // set select boxes sort order (none by default)
307        if (!isset($sort)) {
308            $sort = false;
309        }
310        if ($sort === SORT_ASC) {
311            $this->_sort = 'asc';
312        } elseif ($sort === SORT_DESC) {
313            $this->_sort = 'desc';
314        } else {
315            $this->_sort = 'none';
316        }
317    }
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     *                                                     'moveup' or 'movedown'
338     * @param mixed  $attributes (optional) Either a typical HTML attribute string
339     *                                      or an associative array
340     *
341     * @return     void
342     * @throws     PEAR_Error   $button argument
343     *                          is not a string, or not in range
344     *                          (add, remove, all, none, toggle, moveup, movedown)
345     * @access     public
346     * @since      0.4.0
347     *
348     * @example    examples/qfams_custom_5.php
349     *             Custom example 5: source code
350     * @link       http://www.laurent-laville.org/img/qfams/screenshot/custom5.png
351     *             Custom example 5: screenshot
352     *
353     * @example    examples/qfams_custom_2.php
354     *             Custom example 2: source code
355     * @link       http://www.laurent-laville.org/img/qfams/screenshot/custom2.png
356     *             Custom example 2: screenshot
357     *
358     * @example    examples/qfams_custom_1.php
359     *             Custom example 1: source code
360     * @link       http://www.laurent-laville.org/img/qfams/screenshot/custom1.png
361     *             Custom example 1: screenshot
362     */
363    function setButtonAttributes($button, $attributes = null)
364    {
365        if (!is_string($button)) {
366            return PEAR::raiseError('Argument 1 of ' .
367                       'advmultiselect::setButtonAttributes is not a string');
368        }
369
370        switch ($button) {
371        case 'add':
372            if (is_null($attributes)) {
373                $this->_addButtonAttributes
374                    = array('name'  => 'add',
375                            'value' => ' >> ',
376                            'type'  => 'button');
377            } else {
378                $this->_updateAttrArray($this->_addButtonAttributes,
379                                        $this->_parseAttributes($attributes));
380            }
381            break;
382        case 'remove':
383            if (is_null($attributes)) {
384                $this->_removeButtonAttributes
385                    = array('name'  => 'remove',
386                            'value' => ' << ',
387                            'type'  => 'button');
388            } else {
389                $this->_updateAttrArray($this->_removeButtonAttributes,
390                                        $this->_parseAttributes($attributes));
391            }
392            break;
393        case 'all':
394            if (is_null($attributes)) {
395                $this->_allButtonAttributes
396                    = array('name'  => 'all',
397                            'value' => ' Select All ',
398                            'type'  => 'button');
399            } else {
400                $this->_updateAttrArray($this->_allButtonAttributes,
401                                        $this->_parseAttributes($attributes));
402            }
403            break;
404        case 'none':
405            if (is_null($attributes)) {
406                $this->_noneButtonAttributes
407                    = array('name'  => 'none',
408                            'value' => ' Select None ',
409                            'type'  => 'button');
410            } else {
411                $this->_updateAttrArray($this->_noneButtonAttributes,
412                                        $this->_parseAttributes($attributes));
413            }
414            break;
415        case 'toggle':
416            if (is_null($attributes)) {
417                $this->_toggleButtonAttributes
418                    = array('name'  => 'toggle',
419                            'value' => ' Toggle Selection ',
420                            'type'  => 'button');
421            } else {
422                $this->_updateAttrArray($this->_toggleButtonAttributes,
423                                        $this->_parseAttributes($attributes));
424            }
425            break;
426        case 'moveup':
427            if (is_null($attributes)) {
428                $this->_upButtonAttributes
429                    = array('name'  => 'up',
430                            'value' => ' Up ',
431                            'type'  => 'button');
432            } else {
433                $this->_updateAttrArray($this->_upButtonAttributes,
434                                        $this->_parseAttributes($attributes));
435            }
436            break;
437        case 'movedown':
438            if (is_null($attributes)) {
439                $this->_downButtonAttributes
440                    = array('name'  => 'down',
441                            'value' => ' Down ',
442                            'type'  => 'button');
443            } else {
444                $this->_updateAttrArray($this->_downButtonAttributes,
445                                        $this->_parseAttributes($attributes));
446            }
447            break;
448        default;
449            return PEAR::raiseError('Argument 1 of ' .
450                       'advmultiselect::setButtonAttributes has unexpected value');
451        }
452    }
453
454    /**
455     * Sets element template
456     *
457     * @param string $html The HTML surrounding select boxes and buttons
458     *
459     * @access     public
460     * @return     void
461     * @since      0.4.0
462     */
463    function setElementTemplate($html)
464    {
465        $this->_elementTemplate = $html;
466    }
467
468    /**
469     * Sets JavaScript function name parts. Maybe usefull to avoid conflict names
470     *
471     * In <b>multiple example 1</b>, the javascript function prefix
472     * is set to not null.
473     *
474     * @param string $pref (optional) Prefix name
475     * @param string $post (optional) Postfix name
476     *
477     * @access     public
478     * @return     void
479     * @see        getElementJs()
480     * @since      0.4.0
481     * @deprecated since version 1.3.0
482     *
483     * @example    examples/qfams_multiple_1.php
484     *             Multiple example 1: source code
485     * @link       http://www.laurent-laville.org/img/qfams/screenshot/multiple1.png
486     *             Multiple example 1: screenshot
487     */
488    function setJsElement($pref = null, $post = 'moveSelections')
489    {
490        $this->_jsPrefix  = 'qfams';
491        $this->_jsPostfix = 'MoveSelection';
492    }
493
494    /**
495     * Gets default element stylesheet for a single multi-select shape render
496     *
497     * In <b>custom example 4</b>, the template defined allows
498     * a single multi-select checkboxes shape. Useful when javascript is disabled
499     * (or when browser is not js compliant). In our example, no need to add
500     * javascript code, but css is mandatory.
501     *
502     * @param boolean $raw (optional) html output with style tags or just raw data
503     *
504     * @access     public
505     * @return     string
506     * @since      0.4.0
507     *
508     * @example    qfams_custom_4.php
509     *             Custom example 4: source code
510     * @link       http://www.laurent-laville.org/img/qfams/screenshot/custom4.png
511     *             Custom example 4: screenshot
512     */
513    function getElementCss($raw = true)
514    {
515        $id  = $this->getAttribute('name');
516        $css = str_replace('{id}', $id, $this->_elementCSS);
517
518        if ($raw !== true) {
519            $css = '<style type="text/css">' . PHP_EOL
520                 . '<!--' . $css . '// -->'  . PHP_EOL
521                 . '</style>';
522        }
523        return $css;
524    }
525
526    /**
527     * Returns the HTML generated for the advanced mutliple select component
528     *
529     * @access     public
530     * @return     string
531     * @since      0.4.0
532     */
533    function toHtml()
534    {
535        if ($this->_flagFrozen) {
536            return $this->getFrozenHtml();
537        }
538
539        $tabs    = $this->_getTabs();
540        $tab     = $this->_getTab();
541        $strHtml = '';
542
543        if ($this->getComment() != '') {
544            $strHtml .= $tabs . '<!-- ' . $this->getComment() . " //-->" . PHP_EOL;
545        }
546
547        $selectId       = $this->getName();
548        $selectName     = $this->getName() . '[]';
549        $selectNameFrom = $this->getName() . '-f[]';
550        $selectNameTo   = $this->getName() . '-t[]';
551        $selected_count = 0;
552
553        // placeholder {unselected} existence determines if we will render
554        if (strpos($this->_elementTemplate, '{unselected}') === false) {
555            // ... a single multi-select with checkboxes
556            $this->_jsPostfix = 'EditSelection';
557
558            $id = $this->getAttribute('name');
559
560            $strHtmlSelected  = $tab . '<div id="qfams_'.$id.'">'  . PHP_EOL;
561            $unselected_count = count($this->_options);
562
563            $checkbox_id_suffix = 0;
564
565            foreach ($this->_options as $option) {
566                $_labelAttributes
567                    = array('style', 'class', 'onmouseover', 'onmouseout');
568                $labelAttributes = array();
569                foreach ($_labelAttributes as $attr) {
570                    if (isset($option['attr'][$attr])) {
571                        $labelAttributes[$attr] = $option['attr'][$attr];
572                        unset($option['attr'][$attr]);
573                    }
574                }
575
576                if (is_array($this->_values)
577                    && in_array((string)$option['attr']['value'], $this->_values)) {
578                    // The items is *selected*
579                    $checked = ' checked="checked"';
580                    $selected_count++;
581                } else {
582                    // The item is *unselected* so we want to put it
583                    $checked = '';
584                }
585                $checkbox_id_suffix++;
586                $strHtmlSelected .= $tab
587                                 .  '<label'
588                                 .  $this->_getAttrString($labelAttributes) .'>'
589                                 .  '<input type="checkbox"'
590                                 .  ' id="'.$selectId . $checkbox_id_suffix.'"'
591                                 .  ' name="'.$selectName.'"'
592                                 .  $checked
593                                 .  $this->_getAttrString($option['attr'])
594                                 .  ' />' .  $option['text'] . '</label>'
595                                 .  PHP_EOL;
596            }
597            $strHtmlSelected .= $tab . '</div>'. PHP_EOL;
598
599            $strHtmlHidden     = '';
600            $strHtmlUnselected = '';
601            $strHtmlAdd        = '';
602            $strHtmlRemove     = '';
603
604            // build the select all button with all its attributes
605            $jsName     = $this->_jsPrefix . $this->_jsPostfix;
606            $attributes = array('onclick' => $jsName .
607                                             "('". $selectId ."', 1);");
608            $this->_allButtonAttributes
609                        = array_merge($this->_allButtonAttributes, $attributes);
610            $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
611            $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
612
613            // build the select none button with all its attributes
614            $attributes = array('onclick' => $jsName .
615                                             "('". $selectId ."', 0);");
616            $this->_noneButtonAttributes
617                         = array_merge($this->_noneButtonAttributes, $attributes);
618            $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
619            $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
620
621            // build the toggle selection button with all its attributes
622            $attributes = array('onclick' => $jsName .
623                                             "('". $selectId ."', 2);");
624            $this->_toggleButtonAttributes
625                           = array_merge($this->_toggleButtonAttributes,
626                                 $attributes);
627            $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
628            $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
629
630            $strHtmlMoveUp   = '';
631            $strHtmlMoveDown = '';
632
633            // default selection counters
634            $strHtmlSelectedCount = $selected_count . '/' . $unselected_count;
635        } else {
636            // ... or a dual multi-select
637            $this->_jsPostfix = 'MoveSelection';
638            $jsName           = $this->_jsPrefix . $this->_jsPostfix;
639
640            // set name of Select From Box
641            $this->_attributesUnselected
642                = array('id' => $selectId . '-f',
643                        'name' => $selectNameFrom,
644                        'ondblclick' => $jsName .
645                            "('{$selectId}', ".
646                            "this.form.elements['" . $selectNameFrom . "'], " .
647                            "this.form.elements['" . $selectNameTo . "'], " .
648                            "this.form.elements['" . $selectName . "'], " .
649                            "'add', '{$this->_sort}')");
650            $this->_attributesUnselected
651                = array_merge($this->_attributes, $this->_attributesUnselected);
652            $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
653
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
668            // set name of Select hidden Box
669            $this->_attributesHidden
670                = array('name' => $selectName,
671                        'style' => 'overflow: hidden; visibility: hidden; ' .
672                                   'width: 1px; height: 0;');
673            $this->_attributesHidden
674                = array_merge($this->_attributes, $this->_attributesHidden);
675            $attrHidden = $this->_getAttrString($this->_attributesHidden);
676
677            // prepare option tables to be displayed as in POST order
678            $append = count($this->_values);
679            if ($append > 0) {
680                $arrHtmlSelected = array_fill(0, $append, ' ');
681            } else {
682                $arrHtmlSelected = array();
683            }
684
685            $options           = count($this->_options);
686            $arrHtmlUnselected = array();
687            if ($options > 0) {
688                $arrHtmlHidden = array_fill(0, $options, ' ');
689
690                foreach ($this->_options as $option) {
691                    if (is_array($this->_values)
692                        && in_array((string)$option['attr']['value'],
693                               $this->_values)) {
694                        // Get the post order
695                        $key = array_search($option['attr']['value'],
696                                   $this->_values);
697
698                        /** The items is *selected* so we want to put it
699                            in the 'selected' multi-select */
700                        $arrHtmlSelected[$key] = $option;
701                        /** Add it to the 'hidden' multi-select
702                            and set it as 'selected' */
703                        $option['attr']['selected'] = 'selected';
704                        $arrHtmlHidden[$key]        = $option;
705                    } else {
706                        /** The item is *unselected* so we want to put it
707                            in the 'unselected' multi-select */
708                        $arrHtmlUnselected[] = $option;
709                        // Add it to the hidden multi-select as 'unselected'
710                        $arrHtmlHidden[$append] = $option;
711                        $append++;
712                    }
713                }
714            } else {
715                $arrHtmlHidden = array();
716            }
717
718            // The 'unselected' multi-select which appears on the left
719            $unselected_count = count($arrHtmlUnselected);
720
721            if ($unselected_count == 0) {
722                $this->_attributesUnselected['disabled'] = 'disabled';
723                $this->_attributesUnselected
724                    = array_merge($this->_attributes, $this->_attributesUnselected);
725                $attrUnselected = $this->_getAttrString($this->_attributesUnselected);
726            }
727            $strHtmlUnselected = "<select$attrUnselected>". PHP_EOL;
728            if ($unselected_count > 0) {
729                foreach ($arrHtmlUnselected as $data) {
730                    $strHtmlUnselected
731                        .= $tabs . $tab
732                        . '<option' . $this->_getAttrString($data['attr']) . '>'
733                        . $data['text'] . '</option>' . PHP_EOL;
734                }
735            } else {
736                $strHtmlUnselected .= '<option value="">&nbsp;</option>';
737            }
738            $strHtmlUnselected .= '</select>';
739
740            // The 'selected' multi-select which appears on the right
741            $selected_count = count($arrHtmlSelected);
742
743            if ($selected_count == 0) {
744                $this->_attributesSelected['disabled'] = 'disabled';
745                $this->_attributesSelected
746                    = array_merge($this->_attributes, $this->_attributesSelected);
747                $attrSelected = $this->_getAttrString($this->_attributesSelected);
748            }
749            $strHtmlSelected = "<select$attrSelected>". PHP_EOL;
750            if ($selected_count > 0) {
751                foreach ($arrHtmlSelected as $data) {
752                    $strHtmlSelected
753                        .= $tabs . $tab
754                        . '<option' . $this->_getAttrString($data['attr']) . '>'
755                        . $data['text'] . '</option>' . PHP_EOL;
756                }
757            } else {
758                $strHtmlSelected .= '<option value="">&nbsp;</option>';
759            }
760            $strHtmlSelected .= '</select>';
761
762            // The 'hidden' multi-select
763            $strHtmlHidden = "<select$attrHidden>". PHP_EOL;
764            if (count($arrHtmlHidden) > 0) {
765                foreach ($arrHtmlHidden as $data) {
766                    $strHtmlHidden
767                        .= $tabs . $tab
768                        . '<option' . $this->_getAttrString($data['attr']) . '>'
769                        . $data['text'] . '</option>' . PHP_EOL;
770                }
771            }
772            $strHtmlHidden .= '</select>';
773
774            // build the remove button with all its attributes
775            $attributes
776                = array('onclick' => $jsName .
777                            "('{$selectId}', " .
778                            "this.form.elements['" . $selectNameFrom . "'], " .
779                            "this.form.elements['" . $selectNameTo . "'], " .
780                            "this.form.elements['" . $selectName . "'], " .
781                            "'remove', '{$this->_sort}'); return false;");
782            $this->_removeButtonAttributes
783                = array_merge($this->_removeButtonAttributes, $attributes);
784            $attrStrRemove = $this->_getAttrString($this->_removeButtonAttributes);
785            $strHtmlRemove = "<input$attrStrRemove />". PHP_EOL;
786
787            // build the add button with all its attributes
788            $attributes
789                = array('onclick' => $jsName .
790                            "('{$selectId}', " .
791                            "this.form.elements['" . $selectNameFrom . "'], " .
792                            "this.form.elements['" . $selectNameTo . "'], " .
793                            "this.form.elements['" . $selectName . "'], " .
794                            "'add', '{$this->_sort}'); return false;");
795            $this->_addButtonAttributes
796                = array_merge($this->_addButtonAttributes, $attributes);
797            $attrStrAdd = $this->_getAttrString($this->_addButtonAttributes);
798            $strHtmlAdd = "<input$attrStrAdd />". PHP_EOL;
799
800            // build the select all button with all its attributes
801            $attributes
802                = array('onclick' => $jsName .
803                            "('{$selectId}', " .
804                            "this.form.elements['" . $selectNameFrom . "'], " .
805                            "this.form.elements['" . $selectNameTo . "'], " .
806                            "this.form.elements['" . $selectName . "'], " .
807                            "'all', '{$this->_sort}'); return false;");
808            $this->_allButtonAttributes
809                = array_merge($this->_allButtonAttributes, $attributes);
810            $attrStrAll = $this->_getAttrString($this->_allButtonAttributes);
811            $strHtmlAll = "<input$attrStrAll />". PHP_EOL;
812
813            // build the select none button with all its attributes
814            $attributes
815                = array('onclick' => $jsName .
816                            "('{$selectId}', " .
817                            "this.form.elements['" . $selectNameFrom . "'], " .
818                            "this.form.elements['" . $selectNameTo . "'], " .
819                            "this.form.elements['" . $selectName . "'], " .
820                            "'none', '{$this->_sort}'); return false;");
821            $this->_noneButtonAttributes
822                = array_merge($this->_noneButtonAttributes, $attributes);
823            $attrStrNone = $this->_getAttrString($this->_noneButtonAttributes);
824            $strHtmlNone = "<input$attrStrNone />". PHP_EOL;
825
826            // build the toggle button with all its attributes
827            $attributes
828                = array('onclick' => $jsName .
829                            "('{$selectId}', " .
830                            "this.form.elements['" . $selectNameFrom . "'], " .
831                            "this.form.elements['" . $selectNameTo . "'], " .
832                            "this.form.elements['" . $selectName . "'], " .
833                            "'toggle', '{$this->_sort}'); return false;");
834            $this->_toggleButtonAttributes
835                = array_merge($this->_toggleButtonAttributes, $attributes);
836            $attrStrToggle = $this->_getAttrString($this->_toggleButtonAttributes);
837            $strHtmlToggle = "<input$attrStrToggle />". PHP_EOL;
838
839            // build the move up button with all its attributes
840            $attributes
841                = array('onclick' => "{$this->_jsPrefix}MoveUp" .
842                            "(this.form.elements['" . $selectNameTo . "'], " .
843                            "this.form.elements['" . $selectName . "']); " .
844                            "return false;");
845            $this->_upButtonAttributes
846                = array_merge($this->_upButtonAttributes, $attributes);
847            $attrStrUp     = $this->_getAttrString($this->_upButtonAttributes);
848            $strHtmlMoveUp = "<input$attrStrUp />". PHP_EOL;
849
850            // build the move down button with all its attributes
851            $attributes
852                = array('onclick' => "{$this->_jsPrefix}MoveDown" .
853                            "(this.form.elements['" . $selectNameTo . "'], " .
854                            "this.form.elements['" . $selectName . "']); " .
855                            "return false;");
856            $this->_downButtonAttributes
857                = array_merge($this->_downButtonAttributes, $attributes);
858            $attrStrDown     = $this->_getAttrString($this->_downButtonAttributes);
859            $strHtmlMoveDown = "<input$attrStrDown />". PHP_EOL;
860
861            // default selection counters
862            $strHtmlSelectedCount = $selected_count;
863        }
864        $strHtmlUnselectedCount = $unselected_count;
865
866        $strHtmlSelectedCountId   = $selectId .'_selected';
867        $strHtmlUnselectedCountId = $selectId .'_unselected';
868
869        // render all part of the multi select component with the template
870        $strHtml = $this->_elementTemplate;
871
872        // Prepare multiple labels
873        $labels = $this->getLabel();
874        if (is_array($labels)) {
875            array_shift($labels);
876        }
877        // render extra labels, if any
878        if (is_array($labels)) {
879            foreach ($labels as $key => $text) {
880                $key     = is_int($key)? $key + 2: $key;
881                $strHtml = str_replace("{label_{$key}}", $text, $strHtml);
882                $strHtml = str_replace("<!-- BEGIN label_{$key} -->", '', $strHtml);
883                $strHtml = str_replace("<!-- END label_{$key} -->", '', $strHtml);
884            }
885        }
886        // clean up useless label tags
887        if (strpos($strHtml, '{label_')) {
888            $strHtml = preg_replace('/\s*<!-- BEGIN label_(\S+) -->'.
889                           '.*<!-- END label_\1 -->\s*/i', '', $strHtml);
890        }
891
892        $placeHolders = array(
893            '{stylesheet}', '{javascript}',
894            '{class}',
895            '{unselected_count_id}', '{selected_count_id}',
896            '{unselected_count}', '{selected_count}',
897            '{unselected}', '{selected}',
898            '{add}', '{remove}',
899            '{all}', '{none}', '{toggle}',
900            '{moveup}', '{movedown}'
901        );
902        $htmlElements = array(
903            $this->getElementCss(false), $this->getElementJs(false),
904            $this->_tableAttributes,
905            $strHtmlUnselectedCountId, $strHtmlSelectedCountId,
906            $strHtmlUnselectedCount, $strHtmlSelectedCount,
907            $strHtmlUnselected, $strHtmlSelected . $strHtmlHidden,
908            $strHtmlAdd, $strHtmlRemove,
909            $strHtmlAll, $strHtmlNone, $strHtmlToggle,
910            $strHtmlMoveUp, $strHtmlMoveDown
911        );
912
913        $strHtml = str_replace($placeHolders, $htmlElements, $strHtml);
914
915        return $strHtml;
916    }
917
918    /**
919     * Returns the javascript code generated to handle this element
920     *
921     * @param boolean $raw (optional) html output with script tags or just raw data
922     *
923     * @access     public
924     * @return     string
925     * @see        setJsElement()
926     * @since      0.4.0
927     */
928    function getElementJs($raw = true)
929    {
930        $js = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'qfamsHandler.js';
931
932        if (file_exists($js)) {
933            $js = file_get_contents($js);
934        } else {
935            $js = '';
936        }
937
938        if ($raw !== true) {
939            $js = '<script type="text/javascript">'
940                . PHP_EOL . '//<![CDATA['
941                . PHP_EOL . $js
942                . PHP_EOL . '//]]>'
943                . PHP_EOL . '</script>'
944                . PHP_EOL;
945        }
946        return $js;
947    }
948}
949
950if (class_exists('HTML_QuickForm')) {
951    HTML_QuickForm::registerElementType('advmultiselect',
952        'HTML/QuickForm/advmultiselect.php', 'HTML_QuickForm_advmultiselect');
953}
954?>