Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4: */
   3  // +----------------------------------------------------------------------+
   4  // | PHP Version 4                                                        |
   5  // +----------------------------------------------------------------------+
   6  // | Copyright (c) 1997-2003 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  // | Author:  Matteo Di Giovinazzo <matteodg@infinito.it>                 |
  17  // |                                                                      |
  18  // | For the JavaScript code thanks to Martin Honnen and                  |
  19  // | Nicholas C. Zakas                                                    |
  20  // | See:                                                                 |
  21  // |      http://www.faqts.com/knowledge_base/view.phtml/aid/13562        |
  22  // | and                                                                  |
  23  // |      http://www.sitepoint.com/article/1220                           |
  24  // +----------------------------------------------------------------------+
  25  //
  26  // $Id$
  27  
  28  
  29  require_once("HTML/QuickForm/text.php");
  30  
  31  
  32  /**
  33   * Class to dynamically create an HTML input text element that
  34   * at every keypressed javascript event, check in an array of options
  35   * if there's a match and autocomplete the text in case of match.
  36   *
  37   * Ex:
  38   * $autocomplete =& $form->addElement('autocomplete', 'fruit', 'Favourite fruit:');
  39   * $options = array("Apple", "Orange", "Pear", "Strawberry");
  40   * $autocomplete->setOptions($options);
  41   *
  42   * @author       Matteo Di Giovinazzo <matteodg@infinito.it>
  43   */
  44  class HTML_QuickForm_autocomplete extends HTML_QuickForm_text
  45  {
  46      // {{{ properties
  47  
  48      /**
  49       * Options for the autocomplete input text element
  50       *
  51       * @var       array
  52       * @access    private
  53       */
  54      var $_options = array();
  55  
  56      /**
  57       * "One-time" javascript (containing functions), see bug #4611
  58       *
  59       * @var     string
  60       * @access  private
  61       */
  62      var $_js = '';
  63  
  64      // }}}
  65      // {{{ constructor
  66  
  67      /**
  68       * Class constructor
  69       *
  70       * @param     string    $elementName    (optional)Input field name attribute
  71       * @param     string    $elementLabel   (optional)Input field label in form
  72       * @param     array     $options        (optional)Autocomplete options
  73       * @param     mixed     $attributes     (optional)Either a typical HTML attribute string
  74       *                                      or an associative array. Date format is passed along the attributes.
  75       * @access    public
  76       * @return    void
  77       */
  78      public function __construct($elementName = null, $elementLabel = null, $options = null, $attributes = null) {
  79          parent::__construct($elementName, $elementLabel, $attributes);
  80          $this->_persistantFreeze = true;
  81          $this->_type = 'autocomplete';
  82          if (isset($options)) {
  83              $this->setOptions($options);
  84          }
  85      } //end constructor
  86  
  87      /**
  88       * Old syntax of class constructor. Deprecated in PHP7.
  89       *
  90       * @deprecated since Moodle 3.1
  91       */
  92      public function HTML_QuickForm_autocomplete($elementName = null, $elementLabel = null, $options = null, $attributes = null) {
  93          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
  94          self::__construct($elementName, $elementLabel, $options, $attributes);
  95      }
  96  
  97      // }}}
  98      // {{{ setOptions()
  99  
 100      /**
 101       * Sets the options for the autocomplete input text element
 102       *
 103       * @param     array    $options    Array of options for the autocomplete input text element
 104       * @access    public
 105       * @return    void
 106       */
 107      function setOptions($options)
 108      {
 109          $this->_options = array_values($options);
 110      } // end func setOptions
 111  
 112      // }}}
 113      // {{{ toHtml()
 114  
 115      /**
 116       * Returns Html for the autocomplete input text element
 117       *
 118       * @access      public
 119       * @return      string
 120       */
 121      function toHtml()
 122      {
 123          // prevent problems with grouped elements
 124          $arrayName = str_replace(array('[', ']'), array('__', ''), $this->getName()) . '_values';
 125  
 126          $this->updateAttributes(array(
 127              'onkeypress' => 'return autocomplete(this, event, ' . $arrayName . ');'
 128          ));
 129          if ($this->_flagFrozen) {
 130              $js = '';
 131          } else {
 132              $js = "<script type=\"text/javascript\">\n//<![CDATA[\n";
 133              if (!defined('HTML_QUICKFORM_AUTOCOMPLETE_EXISTS')) {
 134                  $this->_js .= <<<EOS
 135  
 136  /* begin javascript for autocomplete */
 137  function setSelectionRange(input, selectionStart, selectionEnd) {
 138      if (input.setSelectionRange) {
 139          input.setSelectionRange(selectionStart, selectionEnd);
 140      }
 141      else if (input.createTextRange) {
 142          var range = input.createTextRange();
 143          range.collapse(true);
 144          range.moveEnd("character", selectionEnd);
 145          range.moveStart("character", selectionStart);
 146          range.select();
 147      }
 148      input.focus();
 149  }
 150  
 151  function setCaretToPosition(input, position) {
 152      setSelectionRange(input, position, position);
 153  }
 154  
 155  function replaceSelection (input, replaceString) {
 156  	 var len = replaceString.length;
 157      if (input.setSelectionRange) {
 158          var selectionStart = input.selectionStart;
 159          var selectionEnd = input.selectionEnd;
 160  
 161          input.value = input.value.substring(0, selectionStart) + replaceString + input.value.substring(selectionEnd);
 162  	 	 input.selectionStart  = selectionStart + len;
 163  	 	 input.selectionEnd  = selectionStart + len;
 164      }
 165      else if (document.selection) {
 166          var range = document.selection.createRange();
 167  	 	 var saved_range = range.duplicate();
 168  
 169          if (range.parentElement() == input) {
 170              range.text = replaceString;
 171  	 	 	 range.moveEnd("character", saved_range.selectionStart + len);
 172  	 	 	 range.moveStart("character", saved_range.selectionStart + len);
 173  	 	 	 range.select();
 174          }
 175      }
 176      input.focus();
 177  }
 178  
 179  
 180  function autocompleteMatch (text, values) {
 181      for (var i = 0; i < values.length; i++) {
 182          if (values[i].toUpperCase().indexOf(text.toUpperCase()) == 0) {
 183              return values[i];
 184          }
 185      }
 186  
 187      return null;
 188  }
 189  
 190  function autocomplete(textbox, event, values) {
 191      if (textbox.setSelectionRange || textbox.createTextRange) {
 192          switch (event.keyCode) {
 193              case 38:    // up arrow
 194              case 40:    // down arrow
 195              case 37:    // left arrow
 196              case 39:    // right arrow
 197              case 33:    // page up
 198              case 34:    // page down
 199              case 36:    // home
 200              case 35:    // end
 201              case 13:    // enter
 202              case 9:     // tab
 203              case 27:    // esc
 204              case 16:    // shift
 205              case 17:    // ctrl
 206              case 18:    // alt
 207              case 20:    // caps lock
 208              case 8:     // backspace
 209              case 46:    // delete
 210                  return true;
 211                  break;
 212  
 213              default:
 214                  var c = String.fromCharCode(
 215                      (event.charCode == undefined) ? event.keyCode : event.charCode
 216                  );
 217                  replaceSelection(textbox, c);
 218                  sMatch = autocompleteMatch(textbox.value, values);
 219                  var len = textbox.value.length;
 220  	 	 	 	 
 221                  if (sMatch != null) {
 222                      textbox.value = sMatch;
 223                      setSelectionRange(textbox, len, textbox.value.length);
 224                  }
 225                  return false;
 226          }
 227      }
 228      else {
 229          return true;
 230      }
 231  }
 232  /* end javascript for autocomplete */
 233  
 234  EOS;
 235                  define('HTML_QUICKFORM_AUTOCOMPLETE_EXISTS', true);
 236              }
 237              $jsEscape = array(
 238                  "\r"    => '\r',
 239                  "\n"    => '\n',
 240                  "\t"    => '\t',
 241                  "'"     => "\\'",
 242                  '"'     => '\"',
 243                  '\\'    => '\\\\'
 244              );
 245  
 246              $js .= $this->_js;
 247              $js .= 'var ' . $arrayName . " = new Array();\n";
 248              for ($i = 0; $i < count($this->_options); $i++) {
 249                  $js .= $arrayName . '[' . $i . "] = '" . strtr($this->_options[$i], $jsEscape) . "';\n";
 250              }
 251              $js .= "//]]>\n</script>";
 252          }
 253          return $js . parent::toHtml();
 254      }// end func toHtml
 255  
 256      // }}}
 257  } // end class HTML_QuickForm_autocomplete
 258  ?>