Subversion Repositories Applications.papyrus

Rev

Rev 320 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
320 jpm 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>                     |
443 ddelon 18
// |          Alexey Borzov <avb@php.net>
320 jpm 19
// +----------------------------------------------------------------------+
20
//
443 ddelon 21
// $Id: hierselect.php,v 1.2 2005-09-20 17:01:22 ddelon Exp $
320 jpm 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
    function HTML_QuickForm_hierselect($elementName=null, $elementLabel=null, $attributes=null, $separator=null)
118
    {
119
        $this->HTML_QuickForm_element($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
    // {{{ setOptions()
130
 
131
    /**
132
     * Initialize the array structure containing the options for each select element.
133
     * Call the functions that actually do the magic.
134
     *
135
     * @param     array    $options    Array of options defining each element
136
     *
137
     * @access    public
138
     * @return    void
139
     */
140
    function setOptions($options)
141
    {
142
        $this->_options = $options;
143
 
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++;
154
            }
155
        }
156
 
157
        $this->_setOptions();
158
    } // end func setMainOptions
159
 
160
    // }}}
161
    // {{{ setMainOptions()
162
 
163
    /**
164
     * Sets the options for the first select element. Deprecated. setOptions() should be used.
165
     *
166
     * @param     array     $array    Options for the first select element
167
     *
168
     * @access    public
169
     * @return    void
170
     */
171
    function setMainOptions($array)
172
    {
173
        $this->_options[0] = $array;
174
 
175
        if (empty($this->_elements)) {
176
            $this->_nbElements = 2;
177
            $this->_createElements();
178
        }
179
    } // end func setMainOptions
180
 
181
    // }}}
182
    // {{{ setSecOptions()
183
 
184
    /**
185
     * Sets the options for the second select element. Deprecated. setOptions() should be used.
186
     * The main _options array is initialized and the _setOptions function is called.
187
     *
188
     * @param     array     $array    Options for the second select element
189
     *
190
     * @access    public
191
     * @return    void
192
     */
193
    function setSecOptions($array)
194
    {
195
        $this->_options[1] = $array;
196
 
197
        if (empty($this->_elements)) {
198
            $this->_nbElements = 2;
199
            $this->_createElements();
200
        } else {
201
            // setDefaults has probably been called before this function
202
            // check if all elements have been created
203
            $totalNbElements = 2;
204
            for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) {
205
                $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
206
                $this->_nbElements++;
207
            }
208
        }
209
 
210
        $this->_setOptions();
211
    } // end func setSecOptions
212
 
213
    // }}}
214
    // {{{ _setOptions()
215
 
216
    /**
217
     * Sets the options for each select element
218
     *
219
     * @access    private
220
     * @return    void
221
     */
222
    function _setOptions()
223
    {
224
        $toLoad = '';
225
        foreach (array_keys($this->_elements) AS $key) {
443 ddelon 226
            $array = eval("return isset(\$this->_options[{$key}]{$toLoad})? \$this->_options[{$key}]{$toLoad}: null;");
227
            if (is_array($array)) {
228
                $select =& $this->_elements[$key];
229
                $select->_options = array();
230
                $select->loadArray($array);
320 jpm 231
 
443 ddelon 232
                $value  = is_array($v = $select->getValue()) ? $v[0] : key($array);
233
                $toLoad .= '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $value) . '\']';
320 jpm 234
            }
235
        }
236
    } // end func _setOptions
237
 
238
    // }}}
239
    // {{{ setValue()
240
 
241
    /**
242
     * Sets values for group's elements
243
     *
244
     * @param     array     $value    An array of 2 or more values, for the first,
245
     *                                the second, the third etc. select
246
     *
247
     * @access    public
248
     * @return    void
249
     */
250
    function setValue($value)
251
    {
252
        $this->_nbElements = count($value);
253
        parent::setValue($value);
254
        $this->_setOptions();
255
    } // end func setValue
256
 
257
    // }}}
258
    // {{{ _createElements()
259
 
260
    /**
261
     * Creates all the elements for the group
262
     *
263
     * @access    private
264
     * @return    void
265
     */
266
    function _createElements()
267
    {
268
        for ($i = 0; $i < $this->_nbElements; $i++) {
269
            $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
270
        }
271
    } // end func _createElements
272
 
273
    // }}}
274
    // {{{ toHtml()
275
 
276
    function toHtml()
