[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/pear/HTML/QuickForm/ -> hierselect.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1