[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |