HTML_QuickForm
[ class tree: HTML_QuickForm ] [ index: HTML_QuickForm ] [ all elements ]

Source for file hierselect.php

Documentation is available at hierselect.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Hierarchical select element
  6.  * 
  7.  * PHP versions 4 and 5
  8.  *
  9.  * LICENSE: This source file is subject to version 3.01 of the PHP license
  10.  * that is available through the world-wide-web at the following URI:
  11.  * http://www.php.net/license/3_01.txt If you did not receive a copy of
  12.  * the PHP License and are unable to obtain it through the web, please
  13.  * send a note to license@php.net so we can mail you a copy immediately.
  14.  *
  15.  * @category    HTML
  16.  * @package     HTML_QuickForm
  17.  * @author      Herim Vasquez <vasquezh@iro.umontreal.ca>
  18.  * @author      Bertrand Mansion <bmansion@mamasam.com>
  19.  * @author      Alexey Borzov <avb@php.net>
  20.  * @copyright   2001-2011 The PHP Group
  21.  * @license     http://www.php.net/license/3_01.txt PHP License 3.01
  22.  * @version     CVS: $Id: hierselect.php 317587 2011-10-01 07:55:53Z avb $
  23.  * @link        http://pear.php.net/package/HTML_QuickForm
  24.  */
  25.  
  26. /**
  27.  * Class for a group of form elements
  28.  */
  29. require_once 'HTML/QuickForm/group.php';
  30. /**
  31.  * Class for <select></select> elements
  32.  */
  33. require_once 'HTML/QuickForm/select.php';
  34.  
  35. /**
  36.  * Hierarchical select element
  37.  * 
  38.  * Class to dynamically create two or more HTML Select elements
  39.  * The first select changes the content of the second select and so on.
  40.  * This element is considered as a group. Selects will be named
  41.  * groupName[0], groupName[1], groupName[2]...
  42.  *
  43.  * @category    HTML
  44.  * @package     HTML_QuickForm
  45.  * @author      Herim Vasquez <vasquezh@iro.umontreal.ca>
  46.  * @author      Bertrand Mansion <bmansion@mamasam.com>
  47.  * @author      Alexey Borzov <avb@php.net>
  48.  * @version     Release: 3.2.13
  49.  * @since       3.1
  50.  */
  51. {   
  52.     // {{{ properties
  53.  
  54.     /**
  55.      * Options for all the select elements
  56.      *
  57.      * @see       setOptions()
  58.      * @var       array 
  59.      * @access    private
  60.      */
  61.     var $_options = array();
  62.     
  63.     /**
  64.      * Number of select elements on this group
  65.      *
  66.      * @var       int 
  67.      * @access    private
  68.      */
  69.     var $_nbElements = 0;
  70.  
  71.     /**
  72.      * The javascript used to set and change the options
  73.      *
  74.      * @var       string 
  75.      * @access    private
  76.      */
  77.     var $_js '';
  78.  
  79.     // }}}
  80.     // {{{ constructor
  81.  
  82.     /**
  83.      * Class constructor
  84.      * 
  85.      * @param     string    $elementName    (optional)Input field name attribute
  86.      * @param     string    $elementLabel   (optional)Input field label in form
  87.      * @param     mixed     $attributes     (optional)Either a typical HTML attribute string
  88.      *                                       or an associative array. Date format is passed along the attributes.
  89.      * @param     mixed     $separator      (optional)Use a string for one separator,
  90.      *                                       use an array to alternate the separators.
  91.      * @access    public
  92.      * @return    void 
  93.      */
  94.     function HTML_QuickForm_hierselect($elementName=null$elementLabel=null$attributes=null$separator=null)
  95.     {
  96.         $this->HTML_QuickForm_element($elementName$elementLabel$attributes);
  97.         $this->_persistantFreeze = true;
  98.         if (isset($separator)) {
  99.             $this->_separator $separator;
  100.         }
  101.         $this->_type 'hierselect';
  102.         $this->_appendName = true;
  103.     //end constructor
  104.  
  105.     // }}}
  106.     // {{{ setOptions()
  107.  
  108.     /**
  109.      * Initialize the array structure containing the options for each select element.
  110.      * Call the functions that actually do the magic.
  111.      *
  112.      * Format is a bit more complex than for a simple select as we need to know
  113.      * which options are related to the ones in the previous select:
  114.      *
  115.      * Ex:
  116.      * <code>
  117.      * // first select
  118.      * $select1[0] = 'Pop';
  119.      * $select1[1] = 'Classical';
  120.      * $select1[2] = 'Funeral doom';
  121.      *
  122.      * // second select
  123.      * $select2[0][0] = 'Red Hot Chil Peppers';
  124.      * $select2[0][1] = 'The Pixies';
  125.      * $select2[1][0] = 'Wagner';
  126.      * $select2[1][1] = 'Strauss';
  127.      * $select2[2][0] = 'Pantheist';
  128.      * $select2[2][1] = 'Skepticism';
  129.      *
  130.      * // If only need two selects
  131.      * //     - and using the deprecated functions
  132.      * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
  133.      * $sel->setMainOptions($select1);
  134.      * $sel->setSecOptions($select2);
  135.      *
  136.      * //     - and using the new setOptions function
  137.      * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
  138.      * $sel->setOptions(array($select1, $select2));
  139.      *
  140.      * // If you have a third select with prices for the cds
  141.      * $select3[0][0][0] = '15.00$';
  142.      * $select3[0][0][1] = '17.00$';
  143.      * // etc
  144.      *
  145.      * // You can now use
  146.      * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
  147.      * $sel->setOptions(array($select1, $select2, $select3));
  148.      * </code>
  149.      * 
  150.      * @param     array    $options    Array of options defining each element
  151.      * @access    public
  152.      * @return    void 
  153.      */
  154.     function setOptions($options)
  155.     {
  156.         $this->_options $options;
  157.  
  158.         if (empty($this->_elements)) {
  159.             $this->_nbElements count($this->_options);
  160.             $this->_createElements();
  161.         else {
  162.             // setDefaults has probably been called before this function
  163.             // check if all elements have been created
  164.             $totalNbElements count($this->_options);
  165.             for ($i $this->_nbElements$i $totalNbElements$i ++{
  166.                 $this->_elements[=new HTML_QuickForm_select($inullarray()$this->getAttributes());
  167.                 $this->_nbElements++;
  168.             }
  169.         }
  170.         
  171.         $this->_setOptions();
  172.     // end func setMainOptions
  173.  
  174.     // }}}
  175.     // {{{ setMainOptions()
  176.     
  177.     /**
  178.      * Sets the options for the first select element. Deprecated. setOptions() should be used.
  179.      *
  180.      * @param     array     $array    Options for the first select element
  181.      *
  182.      * @access    public
  183.      * @deprecated          Deprecated since release 3.2.2
  184.      * @return    void 
  185.      */
  186.     function setMainOptions($array)
  187.     {
  188.         $this->_options[0$array;
  189.  
  190.         if (empty($this->_elements)) {
  191.             $this->_nbElements = 2;
  192.             $this->_createElements();
  193.         }
  194.     // end func setMainOptions
  195.     
  196.     // }}}
  197.     // {{{ setSecOptions()
  198.     
  199.     /**
  200.      * Sets the options for the second select element. Deprecated. setOptions() should be used.
  201.      * The main _options array is initialized and the _setOptions function is called.
  202.      *
  203.      * @param     array     $array    Options for the second select element
  204.      *
  205.      * @access    public
  206.      * @deprecated          Deprecated since release 3.2.2
  207.      * @return    void 
  208.      */
  209.     function setSecOptions($array)
  210.     {
  211.         $this->_options[1$array;
  212.  
  213.         if (empty($this->_elements)) {
  214.             $this->_nbElements = 2;
  215.             $this->_createElements();
  216.         else {
  217.             // setDefaults has probably been called before this function
  218.             // check if all elements have been created
  219.             $totalNbElements = 2;
  220.             for ($i $this->_nbElements$i $totalNbElements$i ++{
  221.                 $this->_elements[=new HTML_QuickForm_select($inullarray()$this->getAttributes());
  222.                 $this->_nbElements++;
  223.             }
  224.         }
  225.         
  226.         $this->_setOptions();
  227.     // end func setSecOptions
  228.     
  229.     // }}}
  230.     // {{{ _setOptions()
  231.     
  232.     /**
  233.      * Sets the options for each select element
  234.      *
  235.      * @access    private
  236.      * @return    void 
  237.      */
  238.     function _setOptions()
  239.     {
  240.         $toLoad '';
  241.         foreach (array_keys($this->_elementsAS $key{
  242.             $array = eval("return isset(\$this->_options[{$key}]{$toLoad})? \$this->_options[{$key}]{$toLoad}: null;");
  243.             if (is_array($array)) {
  244.                 $select =$this->_elements[$key];
  245.                 $select->_options = array();
  246.                 $select->loadArray($array);
  247.  
  248.                 $value  is_array($v $select->getValue()) $v[0key($array);
  249.                 $toLoad .= '[\'' str_replace(array('\\''\'')array('\\\\''\\\'')$value'\']';
  250.             }
  251.         }
  252.     // end func _setOptions
  253.     
  254.     // }}}
  255.     // {{{ setValue()
  256.  
  257.     /**
  258.      * Sets values for group's elements
  259.      * 
  260.      * @param     array     $value    An array of 2 or more values, for the first,
  261.      *                                 the second, the third etc. select
  262.      *
  263.      * @access    public
  264.      * @return    void 
  265.      */
  266.     function setValue($value)
  267.     {
  268.         // fix for bug #6766. Hope this doesn't break anything more 
  269.         // after bug #7961. Forgot that _nbElements was used in
  270.         // _createElements() called in several places... 
  271.         $this->_nbElements max($this->_nbElementscount($value));
  272.         parent::setValue($value);
  273.         $this->_setOptions();
  274.     // end func setValue
  275.     
  276.     // }}}
  277.     // {{{ _createElements()
  278.  
  279.     /**
  280.      * Creates all the elements for the group
  281.      * 
  282.      * @access    private
  283.      * @return    void 
  284.      */
  285.     function _createElements()
  286.     {
  287.         for ($i = 0; $i $this->_nbElements$i++{
  288.             $this->_elements[=new HTML_QuickForm_select($inullarray()$this->getAttributes());
  289.         }
  290.     // end func _createElements
  291.  
  292.     // }}}
  293.     // {{{ toHtml()
  294.  
  295.     function toHtml()
  296.     {
  297.         $this->_js '';
  298.         if (!$this->_flagFrozen{
  299.             // set the onchange attribute for each element except last
  300.             $keys     array_keys($this->_elements);
  301.             $onChange = array();
  302.             for ($i = 0; $i count($keys- 1; $i++{
  303.                 $select =$this->_elements[$keys[$i]];
  304.                 $onChange[$i$select->getAttribute('onchange');
  305.                 $select->updateAttributes(
  306.                     array('onchange' => '_hs_swapOptions(this.form, \'' $this->_escapeString($this->getName()) '\', ' $keys[$i');' $onChange[$i])
  307.                 );
  308.             }
  309.             
  310.             // create the js function to call
  311.             if (!defined('HTML_QUICKFORM_HIERSELECT_EXISTS')) {
  312.                 $this->_js .= <<<JAVASCRIPT
  313. function _hs_findOptions(ary, keys)
  314. {
  315.     if (ary == undefined) {
  316.         return {};
  317.     }
  318.     var key = keys.shift();
  319.     if (!key in ary) {
  320.         return {};
  321.     } else if (0 == keys.length) {
  322.         return ary[key];
  323.     } else {
  324.         return _hs_findOptions(ary[key], keys);
  325.     }
  326. }
  327.  
  328. function _hs_findSelect(form, groupName, selectIndex)
  329. {
  330.     if (groupName+'['+ selectIndex +']' in form) {
  331.         return form[groupName+'['+ selectIndex +']']; 
  332.     } else {
  333.         return form[groupName+'['+ selectIndex +'][]']; 
  334.     }
  335. }
  336.  
  337. function _hs_unescapeEntities(str)
  338. {
  339.     var div = document.createElement('div');
  340.     div.innerHTML = str;
  341.     return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
  342. }
  343.  
  344. function _hs_replaceOptions(ctl, options)
  345. {
  346.     var j = 0;
  347.     ctl.options.length = 0;
  348.     for (var i = 0; i < options.values.length; i++) {
  349.         ctl.options[i] = new Option(
  350.             (-1 == String(options.texts[i]).indexOf('&'))? options.texts[i]: _hs_unescapeEntities(options.texts[i]),
  351.             options.values[i], false, false
  352.         );
  353.     }
  354. }
  355.  
  356. function _hs_setValue(ctl, value)
  357. {
  358.     var testValue = {};
  359.     if (value instanceof Array) {
  360.         for (var i = 0; i < value.length; i++) {
  361.             testValue[value[i]] = true;
  362.         }
  363.     } else {
  364.         testValue[value] = true;
  365.     }
  366.     for (var i = 0; i < ctl.options.length; i++) {
  367.         if (ctl.options[i].value in testValue) {
  368.             ctl.options[i].selected = true;
  369.         }
  370.     }
  371. }
  372.  
  373. function _hs_swapOptions(form, groupName, selectIndex)
  374. {
  375.     var hsValue = [];
  376.     for (var i = 0; i <= selectIndex; i++) {
  377.         hsValue[i] = _hs_findSelect(form, groupName, i).value;
  378.     }
  379.  
  380.     _hs_replaceOptions(_hs_findSelect(form, groupName, selectIndex + 1), 
  381.                        _hs_findOptions(_hs_options[groupName][selectIndex], hsValue));
  382.     if (selectIndex + 1 < _hs_options[groupName].length) {
  383.         _hs_swapOptions(form, groupName, selectIndex + 1);
  384.     }
  385. }
  386.  
  387. function _hs_onReset(form, groupNames)
  388. {
  389.     for (var i = 0; i < groupNames.length; i++) {
  390.         try {
  391.             for (var j = 0; j <= _hs_options[groupNames[i]].length; j++) {
  392.                 _hs_setValue(_hs_findSelect(form, groupNames[i], j), _hs_defaults[groupNames[i]][j]);
  393.                 if (j < _hs_options[groupNames[i]].length) {
  394.                     _hs_replaceOptions(_hs_findSelect(form, groupNames[i], j + 1), 
  395.                                        _hs_findOptions(_hs_options[groupNames[i]][j], _hs_defaults[groupNames[i]].slice(0, j + 1)));
  396.                 }
  397.             }
  398.         } catch (e) {
  399.             if (!(e instanceof TypeError)) {
  400.                 throw e;
  401.             }
  402.         }
  403.     }
  404. }
  405.  
  406. function _hs_setupOnReset(form, groupNames)
  407. {
  408.     setTimeout(function() { _hs_onReset(form, groupNames); }, 25);
  409. }
  410.  
  411. function _hs_onReload()
  412. {
  413.     var ctl;
  414.     for (var i = 0; i < document.forms.length; i++) {
  415.         for (var j in _hs_defaults) {
  416.             if (ctl = _hs_findSelect(document.forms[i], j, 0)) {
  417.                 for (var k = 0; k < _hs_defaults[j].length; k++) {
  418.                     _hs_setValue(_hs_findSelect(document.forms[i], j, k), _hs_defaults[j][k]);
  419.                 }
  420.             }
  421.         }
  422.     }
  423.  
  424.     if (_hs_prevOnload) {
  425.         _hs_prevOnload();
  426.     }
  427. }
  428.  
  429. var _hs_prevOnload = null;
  430. if (window.onload) {
  431.     _hs_prevOnload = window.onload;
  432. }
  433. window.onload = _hs_onReload;
  434.  
  435. var _hs_options = {};
  436. var _hs_defaults = {};
  437.  
  438. JAVASCRIPT;
  439.                 define('HTML_QUICKFORM_HIERSELECT_EXISTS'true);
  440.             }
  441.             // option lists
  442.             $jsParts = array();
  443.             for ($i = 1; $i $this->_nbElements$i++{
  444.                 $jsParts[$this->_convertArrayToJavascript($this->_prepareOptions($this->_options[$i]$i));
  445.             }
  446.             $this->_js .= "\n_hs_options['" $this->_escapeString($this->getName()) "'] = [\n" .
  447.                           implode(",\n"$jsParts.
  448.                           "\n];\n";
  449.             // default value; if we don't actually have any values yet just use
  450.             // the first option (for single selects) or empty array (for multiple)
  451.             $values = array();
  452.             foreach (array_keys($this->_elementsas $key{
  453.                 if (is_array($v $this->_elements[$key]->getValue())) {
  454.                     $values[count($v> 1? $v$v[0];
  455.                 else {
  456.                     // XXX: accessing the supposedly private _options array
  457.                     $values[$this->_elements[$key]->getMultiple(|| empty($this->_elements[$key]->_options[0])?
  458.                                 array():
  459.                                 $this->_elements[$key]->_options[0]['attr']['value'];
  460.                 }
  461.             }
  462.             $this->_js .= "_hs_defaults['" $this->_escapeString($this->getName()) "'] = " .
  463.                           $this->_convertArrayToJavascript($values";\n";
  464.         }
  465.         include_once('HTML/QuickForm/Renderer/Default.php');
  466.         $renderer =new HTML_QuickForm_Renderer_Default();
  467.         $renderer->setElementTemplate('{element}');
  468.         parent::accept($renderer);
  469.  
  470.         if (!empty($onChange)) {
  471.             $keys     array_keys($this->_elements);
  472.             for ($i = 0; $i count($keys- 1; $i++{
  473.                 $this->_elements[$keys[$i]]->updateAttributes(array('onchange' => $onChange[$i]));
  474.             }
  475.         }
  476.         return (empty($this->_js)''"<script type=\"text/javascript\">\n//<![CDATA[\n" $this->_js "//]]>\n</script>".
  477.                $renderer->toHtml();
  478.     // end func toHtml
  479.  
  480.     // }}}
  481.     // {{{ accept()
  482.  
  483.     function accept(&$renderer$required = false$error = null)
  484.     {
  485.         $renderer->renderElement($this$required$error);
  486.     // end func accept
  487.  
  488.     // }}}
  489.     // {{{ onQuickFormEvent()
  490.  
  491.     function onQuickFormEvent($event$arg&$caller)
  492.     {
  493.         if ('updateValue' == $event{
  494.             // we need to call setValue() so that the secondary option
  495.             // matches the main option
  496.             return HTML_QuickForm_element::onQuickFormEvent($event$arg$caller);
  497.         else {
  498.             $ret = parent::onQuickFormEvent($event$arg$caller);
  499.             // add onreset handler to form to properly reset hierselect (see bug #2970)
  500.             if ('addElement' == $event{
  501.                 $onReset $caller->getAttribute('onreset');
  502.                 if (strlen($onReset)) {
  503.                     if (strpos($onReset'_hs_setupOnReset')) {
  504.                         $caller->updateAttributes(array('onreset' => str_replace('_hs_setupOnReset(this, ['"_hs_setupOnReset(this, ['" $this->_escapeString($this->getName()) "', "$onReset)));
  505.                     else {
  506.                         $caller->updateAttributes(array('onreset' => "var temp = function() { {$onReset} } ; if (!temp()) { return false; } ; if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) "']); } "));
  507.                     }
  508.                 else {
  509.                     $caller->updateAttributes(array('onreset' => "if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" $this->_escapeString($this->getName()) "']); } "));
  510.                 }
  511.             }
  512.             return $ret;
  513.         }
  514.     // end func onQuickFormEvent
  515.  
  516.     // }}}
  517.     // {{{ _prepareOptions()
  518.  
  519.    /**
  520.     * Prepares options for JS encoding
  521.     *
  522.     * We need to preserve order of options when adding them via javascript, so
  523.     * cannot use object literal and for/in loop (see bug #16603). Therefore we
  524.     * convert an associative array of options to two arrays of their values
  525.     * and texts. Backport from HTML_QuickForm2.
  526.     *
  527.     * @param    array   Options array
  528.     * @param    int     Depth within options array
  529.     * @link     http://pear.php.net/bugs/bug.php?id=16603
  530.     * @return   array 
  531.     * @access   private
  532.     */
  533.     function _prepareOptions($ary$depth)
  534.     {
  535.         if (!is_array($ary)) {
  536.             $ret $ary;
  537.         elseif (0 == $depth{
  538.             $ret = array('values' => array_keys($ary)'texts' => array_values($ary));
  539.         else {
  540.             $ret = array();
  541.             foreach ($ary as $k => $v{
  542.                 $ret[$k$this->_prepareOptions($v$depth - 1);
  543.             }
  544.         }
  545.         return $ret;
  546.     }
  547.  
  548.     // }}}
  549.     // {{{ _convertArrayToJavascript()
  550.  
  551.    /**
  552.     * Converts PHP array to its Javascript analog
  553.     *
  554.     * @access private
  555.     * @param  array     PHP array to convert
  556.     * @return string    Javascript representation of the value
  557.     */
  558.     function _convertArrayToJavascript($array)
  559.     {
  560.         if (!is_array($array)) {
  561.             return $this->_convertScalarToJavascript($array);
  562.         elseif (count($array&& array_keys($array!= range(0count($array- 1)) {
  563.             return '{' implode(','array_map(
  564.                 array($this'_encodeNameValue'),
  565.                 array_keys($array)array_values($array)
  566.             )) '}';
  567.         else {
  568.             return '[' implode(','array_map(
  569.                 array($this'_convertArrayToJavascript'),
  570.                 $array
  571.             )) ']';
  572.         }
  573.     }
  574.     
  575.     // }}}
  576.     // {{{ _encodeNameValue()
  577.  
  578.    /**
  579.     * Callback for array_map used to generate JS name-value pairs
  580.     *
  581.     * @param    mixed 
  582.     * @param    mixed 
  583.     * @return   string 
  584.     */
  585.     function _encodeNameValue($name$value)
  586.     {
  587.         return $this->_convertScalarToJavascript((string)$name':'
  588.                . $this->_convertArrayToJavascript($value);
  589.     }
  590.  
  591.     // }}}
  592.     // {{{ _convertScalarToJavascript()
  593.  
  594.    /**
  595.     * Converts PHP's scalar value to its Javascript analog
  596.     *
  597.     * @access private
  598.     * @param  mixed     PHP value to convert
  599.     * @return string    Javascript representation of the value
  600.     */
  601.     function _convertScalarToJavascript($val)
  602.     {
  603.         if (is_bool($val)) {
  604.             return $val 'true' 'false';
  605.         elseif (is_int($val|| is_double($val)) {
  606.             return $val;
  607.         elseif (is_string($val)) {
  608.             return "'" $this->_escapeString($val"'";
  609.         elseif (is_null($val)) {
  610.             return 'null';
  611.         else {
  612.             // don't bother
  613.             return '{}';
  614.         }
  615.     }
  616.  
  617.     // }}}
  618.     // {{{ _escapeString()
  619.  
  620.    /**
  621.     * Quotes the string so that it can be used in Javascript string constants
  622.     *
  623.     * @access private
  624.     * @param  string 
  625.     * @return string 
  626.     */
  627.     function _escapeString($str)
  628.     {
  629.         return strtr($str,array(
  630.             "\r"    => '\r',
  631.             "\n"    => '\n',
  632.             "\t"    => '\t',
  633.             "'"     => "\\'",
  634.             '"'     => '\"',
  635.             '\\'    => '\\\\'
  636.         ));
  637.     }
  638.  
  639.     // }}}
  640. // end class HTML_QuickForm_hierselect
  641. ?>

Documentation generated on Sat, 01 Oct 2011 09:00:12 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.