277
    {
443 ddelon 278
        $this->_js = '';
279
        if (!$this->_flagFrozen) {
280
            // set the onchange attribute for each element except last
281
            $keys     = array_keys($this->_elements);
282
            $onChange = array();
283
            for ($i = 0; $i < count($keys) - 1; $i++) {
320 jpm 284
                $select =& $this->_elements[$keys[$i]];
443 ddelon 285
                $onChange[$i] = $select->getAttribute('onchange');
320 jpm 286
                $select->updateAttributes(
443 ddelon 287
                    array('onchange' => '_hs_swapOptions(this.form, \'' . $this->_escapeString($this->getName()) . '\', ' . $keys[$i] . ');' . $onChange[$i])
320 jpm 288
                );
289
            }
290
 
291
            // create the js function to call
292
            if (!defined('HTML_QUICKFORM_HIERSELECT_EXISTS')) {
443 ddelon 293
                $this->_js .= <<<JAVASCRIPT
294
function _hs_findOptions(ary, keys)
295
{
296
    var key = keys.shift();
297
    if (!key in ary) {
298
        return {};
299
    } else if (0 == keys.length) {
300
        return ary[key];
301
    } else {
302
        return _hs_findOptions(ary[key], keys);
303
    }
304
}
305
 
306
function _hs_findSelect(form, groupName, selectIndex)
307
{
308
    if (groupName+'['+ selectIndex +']' in form) {
309
        return form[groupName+'['+ selectIndex +']'];
310
    } else {
311
        return form[groupName+'['+ selectIndex +'][]'];
312
    }
313
}
314
 
315
function _hs_replaceOptions(ctl, optionList)
316
{
317
    var j = 0;
318
    ctl.options.length = 0;
319
    for (i in optionList) {
320
        ctl.options[j++] = new Option(optionList[i], i, false, false);
321
    }
322
}
323
 
324
function _hs_setValue(ctl, value)
325
{
326
    var testValue = {};
327
    if (value instanceof Array) {
328
        for (var i = 0; i < value.length; i++) {
329
            testValue[value[i]] = true;
330
        }
331
    } else {
332
        testValue[value] = true;
333
    }
334
    for (var i = 0; i < ctl.options.length; i++) {
335
        if (ctl.options[i].value in testValue) {
336
            ctl.options[i].selected = true;
337
        }
338
    }
339
}
340
 
341
function _hs_swapOptions(form, groupName, selectIndex)
342
{
343
    var hsValue = [];
344
    for (var i = 0; i <= selectIndex; i++) {
345
        hsValue[i] = _hs_findSelect(form, groupName, i).value;
346
    }
347
 
348
    _hs_replaceOptions(_hs_findSelect(form, groupName, selectIndex + 1),
349
                       _hs_findOptions(_hs_options[groupName][selectIndex], hsValue));
350
    if (selectIndex + 1 < _hs_options[groupName].length) {
351
        _hs_swapOptions(form, groupName, selectIndex + 1);
352
    }
353
}
354
 
355
function _hs_onReset(form, groupNames)
356
{
357
    for (var i = 0; i < groupNames.length; i++) {
358
        try {
359
            for (var j = 0; j <= _hs_options[groupNames[i]].length; j++) {
360
                _hs_setValue(_hs_findSelect(form, groupNames[i], j), _hs_defaults[groupNames[i]][j]);
361
                if (j < _hs_options[groupNames[i]].length) {
362
                    _hs_replaceOptions(_hs_findSelect(form, groupNames[i], j + 1),
363
                                       _hs_findOptions(_hs_options[groupNames[i]][j], _hs_defaults[groupNames[i]].slice(0, j + 1)));
364
                }
365
            }
366
        } catch (e) {
367
            if (!(e instanceof TypeError)) {
368
                throw e;
369
            }
370
        }
371
    }
372
}
373
 
374
function _hs_setupOnReset(form, groupNames)
375
{
376
    setTimeout(function() { _hs_onReset(form, groupNames); }, 25);
377
}
378
 
379
function _hs_onReload()
380
{
381
    var ctl;
382
    for (var i = 0; i < document.forms.length; i++) {
383
        for (var j in _hs_defaults) {
384
            if (ctl = _hs_findSelect(document.forms[i], j, 0)) {
385
                for (var k = 0; k < _hs_defaults[j].length; k++) {
386
                    _hs_setValue(_hs_findSelect(document.forms[i], j, k), _hs_defaults[j][k]);
387
                }
388
            }
389
        }
390
    }
391
 
392
    if (_hs_prevOnload) {
393
        _hs_prevOnload();
394
    }
395
}
396
 
397
var _hs_prevOnload = null;
398
if (window.onload) {
399
    _hs_prevOnload = window.onload;
400
}
401
window.onload = _hs_onReload;
402
 
403
var _hs_options = {};
404
var _hs_defaults = {};
405
 
406
JAVASCRIPT;
320 jpm 407
                define('HTML_QUICKFORM_HIERSELECT_EXISTS', true);
408
            }
443 ddelon 409
            // option lists
410
            $jsParts = array();
411
            for ($i = 1; $i < $this->_nbElements; $i++) {
412
                $jsParts[] = $this->_convertArrayToJavascript($this->_options[$i]);
413
            }
414
            $this->_js .= "\n_hs_options['" . $this->_escapeString($this->getName()) . "'] = [\n" .
415
                          implode(",\n", $jsParts) .
416
                          "\n];\n";
417
            // default value; if we don't actually have any values yet just use
418
            // the first option (for single selects) or empty array (for multiple)
419
            $values = array();
420
            foreach (array_keys($this->_elements) as $key) {
421
                if (is_array($v = $this->_elements[$key]->getValue())) {
422
                    $values[] = count($v) > 1? $v: $v[0];
423
                } else {
424
                    // XXX: accessing the supposedly private _options array
425
                    $values[] = $this->_elements[$key]->getMultiple() || empty($this->_elements[$key]->_options[0])?
426
                                array():
427
                                $this->_elements[$key]->_options[0]['attr']['value'];
428
                }
429
            }
430
            $this->_js .= "_hs_defaults['" . $this->_escapeString($this->getName()) . "'] = " .
431
                          $this->_convertArrayToJavascript($values, false) . ";\n";
320 jpm 432
        }
433
        include_once('HTML/QuickForm/Renderer/Default.php');
434
        $renderer =& new HTML_QuickForm_Renderer_Default();
435
        $renderer->setElementTemplate('{element}');
436
        parent::accept($renderer);
443 ddelon 437
 
438
        if (!empty($onChange)) {
439
            $keys     = array_keys($this->_elements);
440
            for ($i = 0; $i < count($keys) - 1; $i++) {
441
                $this->_elements[$keys[$i]]->updateAttributes(array('onchange' => $onChange[$i]));
442
            }
443
        }
444
        return (empty($this->_js)? '': "<script type=\"text/javascript\">\n//<![CDATA[\n" . $this->_js . "//]]>\n</script>") .
320 jpm 445
               $renderer->toHtml();
446
    } // end func toHtml
