2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
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 // +----------------------------------------------------------------------+
23 require_once('HTML/QuickForm/group.php');
24 require_once('HTML/QuickForm/select.php');
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>
38 class HTML_QuickForm_hierselect
extends HTML_QuickForm_group
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:
50 * $select1[0] = 'Pop';
51 * $select1[1] = 'Classical';
52 * $select1[2] = 'Funeral doom';
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$';
78 * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
79 * $sel->setOptions(array($select1, $select2, $select3));
84 var $_options = array();
87 * Number of select elements on this group
95 * The javascript used to set and change the options
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.
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;
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
140 function setOptions($options)
142 $this->_options
= $options;
144 if (empty($this->_elements
)) {
145 $this->_nbElements
= count($this->_options
);
146 $this->_createElements();
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
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
169 * @deprecated Deprecated since release 3.2.2
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
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
192 * @deprecated Deprecated since release 3.2.2
195 function setSecOptions($array)
197 $this->_options
[1] = $array;
199 if (empty($this->_elements
)) {
200 $this->_nbElements
= 2;
201 $this->_createElements();
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
219 * Sets the options for each select element
224 function _setOptions()
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
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
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
263 // {{{ _createElements()
266 * Creates all the elements for the group
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
284 if (!$this->_flagFrozen
) {
285 // set the onchange attribute for each element except last
286 $keys = array_keys($this->_elements
);
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();
304 } else if (0 == keys.length) {
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 +']'];
316 return form[groupName+'['+ selectIndex +'][]'];
320 function _hs_unescapeEntities(str)
322 var div = document.createElement('div');
324 return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
327 function _hs_replaceOptions(ctl, optionList)
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)
340 if (value instanceof Array) {
341 for (var i = 0; i < value.length; i++) {
342 testValue[value[i]] = true;
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)
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++) {
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)));
380 if (!(e instanceof TypeError)) {
387 function _hs_setupOnReset(form, groupNames)
389 setTimeout(function() { _hs_onReset(form, groupNames); }, 25);
392 function _hs_onReload()
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) {
410 var _hs_prevOnload = null;
412 _hs_prevOnload = window.onload;
414 window.onload = _hs_onReload;
416 var _hs_options = {};
417 var _hs_defaults = {};
420 define('HTML_QUICKFORM_HIERSELECT_EXISTS', true);
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) .
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)
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];
437 // XXX: accessing the supposedly private _options array
438 $values[] = $this->_elements
[$key]->getMultiple() ||
empty($this->_elements
[$key]->_options
[0])?
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>") .
464 function accept(&$renderer, $required = false, $error = null)
466 $renderer->renderElement($this, $required, $error);
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);
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)));
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()) . "']); } "));
490 $caller->updateAttributes(array('onreset' => "if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "']); } "));
495 } // end func onQuickFormEvent
498 // {{{ _convertArrayToJavascript()
501 * Converts PHP array to its Javascript analog
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);
514 foreach ($array as $key => $val) {
515 $item = $assoc?
"'" . $this->_escapeString($key) . "': ": '';
516 if (is_array($val)) {
517 $item .= $this->_convertArrayToJavascript($val, $assoc);
519 $item .= $this->_convertScalarToJavascript($val);
524 $js = implode(', ', $items);
525 return $assoc?
'{ ' . $js . ' }': '[' . $js . ']';
529 // {{{ _convertScalarToJavascript()
532 * Converts PHP's scalar value to its Javascript analog
535 * @param mixed PHP value to convert
536 * @return string Javascript representation of the value
538 function _convertScalarToJavascript($val)
541 return $val ?
'true' : 'false';
542 } elseif (is_int($val) ||
is_double($val)) {
544 } elseif (is_string($val)) {
545 return "'" . $this->_escapeString($val) . "'";
546 } elseif (is_null($val)) {
555 // {{{ _escapeString()
558 * Quotes the string so that it can be used in Javascript string constants
564 function _escapeString($str)
566 return strtr($str,array(
577 } // end class HTML_QuickForm_hierselect