adding some strings
[moodle-linuxchix.git] / lib / pear / HTML / QuickForm / hierselect.php
blob8c0f31eb24cd9fe695ac35c750dbe1fa085cc6c5
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 // +----------------------------------------------------------------------+
21 // $Id$
23 require_once('HTML/QuickForm/group.php');
24 require_once('HTML/QuickForm/select.php');
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]...
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
38 class HTML_QuickForm_hierselect extends HTML_QuickForm_group
40 // {{{ properties
42 /**
43 * Options for all the select elements
45 * Format is a bit more complex as we need to know which options
46 * are related to the ones in the previous select:
48 * Ex:
49 * // first select
50 * $select1[0] = 'Pop';
51 * $select1[1] = 'Classical';
52 * $select1[2] = 'Funeral doom';
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';
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);
68 * // - and using the new setOptions function
69 * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
70 * $sel->setOptions(array($select1, $select2));
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
77 * // You can now use
78 * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
79 * $sel->setOptions(array($select1, $select2, $select3));
81 * @var array
82 * @access private
84 var $_options = array();
86 /**
87 * Number of select elements on this group
89 * @var int
90 * @access private
92 var $_nbElements = 0;
94 /**
95 * The javascript used to set and change the options
97 * @var string
98 * @access private
100 var $_js = '';
102 // }}}
103 // {{{ constructor
106 * Class constructor
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
117 function HTML_QuickForm_hierselect($elementName=null, $elementLabel=null, $attributes=null, $separator=null)
119 $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes);
120 $this->_persistantFreeze = true;
121 if (isset($separator)) {
122 $this->_separator = $separator;
124 $this->_type = 'hierselect';
125 $this->_appendName = true;
126 } //end constructor
128 // }}}
129 // {{{ setOptions()
132 * Initialize the array structure containing the options for each select element.
133 * Call the functions that actually do the magic.
135 * @param array $options Array of options defining each element
137 * @access public
138 * @return void
140 function setOptions($options)
142 $this->_options = $options;
144 if (empty($this->_elements)) {
145 $this->_nbElements = count($this->_options);
146 $this->_createElements();
147 } else {
148 // setDefaults has probably been called before this function
149 // check if all elements have been created
150 $totalNbElements = count($this->_options);
151 for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) {
152 $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
153 $this->_nbElements++;
157 $this->_setOptions();
158 } // end func setMainOptions
160 // }}}
161 // {{{ setMainOptions()
164 * Sets the options for the first select element. Deprecated. setOptions() should be used.
166 * @param array $array Options for the first select element
168 * @access public
169 * @deprecated Deprecated since release 3.2.2
170 * @return void
172 function setMainOptions($array)
174 $this->_options[0] = $array;
176 if (empty($this->_elements)) {
177 $this->_nbElements = 2;
178 $this->_createElements();
180 } // end func setMainOptions
182 // }}}
183 // {{{ setSecOptions()
186 * Sets the options for the second select element. Deprecated. setOptions() should be used.
187 * The main _options array is initialized and the _setOptions function is called.
189 * @param array $array Options for the second select element
191 * @access public
192 * @deprecated Deprecated since release 3.2.2
193 * @return void
195 function setSecOptions($array)
197 $this->_options[1] = $array;
199 if (empty($this->_elements)) {
200 $this->_nbElements = 2;
201 $this->_createElements();
202 } else {
203 // setDefaults has probably been called before this function
204 // check if all elements have been created
205 $totalNbElements = 2;
206 for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) {
207 $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
208 $this->_nbElements++;
212 $this->_setOptions();
213 } // end func setSecOptions
215 // }}}
216 // {{{ _setOptions()
219 * Sets the options for each select element
221 * @access private
222 * @return void
224 function _setOptions()
226 $toLoad = '';
227 foreach (array_keys($this->_elements) AS $key) {
228 $array = eval("return isset(\$this->_options[{$key}]{$toLoad})? \$this->_options[{$key}]{$toLoad}: null;");
229 if (is_array($array)) {
230 $select =& $this->_elements[$key];
231 $select->_options = array();
232 $select->loadArray($array);
234 $value = is_array($v = $select->getValue()) ? $v[0] : key($array);
235 $toLoad .= '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $value) . '\']';
238 } // end func _setOptions
240 // }}}
241 // {{{ setValue()
244 * Sets values for group's elements
246 * @param array $value An array of 2 or more values, for the first,
247 * the second, the third etc. select
249 * @access public
250 * @return void
252 function setValue($value)
254 // fix for bug #6766. Hope this doesn't break anything more
255 // after bug #7961. Forgot that _nbElements was used in
256 // _createElements() called in several places...
257 $this->_nbElements = max($this->_nbElements, count($value));
258 parent::setValue($value);
259 $this->_setOptions();
260 } // end func setValue
262 // }}}
263 // {{{ _createElements()
266 * Creates all the elements for the group
268 * @access private
269 * @return void
271 function _createElements()
273 for ($i = 0; $i < $this->_nbElements; $i++) {
274 $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
276 } // end func _createElements
278 // }}}
279 // {{{ toHtml()
281 function toHtml()
283 $this->_js = '';
284 if (!$this->_flagFrozen) {
285 // set the onchange attribute for each element except last
286 $keys = array_keys($this->_elements);
287 $onChange = array();
288 for ($i = 0; $i < count($keys) - 1; $i++) {
289 $select =& $this->_elements[$keys[$i]];
290 $onChange[$i] = $select->getAttribute('onchange');
291 $select->updateAttributes(
292 array('onchange' => '_hs_swapOptions(this.form, \'' . $this->_escapeString($this->getName()) . '\', ' . $keys[$i] . ');' . $onChange[$i])
296 // create the js function to call
297 if (!defined('HTML_QUICKFORM_HIERSELECT_EXISTS')) {
298 $this->_js .= <<<JAVASCRIPT
299 function _hs_findOptions(ary, keys)
301 var key = keys.shift();
302 if (!key in ary) {
303 return {};
304 } else if (0 == keys.length) {
305 return ary[key];
306 } else {
307 return _hs_findOptions(ary[key], keys);
311 function _hs_findSelect(form, groupName, selectIndex)
313 if (groupName+'['+ selectIndex +']' in form) {
314 return form[groupName+'['+ selectIndex +']'];
315 } else {
316 return form[groupName+'['+ selectIndex +'][]'];
320 function _hs_unescapeEntities(str)
322 var div = document.createElement('div');
323 div.innerHTML = str;
324 return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
327 function _hs_replaceOptions(ctl, optionList)
329 var j = 0;
330 ctl.options.length = 0;
331 for (i in optionList) {
332 var optionText = (-1 == optionList[i].indexOf('&'))? optionList[i]: _hs_unescapeEntities(optionList[i]);
333 ctl.options[j++] = new Option(optionText, i, false, false);
337 function _hs_setValue(ctl, value)
339 var testValue = {};
340 if (value instanceof Array) {
341 for (var i = 0; i < value.length; i++) {
342 testValue[value[i]] = true;
344 } else {
345 testValue[value] = true;
347 for (var i = 0; i < ctl.options.length; i++) {
348 if (ctl.options[i].value in testValue) {
349 ctl.options[i].selected = true;
354 function _hs_swapOptions(form, groupName, selectIndex)
356 var hsValue = [];
357 for (var i = 0; i <= selectIndex; i++) {
358 hsValue[i] = _hs_findSelect(form, groupName, i).value;
361 _hs_replaceOptions(_hs_findSelect(form, groupName, selectIndex + 1),
362 _hs_findOptions(_hs_options[groupName][selectIndex], hsValue));
363 if (selectIndex + 1 < _hs_options[groupName].length) {
364 _hs_swapOptions(form, groupName, selectIndex + 1);
368 function _hs_onReset(form, groupNames)
370 for (var i = 0; i < groupNames.length; i++) {
371 try {
372 for (var j = 0; j <= _hs_options[groupNames[i]].length; j++) {
373 _hs_setValue(_hs_findSelect(form, groupNames[i], j), _hs_defaults[groupNames[i]][j]);
374 if (j < _hs_options[groupNames[i]].length) {
375 _hs_replaceOptions(_hs_findSelect(form, groupNames[i], j + 1),
376 _hs_findOptions(_hs_options[groupNames[i]][j], _hs_defaults[groupNames[i]].slice(0, j + 1)));
379 } catch (e) {
380 if (!(e instanceof TypeError)) {
381 throw e;
387 function _hs_setupOnReset(form, groupNames)
389 setTimeout(function() { _hs_onReset(form, groupNames); }, 25);
392 function _hs_onReload()
394 var ctl;
395 for (var i = 0; i < document.forms.length; i++) {
396 for (var j in _hs_defaults) {
397 if (ctl = _hs_findSelect(document.forms[i], j, 0)) {
398 for (var k = 0; k < _hs_defaults[j].length; k++) {
399 _hs_setValue(_hs_findSelect(document.forms[i], j, k), _hs_defaults[j][k]);
405 if (_hs_prevOnload) {
406 _hs_prevOnload();
410 var _hs_prevOnload = null;
411 if (window.onload) {
412 _hs_prevOnload = window.onload;
414 window.onload = _hs_onReload;
416 var _hs_options = {};
417 var _hs_defaults = {};
419 JAVASCRIPT;
420 define('HTML_QUICKFORM_HIERSELECT_EXISTS', true);
422 // option lists
423 $jsParts = array();
424 for ($i = 1; $i < $this->_nbElements; $i++) {
425 $jsParts[] = $this->_convertArrayToJavascript($this->_options[$i]);
427 $this->_js .= "\n_hs_options['" . $this->_escapeString($this->getName()) . "'] = [\n" .
428 implode(",\n", $jsParts) .
429 "\n];\n";
430 // default value; if we don't actually have any values yet just use
431 // the first option (for single selects) or empty array (for multiple)
432 $values = array();
433 foreach (array_keys($this->_elements) as $key) {
434 if (is_array($v = $this->_elements[$key]->getValue())) {
435 $values[] = count($v) > 1? $v: $v[0];
436 } else {
437 // XXX: accessing the supposedly private _options array
438 $values[] = $this->_elements[$key]->getMultiple() || empty($this->_elements[$key]->_options[0])?
439 array():
440 $this->_elements[$key]->_options[0]['attr']['value'];
443 $this->_js .= "_hs_defaults['" . $this->_escapeString($this->getName()) . "'] = " .
444 $this->_convertArrayToJavascript($values, false) . ";\n";
446 include_once('HTML/QuickForm/Renderer/Default.php');
447 $renderer =& new HTML_QuickForm_Renderer_Default();
448 $renderer->setElementTemplate('{element}');
449 parent::accept($renderer);
451 if (!empty($onChange)) {
452 $keys = array_keys($this->_elements);
453 for ($i = 0; $i < count($keys) - 1; $i++) {
454 $this->_elements[$keys[$i]]->updateAttributes(array('onchange' => $onChange[$i]));
457 return (empty($this->_js)? '': "<script type=\"text/javascript\">\n//<![CDATA[\n" . $this->_js . "//]]>\n</script>") .
458 $renderer->toHtml();
459 } // end func toHtml
461 // }}}
462 // {{{ accept()
464 function accept(&$renderer, $required = false, $error = null)
466 $renderer->renderElement($this, $required, $error);
467 } // end func accept
469 // }}}
470 // {{{ onQuickFormEvent()
472 function onQuickFormEvent($event, $arg, &$caller)
474 if ('updateValue' == $event) {
475 // we need to call setValue() so that the secondary option
476 // matches the main option
477 return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller);
478 } else {
479 $ret = parent::onQuickFormEvent($event, $arg, $caller);
480 // add onreset handler to form to properly reset hierselect (see bug #2970)
481 if ('addElement' == $event) {
482 $onReset = $caller->getAttribute('onreset');
483 if (strlen($onReset)) {
484 if (strpos($onReset, '_hs_setupOnReset')) {
485 $caller->updateAttributes(array('onreset' => str_replace('_hs_setupOnReset(this, [', "_hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "', ", $onReset)));
486 } else {
487 $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()) . "']); } "));
489 } else {
490 $caller->updateAttributes(array('onreset' => "if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "']); } "));
493 return $ret;
495 } // end func onQuickFormEvent
497 // }}}
498 // {{{ _convertArrayToJavascript()
501 * Converts PHP array to its Javascript analog
503 * @access private
504 * @param array PHP array to convert
505 * @param bool Generate Javascript object literal (default, works like PHP's associative array) or array literal
506 * @return string Javascript representation of the value
508 function _convertArrayToJavascript($array, $assoc = true)
510 if (!is_array($array)) {
511 return $this->_convertScalarToJavascript($array);
512 } else {
513 $items = array();
514 foreach ($array as $key => $val) {
515 $item = $assoc? "'" . $this->_escapeString($key) . "': ": '';
516 if (is_array($val)) {
517 $item .= $this->_convertArrayToJavascript($val, $assoc);
518 } else {
519 $item .= $this->_convertScalarToJavascript($val);
521 $items[] = $item;
524 $js = implode(', ', $items);
525 return $assoc? '{ ' . $js . ' }': '[' . $js . ']';
528 // }}}
529 // {{{ _convertScalarToJavascript()
532 * Converts PHP's scalar value to its Javascript analog
534 * @access private
535 * @param mixed PHP value to convert
536 * @return string Javascript representation of the value
538 function _convertScalarToJavascript($val)
540 if (is_bool($val)) {
541 return $val ? 'true' : 'false';
542 } elseif (is_int($val) || is_double($val)) {
543 return $val;
544 } elseif (is_string($val)) {
545 return "'" . $this->_escapeString($val) . "'";
546 } elseif (is_null($val)) {
547 return 'null';
548 } else {
549 // don't bother
550 return '{}';
554 // }}}
555 // {{{ _escapeString()
558 * Quotes the string so that it can be used in Javascript string constants
560 * @access private
561 * @param string
562 * @return string
564 function _escapeString($str)
566 return strtr($str,array(
567 "\r" => '\r',
568 "\n" => '\n',
569 "\t" => '\t',
570 "'" => "\\'",
571 '"' => '\"',
572 '\\' => '\\\\'
576 // }}}
577 } // end class HTML_QuickForm_hierselect