Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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