447
 
448
    // }}}
449
    // {{{ accept()
450
 
451
    function accept(&$renderer, $required = false, $error = null)
452
    {
453
        $renderer->renderElement($this, $required, $error);
454
    } // end func accept
455
 
456
    // }}}
457
    // {{{ onQuickFormEvent()
458
 
459
    function onQuickFormEvent($event, $arg, &$caller)
460
    {
461
        if ('updateValue' == $event) {
462
            // we need to call setValue() so that the secondary option
463
            // matches the main option
464
            return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller);
465
        } else {
443 ddelon 466
            $ret = parent::onQuickFormEvent($event, $arg, $caller);
467
            // add onreset handler to form to properly reset hierselect (see bug #2970)
468
            if ('addElement' == $event) {
469
                $onReset = $caller->getAttribute('onreset');
470
                if (strlen($onReset)) {
471
                    if (strpos($onReset, '_hs_setupOnReset')) {
472
                        $caller->updateAttributes(array('onreset' => str_replace('_hs_setupOnReset(this, [', "_hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "', ", $onReset)));
473
                    } else {
474
                        $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()) . "']); } "));
475
                    }
476
                } else {
477
                    $caller->updateAttributes(array('onreset' => "if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "']); } "));
478
                }
479
            }
480
            return $ret;
320 jpm 481
        }
482
    } // end func onQuickFormEvent
483
 
443 ddelon 484
    // }}}
485
    // {{{ _convertArrayToJavascript()
486
 
487
   /**
488
    * Converts PHP array to its Javascript analog
489
    *
490
    * @access private
491
    * @param  array     PHP array to convert
492
    * @param  bool      Generate Javascript object literal (default, works like PHP's associative array) or array literal
493
    * @return string    Javascript representation of the value
494
    */
495
    function _convertArrayToJavascript($array, $assoc = true)
496
    {
497
        if (!is_array($array)) {
498
            return $this->_convertScalarToJavascript($array);
499
        } else {
500
            $items = array();
501
            foreach ($array as $key => $val) {
502
                $item = $assoc? "'" . $this->_escapeString($key) . "': ": '';
503
                if (is_array($val)) {
504
                    $item .= $this->_convertArrayToJavascript($val, $assoc);
505
                } else {
506
                    $item .= $this->_convertScalarToJavascript($val);
507
                }
508
                $items[] = $item;
509
            }
510
        }
511
        $js = implode(', ', $items);
512
        return $assoc? '{ ' . $js . ' }': '[' . $js . ']';
513
    }
514
 
515
    // }}}
516
    // {{{ _convertScalarToJavascript()
517
 
518
   /**
519
    * Converts PHP's scalar value to its Javascript analog
520
    *
521
    * @access private
522
    * @param  mixed     PHP value to convert
523
    * @return string    Javascript representation of the value
524
    */
525
    function _convertScalarToJavascript($val)
526
    {
527
        if (is_bool($val)) {
528
            return $val ? 'true' : 'false';
529
        } elseif (is_int($val) || is_double($val)) {
530
            return $val;
531
        } elseif (is_string($val)) {
532
            return "'" . $this->_escapeString($val) . "'";
533
        } elseif (is_null($val)) {
534
            return 'null';
535
        } else {
536
            // don't bother
537
            return '{}';
538
        }
539
    }
540
 
541
    // }}}
542
    // {{{ _escapeString()
543
 
544
   /**
545
    * Quotes the string so that it can be used in Javascript string constants
546
    *
547
    * @access private
548
    * @param  string
549
    * @return string
550
    */
551
    function _escapeString($str)
552
    {
553
        return strtr($str,array(
554
            "\r"    => '\r',
555
            "\n"    => '\n',
556
            "\t"    => '\t',
557
            "'"     => "\\'",
558
            '"'     => '\"',
559
            '\\'    => '\\\\'
560
        ));
561
    }
562
 
563
    // }}}
320 jpm 564
} // end class HTML_QuickForm_hierselect
565
?>