1 <?php
  2 /**
  3  * @package     Joomla.Libraries
  4  * @subpackage  HTML
  5  *
  6  * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
  7  * @license     GNU General Public License version 2 or later; see LICENSE.txt
  8  */
  9 
 10 defined('JPATH_PLATFORM') or die;
 11 
 12 use Joomla\Utilities\ArrayHelper;
 13 
 14 /**
 15  * Utility class for creating HTML select lists
 16  *
 17  * @since  1.5
 18  */
 19 abstract class JHtmlSelect
 20 {
 21     /**
 22      * Default values for options. Organized by option group.
 23      *
 24      * @var     array
 25      * @since   1.5
 26      */
 27     protected static $optionDefaults = array(
 28         'option' => array(
 29             'option.attr' => null,
 30             'option.disable' => 'disable',
 31             'option.id' => null,
 32             'option.key' => 'value',
 33             'option.key.toHtml' => true,
 34             'option.label' => null,
 35             'option.label.toHtml' => true,
 36             'option.text' => 'text',
 37             'option.text.toHtml' => true,
 38             'option.class' => 'class',
 39             'option.onclick' => 'onclick',
 40         ),
 41     );
 42 
 43     /**
 44      * Generates a yes/no radio list.
 45      *
 46      * @param   string  $name      The value of the HTML name attribute
 47      * @param   array   $attribs   Additional HTML attributes for the `<select>` tag
 48      * @param   string  $selected  The key that is selected
 49      * @param   string  $yes       Language key for Yes
 50      * @param   string  $no        Language key for no
 51      * @param   mixed   $id        The id for the field or false for no id
 52      *
 53      * @return  string  HTML for the radio list
 54      *
 55      * @since   1.5
 56      * @see     JFormFieldRadio
 57      */
 58     public static function booleanlist($name, $attribs = array(), $selected = null, $yes = 'JYES', $no = 'JNO', $id = false)
 59     {
 60         $arr = array(JHtml::_('select.option', '0', JText::_($no)), JHtml::_('select.option', '1', JText::_($yes)));
 61 
 62         return JHtml::_('select.radiolist', $arr, $name, $attribs, 'value', 'text', (int) $selected, $id);
 63     }
 64 
 65     /**
 66      * Generates an HTML selection list.
 67      *
 68      * @param   array    $data       An array of objects, arrays, or scalars.
 69      * @param   string   $name       The value of the HTML name attribute.
 70      * @param   mixed    $attribs    Additional HTML attributes for the `<select>` tag. This
 71      *                               can be an array of attributes, or an array of options. Treated as options
 72      *                               if it is the last argument passed. Valid options are:
 73      *                               Format options, see {@see JHtml::$formatOptions}.
 74      *                               Selection options, see {@see JHtmlSelect::options()}.
 75      *                               list.attr, string|array: Additional attributes for the select
 76      *                               element.
 77      *                               id, string: Value to use as the select element id attribute.
 78      *                               Defaults to the same as the name.
 79      *                               list.select, string|array: Identifies one or more option elements
 80      *                               to be selected, based on the option key values.
 81      * @param   string   $optKey     The name of the object variable for the option value. If
 82      *                               set to null, the index of the value array is used.
 83      * @param   string   $optText    The name of the object variable for the option text.
 84      * @param   mixed    $selected   The key that is selected (accepts an array or a string).
 85      * @param   mixed    $idtag      Value of the field id or null by default
 86      * @param   boolean  $translate  True to translate
 87      *
 88      * @return  string  HTML for the select list.
 89      *
 90      * @since   1.5
 91      */
 92     public static function genericlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false,
 93         $translate = false)
 94     {
 95         // Set default options
 96         $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'id' => false));
 97 
 98         if (is_array($attribs) && func_num_args() === 3)
 99         {
100             // Assume we have an options array
101             $options = array_merge($options, $attribs);
102         }
103         else
104         {
105             // Get options from the parameters
106             $options['id'] = $idtag;
107             $options['list.attr'] = $attribs;
108             $options['list.translate'] = $translate;
109             $options['option.key'] = $optKey;
110             $options['option.text'] = $optText;
111             $options['list.select'] = $selected;
112         }
113 
114         $attribs = '';
115 
116         if (isset($options['list.attr']))
117         {
118             if (is_array($options['list.attr']))
119             {
120                 $attribs = ArrayHelper::toString($options['list.attr']);
121             }
122             else
123             {
124                 $attribs = $options['list.attr'];
125             }
126 
127             if ($attribs !== '')
128             {
129                 $attribs = ' ' . $attribs;
130             }
131         }
132 
133         $id = $options['id'] !== false ? $options['id'] : $name;
134         $id = str_replace(array('[', ']', ' '), '', $id);
135 
136         $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
137         $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']
138             . static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol'];
139 
140         return $html;
141     }
142 
143     /**
144      * Method to build a list with suggestions
145      *
146      * @param   array    $data       An array of objects, arrays, or values.
147      * @param   string   $optKey     The name of the object variable for the option value. If
148      *                               set to null, the index of the value array is used.
149      * @param   string   $optText    The name of the object variable for the option text.
150      * @param   mixed    $idtag      Value of the field id or null by default
151      * @param   boolean  $translate  True to translate
152      *
153      * @return  string  HTML for the select list
154      *
155      * @since       3.2
156      * @deprecated  4.0  Just create the `<datalist>` directly instead
157      */
158     public static function suggestionlist($data, $optKey = 'value', $optText = 'text', $idtag = null, $translate = false)
159     {
160         // Log deprecated message
161         JLog::add(
162             'JHtmlSelect::suggestionlist() is deprecated. Create the <datalist> tag directly instead.',
163             JLog::WARNING,
164             'deprecated'
165         );
166 
167         // Note: $idtag is requried but has to be an optional argument in the funtion call due to argument order
168         if (!$idtag)
169         {
170             throw new InvalidArgumentException('$idtag is a required argument in deprecated JHtmlSelect::suggestionlist');
171         }
172 
173         // Set default options
174         $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'id' => false));
175 
176         // Get options from the parameters
177         $options['id'] = $idtag;
178         $options['list.attr'] = null;
179         $options['list.translate'] = $translate;
180         $options['option.key'] = $optKey;
181         $options['option.text'] = $optText;
182         $options['list.select'] = null;
183 
184         $id = ' id="' . $idtag . '"';
185 
186         $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
187         $html = $baseIndent . '<datalist' . $id . '>' . $options['format.eol']
188             . static::options($data, $options) . $baseIndent . '</datalist>' . $options['format.eol'];
189 
190         return $html;
191     }
192 
193     /**
194      * Generates a grouped HTML selection list from nested arrays.
195      *
196      * @param   array   $data     An array of groups, each of which is an array of options.
197      * @param   string  $name     The value of the HTML name attribute
198      * @param   array   $options  Options, an array of key/value pairs. Valid options are:
199      *                            Format options, {@see JHtml::$formatOptions}.
200      *                            Selection options. See {@see JHtmlSelect::options()}.
201      *                            group.id: The property in each group to use as the group id
202      *                            attribute. Defaults to none.
203      *                            group.label: The property in each group to use as the group
204      *                            label. Defaults to "text". If set to null, the data array index key is
205      *                            used.
206      *                            group.items: The property in each group to use as the array of
207      *                            items in the group. Defaults to "items". If set to null, group.id and
208      *                            group. label are forced to null and the data element is assumed to be a
209      *                            list of selections.
210      *                            id: Value to use as the select element id attribute. Defaults to
211      *                            the same as the name.
212      *                            list.attr: Attributes for the select element. Can be a string or
213      *                            an array of key/value pairs. Defaults to none.
214      *                            list.select: either the value of one selected option or an array
215      *                            of selected options. Default: none.
216      *                            list.translate: Boolean. If set, text and labels are translated via
217      *                            JText::_().
218      *
219      * @return  string  HTML for the select list
220      *
221      * @since   1.5
222      * @throws  RuntimeException If a group has contents that cannot be processed.
223      */
224     public static function groupedlist($data, $name, $options = array())
225     {
226         // Set default options and overwrite with anything passed in
227         $options = array_merge(
228             JHtml::$formatOptions,
229             array('format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false),
230             $options
231         );
232 
233         // Apply option rules
234         if ($options['group.items'] === null)
235         {
236             $options['group.label'] = null;
237         }
238 
239         $attribs = '';
240 
241         if (isset($options['list.attr']))
242         {
243             if (is_array($options['list.attr']))
244             {
245                 $attribs = ArrayHelper::toString($options['list.attr']);
246             }
247             else
248             {
249                 $attribs = $options['list.attr'];
250             }
251 
252             if ($attribs !== '')
253             {
254                 $attribs = ' ' . $attribs;
255             }
256         }
257 
258         $id = $options['id'] !== false ? $options['id'] : $name;
259         $id = str_replace(array('[', ']', ' '), '', $id);
260 
261         // Disable groups in the options.
262         $options['groups'] = false;
263 
264         $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
265         $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'];
266         $groupIndent = str_repeat($options['format.indent'], $options['format.depth']++);
267 
268         foreach ($data as $dataKey => $group)
269         {
270             $label = $dataKey;
271             $id = '';
272             $noGroup = is_int($dataKey);
273 
274             if ($options['group.items'] == null)
275             {
276                 // Sub-list is an associative array
277                 $subList = $group;
278             }
279             elseif (is_array($group))
280             {
281                 // Sub-list is in an element of an array.
282                 $subList = $group[$options['group.items']];
283 
284                 if (isset($group[$options['group.label']]))
285                 {
286                     $label = $group[$options['group.label']];
287                     $noGroup = false;
288                 }
289 
290                 if (isset($options['group.id']) && isset($group[$options['group.id']]))
291                 {
292                     $id = $group[$options['group.id']];
293                     $noGroup = false;
294                 }
295             }
296             elseif (is_object($group))
297             {
298                 // Sub-list is in a property of an object
299                 $subList = $group->{$options['group.items']};
300 
301                 if (isset($group->{$options['group.label']}))
302                 {
303                     $label = $group->{$options['group.label']};
304                     $noGroup = false;
305                 }
306 
307                 if (isset($options['group.id']) && isset($group->{$options['group.id']}))
308                 {
309                     $id = $group->{$options['group.id']};
310                     $noGroup = false;
311                 }
312             }
313             else
314             {
315                 throw new RuntimeException('Invalid group contents.', 1);
316             }
317 
318             if ($noGroup)
319             {
320                 $html .= static::options($subList, $options);
321             }
322             else
323             {
324                 $html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="'
325                     . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol']
326                     . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol'];
327             }
328         }
329 
330         $html .= $baseIndent . '</select>' . $options['format.eol'];
331 
332         return $html;
333     }
334 
335     /**
336      * Generates a selection list of integers.
337      *
338      * @param   integer  $start     The start integer
339      * @param   integer  $end       The end integer
340      * @param   integer  $inc       The increment
341      * @param   string   $name      The value of the HTML name attribute
342      * @param   mixed    $attribs   Additional HTML attributes for the `<select>` tag, an array of
343      *                              attributes, or an array of options. Treated as options if it is the last
344      *                              argument passed.
345      * @param   mixed    $selected  The key that is selected
346      * @param   string   $format    The printf format to be applied to the number
347      *
348      * @return  string   HTML for the select list
349      *
350      * @since   1.5
351      */
352     public static function integerlist($start, $end, $inc, $name, $attribs = null, $selected = null, $format = '')
353     {
354         // Set default options
355         $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'option.format' => '', 'id' => null));
356 
357         if (is_array($attribs) && func_num_args() === 5)
358         {
359             // Assume we have an options array
360             $options = array_merge($options, $attribs);
361 
362             // Extract the format and remove it from downstream options
363             $format = $options['option.format'];
364             unset($options['option.format']);
365         }
366         else
367         {
368             // Get options from the parameters
369             $options['list.attr'] = $attribs;
370             $options['list.select'] = $selected;
371         }
372 
373         $start = (int) $start;
374         $end   = (int) $end;
375         $inc   = (int) $inc;
376 
377         $data = array();
378 
379         for ($i = $start; $i <= $end; $i += $inc)
380         {
381             $data[$i] = $format ? sprintf($format, $i) : $i;
382         }
383 
384         // Tell genericlist() to use array keys
385         $options['option.key'] = null;
386 
387         return JHtml::_('select.genericlist', $data, $name, $options);
388     }
389 
390     /**
391      * Create a placeholder for an option group.
392      *
393      * @param   string  $text     The text for the option
394      * @param   string  $optKey   The returned object property name for the value
395      * @param   string  $optText  The returned object property name for the text
396      *
397      * @return  stdClass
398      *
399      * @deprecated  4.0  Use JHtmlSelect::groupedList()
400      * @see     JHtmlSelect::groupedList()
401      * @since   1.5
402      */
403     public static function optgroup($text, $optKey = 'value', $optText = 'text')
404     {
405         JLog::add('JHtmlSelect::optgroup() is deprecated, use JHtmlSelect::groupedList() instead.', JLog::WARNING, 'deprecated');
406 
407         // Set initial state
408         static $state = 'open';
409 
410         // Toggle between open and close states:
411         switch ($state)
412         {
413             case 'open':
414                 $obj = new stdClass;
415                 $obj->$optKey = '<OPTGROUP>';
416                 $obj->$optText = $text;
417                 $state = 'close';
418                 break;
419             case 'close':
420                 $obj = new stdClass;
421                 $obj->$optKey = '</OPTGROUP>';
422                 $obj->$optText = $text;
423                 $state = 'open';
424                 break;
425         }
426 
427         return $obj;
428     }
429 
430     /**
431      * Create an object that represents an option in an option list.
432      *
433      * @param   string   $value    The value of the option
434      * @param   string   $text     The text for the option
435      * @param   mixed    $optKey   If a string, the returned object property name for
436      *                             the value. If an array, options. Valid options are:
437      *                             attr: String|array. Additional attributes for this option.
438      *                             Defaults to none.
439      *                             disable: Boolean. If set, this option is disabled.
440      *                             label: String. The value for the option label.
441      *                             option.attr: The property in each option array to use for
442      *                             additional selection attributes. Defaults to none.
443      *                             option.disable: The property that will hold the disabled state.
444      *                             Defaults to "disable".
445      *                             option.key: The property that will hold the selection value.
446      *                             Defaults to "value".
447      *                             option.label: The property in each option array to use as the
448      *                             selection label attribute. If a "label" option is provided, defaults to
449      *                             "label", if no label is given, defaults to null (none).
450      *                             option.text: The property that will hold the the displayed text.
451      *                             Defaults to "text". If set to null, the option array is assumed to be a
452      *                             list of displayable scalars.
453      * @param   string   $optText  The property that will hold the the displayed text. This
454      *                             parameter is ignored if an options array is passed.
455      * @param   boolean  $disable  Not used.
456      *
457      * @return  stdClass
458      *
459      * @since   1.5
460      */
461     public static function option($value, $text = '', $optKey = 'value', $optText = 'text', $disable = false)
462     {
463         $options = array(
464             'attr' => null,
465             'disable' => false,
466             'option.attr' => null,
467             'option.disable' => 'disable',
468             'option.key' => 'value',
469             'option.label' => null,
470             'option.text' => 'text',
471         );
472 
473         if (is_array($optKey))
474         {
475             // Merge in caller's options
476             $options = array_merge($options, $optKey);
477         }
478         else
479         {
480             // Get options from the parameters
481             $options['option.key'] = $optKey;
482             $options['option.text'] = $optText;
483             $options['disable'] = $disable;
484         }
485 
486         $obj = new stdClass;
487         $obj->{$options['option.key']}  = $value;
488         $obj->{$options['option.text']} = trim($text) ? $text : $value;
489 
490         /*
491          * If a label is provided, save it. If no label is provided and there is
492          * a label name, initialise to an empty string.
493          */
494         $hasProperty = $options['option.label'] !== null;
495 
496         if (isset($options['label']))
497         {
498             $labelProperty = $hasProperty ? $options['option.label'] : 'label';
499             $obj->$labelProperty = $options['label'];
500         }
501         elseif ($hasProperty)
502         {
503             $obj->{$options['option.label']} = '';
504         }
505 
506         // Set attributes only if there is a property and a value
507         if ($options['attr'] !== null)
508         {
509             $obj->{$options['option.attr']} = $options['attr'];
510         }
511 
512         // Set disable only if it has a property and a value
513         if ($options['disable'] !== null)
514         {
515             $obj->{$options['option.disable']} = $options['disable'];
516         }
517 
518         return $obj;
519     }
520 
521     /**
522      * Generates the option tags for an HTML select list (with no select tag
523      * surrounding the options).
524      *
525      * @param   array    $arr        An array of objects, arrays, or values.
526      * @param   mixed    $optKey     If a string, this is the name of the object variable for
527      *                               the option value. If null, the index of the array of objects is used. If
528      *                               an array, this is a set of options, as key/value pairs. Valid options are:
529      *                               -Format options, {@see JHtml::$formatOptions}.
530      *                               -groups: Boolean. If set, looks for keys with the value
531      *                                "<optgroup>" and synthesizes groups from them. Deprecated. Defaults
532      *                                true for backwards compatibility.
533      *                               -list.select: either the value of one selected option or an array
534      *                                of selected options. Default: none.
535      *                               -list.translate: Boolean. If set, text and labels are translated via
536      *                                JText::_(). Default is false.
537      *                               -option.id: The property in each option array to use as the
538      *                                selection id attribute. Defaults to none.
539      *                               -option.key: The property in each option array to use as the
540      *                                selection value. Defaults to "value". If set to null, the index of the
541      *                                option array is used.
542      *                               -option.label: The property in each option array to use as the
543      *                                selection label attribute. Defaults to null (none).
544      *                               -option.text: The property in each option array to use as the
545      *                               displayed text. Defaults to "text". If set to null, the option array is
546      *                               assumed to be a list of displayable scalars.
547      *                               -option.attr: The property in each option array to use for
548      *                                additional selection attributes. Defaults to none.
549      *                               -option.disable: The property that will hold the disabled state.
550      *                                Defaults to "disable".
551      *                               -option.key: The property that will hold the selection value.
552      *                                Defaults to "value".
553      *                               -option.text: The property that will hold the the displayed text.
554      *                               Defaults to "text". If set to null, the option array is assumed to be a
555      *                               list of displayable scalars.
556      * @param   string   $optText    The name of the object variable for the option text.
557      * @param   mixed    $selected   The key that is selected (accepts an array or a string)
558      * @param   boolean  $translate  Translate the option values.
559      *
560      * @return  string  HTML for the select list
561      *
562      * @since   1.5
563      */
564     public static function options($arr, $optKey = 'value', $optText = 'text', $selected = null, $translate = false)
565     {
566         $options = array_merge(
567             JHtml::$formatOptions,
568             static::$optionDefaults['option'],
569             array('format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false)
570         );
571 
572         if (is_array($optKey))
573         {
574             // Set default options and overwrite with anything passed in
575             $options = array_merge($options, $optKey);
576         }
577         else
578         {
579             // Get options from the parameters
580             $options['option.key'] = $optKey;
581             $options['option.text'] = $optText;
582             $options['list.select'] = $selected;
583             $options['list.translate'] = $translate;
584         }
585 
586         $html = '';
587         $baseIndent = str_repeat($options['format.indent'], $options['format.depth']);
588 
589         foreach ($arr as $elementKey => &$element)
590         {
591             $attr = '';
592             $extra = '';
593             $label = '';
594             $id = '';
595 
596             if (is_array($element))
597             {
598                 $key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']];
599                 $text = $element[$options['option.text']];
600 
601                 if (isset($element[$options['option.attr']]))
602                 {
603                     $attr = $element[$options['option.attr']];
604                 }
605 
606                 if (isset($element[$options['option.id']]))
607                 {
608                     $id = $element[$options['option.id']];
609                 }
610 
611                 if (isset($element[$options['option.label']]))
612                 {
613                     $label = $element[$options['option.label']];
614                 }
615 
616                 if (isset($element[$options['option.disable']]) && $element[$options['option.disable']])
617                 {
618                     $extra .= ' disabled="disabled"';
619                 }
620             }
621             elseif (is_object($element))
622             {
623                 $key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']};
624                 $text = $element->{$options['option.text']};
625 
626                 if (isset($element->{$options['option.attr']}))
627                 {
628                     $attr = $element->{$options['option.attr']};
629                 }
630 
631                 if (isset($element->{$options['option.id']}))
632                 {
633                     $id = $element->{$options['option.id']};
634                 }
635 
636                 if (isset($element->{$options['option.label']}))
637                 {
638                     $label = $element->{$options['option.label']};
639                 }
640 
641                 if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']})
642                 {
643                     $extra .= ' disabled="disabled"';
644                 }
645 
646                 if (isset($element->{$options['option.class']}) && $element->{$options['option.class']})
647                 {
648                     $extra .= ' class="' . $element->{$options['option.class']} . '"';
649                 }
650 
651                 if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']})
652                 {
653                     $extra .= ' onclick="' . $element->{$options['option.onclick']} . '"';
654                 }
655             }
656             else
657             {
658                 // This is a simple associative array
659                 $key = $elementKey;
660                 $text = $element;
661             }
662 
663             /*
664              * The use of options that contain optgroup HTML elements was
665              * somewhat hacked for J1.5. J1.6 introduces the grouplist() method
666              * to handle this better. The old solution is retained through the
667              * "groups" option, which defaults true in J1.6, but should be
668              * deprecated at some point in the future.
669              */
670 
671             $key = (string) $key;
672 
673             if ($key === '<OPTGROUP>' && $options['groups'])
674             {
675                 $html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? JText::_($text) : $text) . '">' . $options['format.eol'];
676                 $baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']);
677             }
678             elseif ($key === '</OPTGROUP>' && $options['groups'])
679             {
680                 $baseIndent = str_repeat($options['format.indent'], --$options['format.depth']);
681                 $html .= $baseIndent . '</optgroup>' . $options['format.eol'];
682             }
683             else
684             {
685                 // If no string after hyphen - take hyphen out
686                 $splitText = explode(' - ', $text, 2);
687                 $text = $splitText[0];
688 
689                 if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1]))
690                 {
691                     $text .= ' - ' . $splitText[1];
692                 }
693 
694                 if (!empty($label) && $options['list.translate'])
695                 {
696                     $label = JText::_($label);
697                 }
698 
699                 if ($options['option.label.toHtml'])
700                 {
701                     $label = htmlentities($label);
702                 }
703 
704                 if (is_array($attr))
705                 {
706                     $attr = ArrayHelper::toString($attr);
707                 }
708                 else
709                 {
710                     $attr = trim($attr);
711                 }
712 
713                 $extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra;
714 
715                 if (is_array($options['list.select']))
716                 {
717                     foreach ($options['list.select'] as $val)
718                     {
719                         $key2 = is_object($val) ? $val->{$options['option.key']} : $val;
720 
721                         if ($key == $key2)
722                         {
723                             $extra .= ' selected="selected"';
724                             break;
725                         }
726                     }
727                 }
728                 elseif ((string) $key === (string) $options['list.select'])
729                 {
730                     $extra .= ' selected="selected"';
731                 }
732 
733                 if ($options['list.translate'])
734                 {
735                     $text = JText::_($text);
736                 }
737 
738                 // Generate the option, encoding as required
739                 $html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"'
740                     . $extra . '>';
741                 $html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text;
742                 $html .= '</option>' . $options['format.eol'];
743             }
744         }
745 
746         return $html;
747     }
748 
749     /**
750      * Generates an HTML radio list.
751      *
752      * @param   array    $data       An array of objects
753      * @param   string   $name       The value of the HTML name attribute
754      * @param   string   $attribs    Additional HTML attributes for the `<select>` tag
755      * @param   mixed    $optKey     The key that is selected
756      * @param   string   $optText    The name of the object variable for the option value
757      * @param   string   $selected   The name of the object variable for the option text
758      * @param   boolean  $idtag      Value of the field id or null by default
759      * @param   boolean  $translate  True if options will be translated
760      *
761      * @return  string  HTML for the select list
762      *
763      * @since   1.5
764      */
765     public static function radiolist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false,
766         $translate = false)
767     {
768 
769         if (is_array($attribs))
770         {
771             $attribs = ArrayHelper::toString($attribs);
772         }
773 
774         $id_text = $idtag ?: $name;
775 
776         $html = '<div class="controls">';
777 
778         foreach ($data as $obj)
779         {
780             $k = $obj->$optKey;
781             $t = $translate ? JText::_($obj->$optText) : $obj->$optText;
782             $id = (isset($obj->id) ? $obj->id : null);
783 
784             $extra = '';
785             $id = $id ? $obj->id : $id_text . $k;
786 
787             if (is_array($selected))
788             {
789                 foreach ($selected as $val)
790                 {
791                     $k2 = is_object($val) ? $val->$optKey : $val;
792 
793                     if ($k == $k2)
794                     {
795                         $extra .= ' selected="selected" ';
796                         break;
797                     }
798                 }
799             }
800             else
801             {
802                 $extra .= ((string) $k === (string) $selected ? ' checked="checked" ' : '');
803             }
804 
805             $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl" class="radio">';
806             $html .= "\n\t\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $k . '" ' . $extra
807                 . $attribs . ' />' . $t;
808             $html .= "\n\t" . '</label>';
809         }
810 
811         $html .= "\n";
812         $html .= '</div>';
813         $html .= "\n";
814 
815         return $html;
816     }
817 }
818