3
* Html Helper class file.
5
* Simplifies the construction of HTML elements.
7
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
8
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
10
* Licensed under The MIT License
11
* Redistributions of files must retain the above copyright notice.
13
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
14
* @link http://cakephp.org CakePHP(tm) Project
16
* @subpackage cake.cake.libs.view.helpers
17
* @since CakePHP(tm) v 0.9.1
18
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21
* Html Helper class for easy use of HTML widgets.
23
* HtmlHelper encloses all methods needed while working with HTML pages.
26
* @subpackage cake.cake.libs.view.helpers
27
* @link http://book.cakephp.org/view/1434/HTML
29
class HtmlHelper extends AppHelper {
31
* html tags used by this helper.
37
'meta' => '<meta%s/>',
38
'metalink' => '<link href="%s"%s/>',
39
'link' => '<a href="%s"%s>%s</a>',
40
'mailto' => '<a href="mailto:%s" %s>%s</a>',
41
'form' => '<form %s>',
42
'formend' => '</form>',
43
'input' => '<input name="%s" %s/>',
44
'textarea' => '<textarea name="%s" %s>%s</textarea>',
45
'hidden' => '<input type="hidden" name="%s" %s/>',
46
'checkbox' => '<input type="checkbox" name="%s" %s/>',
47
'checkboxmultiple' => '<input type="checkbox" name="%s[]"%s />',
48
'radio' => '<input type="radio" name="%s" id="%s" %s />%s',
49
'selectstart' => '<select name="%s"%s>',
50
'selectmultiplestart' => '<select name="%s[]"%s>',
51
'selectempty' => '<option value=""%s> </option>',
52
'selectoption' => '<option value="%s"%s>%s</option>',
53
'selectend' => '</select>',
54
'optiongroup' => '<optgroup label="%s"%s>',
55
'optiongroupend' => '</optgroup>',
56
'checkboxmultiplestart' => '',
57
'checkboxmultipleend' => '',
58
'password' => '<input type="password" name="%s" %s/>',
59
'file' => '<input type="file" name="%s" %s/>',
60
'file_no_model' => '<input type="file" name="%s" %s/>',
61
'submit' => '<input %s/>',
62
'submitimage' => '<input type="image" src="%s" %s/>',
63
'button' => '<button type="%s"%s>%s</button>',
64
'image' => '<img src="%s" %s/>',
65
'tableheader' => '<th%s>%s</th>',
66
'tableheaderrow' => '<tr%s>%s</tr>',
67
'tablecell' => '<td%s>%s</td>',
68
'tablerow' => '<tr%s>%s</tr>',
69
'block' => '<div%s>%s</div>',
70
'blockstart' => '<div%s>',
71
'blockend' => '</div>',
72
'tag' => '<%s%s>%s</%s>',
73
'tagstart' => '<%s%s>',
75
'para' => '<p%s>%s</p>',
76
'parastart' => '<p%s>',
77
'label' => '<label for="%s"%s>%s</label>',
78
'fieldset' => '<fieldset%s>%s</fieldset>',
79
'fieldsetstart' => '<fieldset><legend>%s</legend>',
80
'fieldsetend' => '</fieldset>',
81
'legend' => '<legend>%s</legend>',
82
'css' => '<link rel="%s" type="text/css" href="%s" %s/>',
83
'style' => '<style type="text/css"%s>%s</style>',
84
'charset' => '<meta http-equiv="Content-Type" content="text/html; charset=%s" />',
85
'ul' => '<ul%s>%s</ul>',
86
'ol' => '<ol%s>%s</ol>',
87
'li' => '<li%s>%s</li>',
88
'error' => '<div%s>%s</div>',
89
'javascriptblock' => '<script type="text/javascript"%s>%s</script>',
90
'javascriptstart' => '<script type="text/javascript">',
91
'javascriptlink' => '<script type="text/javascript" src="%s"%s></script>',
92
'javascriptend' => '</script>'
101
var $_crumbs = array();
104
* Names of script files that have been included once
109
var $__includedScripts = array();
111
* Options for the currently opened script block buffer if any.
116
var $_scriptBlockOptions = array();
118
* Document type definitions
123
var $__docTypes = array(
124
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
125
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
126
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
127
'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
128
'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
129
'xhtml-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
130
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
134
* Adds a link to the breadcrumbs array.
136
* @param string $name Text for link
137
* @param string $link URL for link (if empty it won't be a link)
138
* @param mixed $options Link attributes e.g. array('id'=>'selected')
140
* @see HtmlHelper::link() for details on $options that can be used.
143
function addCrumb($name, $link = null, $options = null) {
144
$this->_crumbs[] = array($name, $link, $options);
148
* Returns a doctype string.
152
* - html4-strict: HTML4 Strict.
153
* - html4-trans: HTML4 Transitional.
154
* - html4-frame: HTML4 Frameset.
155
* - xhtml-strict: XHTML1 Strict.
156
* - xhtml-trans: XHTML1 Transitional.
157
* - xhtml-frame: XHTML1 Frameset.
158
* - xhtml11: XHTML1.1.
160
* @param string $type Doctype to use.
161
* @return string Doctype string
163
* @link http://book.cakephp.org/view/1439/docType
165
function docType($type = 'xhtml-strict') {
166
if (isset($this->__docTypes[$type])) {
167
return $this->__docTypes[$type];
173
* Creates a link to an external resource and handles basic meta tags
177
* - `inline` Whether or not the link element should be output inline, or in scripts_for_layout.
179
* @param string $type The title of the external resource
180
* @param mixed $url The address of the external resource or string for content attribute
181
* @param array $options Other attributes for the generated tag. If the type attribute is html,
182
* rss, atom, or icon, the mime-type is returned.
183
* @return string A completed `<link />` element.
185
* @link http://book.cakephp.org/view/1438/meta
187
function meta($type, $url = null, $options = array()) {
188
$inline = isset($options['inline']) ? $options['inline'] : true;
189
unset($options['inline']);
191
if (!is_array($type)) {
193
'rss' => array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url),
194
'atom' => array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url),
195
'icon' => array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url),
196
'keywords' => array('name' => 'keywords', 'content' => $url),
197
'description' => array('name' => 'description', 'content' => $url),
200
if ($type === 'icon' && $url === null) {
201
$types['icon']['link'] = $this->webroot('favicon.ico');
204
if (isset($types[$type])) {
205
$type = $types[$type];
206
} elseif (!isset($options['type']) && $url !== null) {
207
if (is_array($url) && isset($url['ext'])) {
208
$type = $types[$url['ext']];
210
$type = $types['rss'];
212
} elseif (isset($options['type']) && isset($types[$options['type']])) {
213
$type = $types[$options['type']];
214
unset($options['type']);
218
} elseif ($url !== null) {
221
$options = array_merge($type, $options);
224
if (isset($options['link'])) {
225
if (isset($options['rel']) && $options['rel'] === 'icon') {
226
$out = sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
227
$options['rel'] = 'shortcut icon';
229
$options['link'] = $this->url($options['link'], true);
231
$out .= sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
233
$out = sprintf($this->tags['meta'], $this->_parseAttributes($options, array('type'), ' ', ' '));
239
$view =& ClassRegistry::getObject('view');
240
$view->addScript($out);
245
* Returns a charset META-tag.
247
* @param string $charset The character set to be used in the meta tag. If empty,
248
* The App.encoding value will be used. Example: "utf-8".
249
* @return string A meta tag containing the specified character set.
251
* @link http://book.cakephp.org/view/1436/charset
253
function charset($charset = null) {
254
if (empty($charset)) {
255
$charset = strtolower(Configure::read('App.encoding'));
257
return sprintf($this->tags['charset'], (!empty($charset) ? $charset : 'utf-8'));
261
* Creates an HTML link.
263
* If $url starts with "http://" this is treated as an external link. Else,
264
* it is treated as a path to controller/action and parsed with the
265
* HtmlHelper::url() method.
267
* If the $url is empty, $title is used instead.
271
* - `escape` Set to false to disable escaping of title and attributes.
273
* @param string $title The content to be wrapped by <a> tags.
274
* @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
275
* @param array $options Array of HTML attributes.
276
* @param string $confirmMessage JavaScript confirmation message.
277
* @return string An `<a />` element.
279
* @link http://book.cakephp.org/view/1442/link
281
function link($title, $url = null, $options = array(), $confirmMessage = false) {
284
$url = $this->url($url);
286
$url = $this->url($title);
288
$escapeTitle = false;
291
if (isset($options['escape'])) {
292
$escapeTitle = $options['escape'];
295
if ($escapeTitle === true) {
297
} elseif (is_string($escapeTitle)) {
298
$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
301
if (!empty($options['confirm'])) {
302
$confirmMessage = $options['confirm'];
303
unset($options['confirm']);
305
if ($confirmMessage) {
306
$confirmMessage = str_replace("'", "\'", $confirmMessage);
307
$confirmMessage = str_replace('"', '\"', $confirmMessage);
308
$options['onclick'] = "return confirm('{$confirmMessage}');";
309
} elseif (isset($options['default']) && $options['default'] == false) {
310
if (isset($options['onclick'])) {
311
$options['onclick'] .= ' event.returnValue = false; return false;';
313
$options['onclick'] = 'event.returnValue = false; return false;';
315
unset($options['default']);
317
return sprintf($this->tags['link'], $url, $this->_parseAttributes($options), $title);
321
* Creates a link element for CSS stylesheets.
325
* - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true
327
* @param mixed $path The name of a CSS style sheet or an array containing names of
328
* CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot
329
* of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css.
330
* @param string $rel Rel attribute. Defaults to "stylesheet". If equal to 'import' the stylesheet will be imported.
331
* @param array $options Array of HTML attributes.
332
* @return string CSS <link /> or <style /> tag, depending on the type of link.
334
* @link http://book.cakephp.org/view/1437/css
336
function css($path, $rel = null, $options = array()) {
337
$options += array('inline' => true);
338
if (is_array($path)) {
340
foreach ($path as $i) {
341
$out .= "\n\t" . $this->css($i, $rel, $options);
343
if ($options['inline']) {
349
if (strpos($path, '://') !== false) {
352
if ($path[0] !== '/') {
353
$path = CSS_URL . $path;
356
if (strpos($path, '?') === false) {
357
if (substr($path, -4) !== '.css') {
361
$url = $this->assetTimestamp($this->webroot($path));
363
if (Configure::read('Asset.filter.css')) {
364
$pos = strpos($url, CSS_URL);
365
if ($pos !== false) {
366
$url = substr($url, 0, $pos) . 'ccss/' . substr($url, $pos + strlen(CSS_URL));
371
if ($rel == 'import') {
372
$out = sprintf($this->tags['style'], $this->_parseAttributes($options, array('inline'), '', ' '), '@import url(' . $url . ');');
377
$out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($options, array('inline'), '', ' '));
380
if ($options['inline']) {
383
$view =& ClassRegistry::getObject('view');
384
$view->addScript($out);
389
* Returns one or many `<script>` tags depending on the number of scripts given.
391
* If the filename is prefixed with "/", the path will be relative to the base path of your
392
* application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js.
394
* Can include one or many Javascript files.
398
* - `inline` - Whether script should be output inline or into scripts_for_layout.
399
* - `once` - Whether or not the script should be checked for uniqueness. If true scripts will only be
400
* included once, use false to allow the same script to be included more than once per request.
402
* @param mixed $url String or array of javascript files to include
403
* @param mixed $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value
404
* @return mixed String of `<script />` tags or null if $inline is false or if $once is true and the file has been
407
* @link http://book.cakephp.org/view/1589/script
409
function script($url, $options = array()) {
410
if (is_bool($options)) {
411
list($inline, $options) = array($options, array());
412
$options['inline'] = $inline;
414
$options = array_merge(array('inline' => true, 'once' => true), $options);
415
if (is_array($url)) {
417
foreach ($url as $i) {
418
$out .= "\n\t" . $this->script($i, $options);
420
if ($options['inline']) {
425
if ($options['once'] && isset($this->__includedScripts[$url])) {
428
$this->__includedScripts[$url] = true;
430
if (strpos($url, '://') === false) {
431
if ($url[0] !== '/') {
432
$url = JS_URL . $url;
434
if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
437
$url = $this->assetTimestamp($this->webroot($url));
439
if (Configure::read('Asset.filter.js')) {
440
$url = str_replace(JS_URL, 'cjs/', $url);
443
$attributes = $this->_parseAttributes($options, array('inline', 'once'), ' ');
444
$out = sprintf($this->tags['javascriptlink'], $url, $attributes);
446
if ($options['inline']) {
449
$view =& ClassRegistry::getObject('view');
450
$view->addScript($out);
455
* Wrap $script in a script tag.
459
* - `safe` (boolean) Whether or not the $script should be wrapped in <![CDATA[ ]]>
460
* - `inline` (boolean) Whether or not the $script should be added to $scripts_for_layout or output inline
462
* @param string $script The script to wrap
463
* @param array $options The options to use.
464
* @return mixed string or null depending on the value of `$options['inline']`
466
* @link http://book.cakephp.org/view/1604/scriptBlock
468
function scriptBlock($script, $options = array()) {
469
$options += array('safe' => true, 'inline' => true);
470
if ($options['safe']) {
471
$script = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
473
$inline = $options['inline'];
474
unset($options['inline'], $options['safe']);
475
$attributes = $this->_parseAttributes($options, ' ', ' ');
477
return sprintf($this->tags['javascriptblock'], $attributes, $script);
479
$view =& ClassRegistry::getObject('view');
480
$view->addScript(sprintf($this->tags['javascriptblock'], $attributes, $script));
486
* Begin a script block that captures output until HtmlHelper::scriptEnd()
487
* is called. This capturing block will capture all output between the methods
488
* and create a scriptBlock from it.
492
* - `safe` Whether the code block should contain a CDATA
493
* - `inline` Should the generated script tag be output inline or in `$scripts_for_layout`
495
* @param array $options Options for the code block.
498
* @link http://book.cakephp.org/view/1605/scriptStart
500
function scriptStart($options = array()) {
501
$options += array('safe' => true, 'inline' => true);
502
$this->_scriptBlockOptions = $options;
508
* End a Buffered section of Javascript capturing.
509
* Generates a script tag inline or in `$scripts_for_layout` depending on the settings
510
* used when the scriptBlock was started
512
* @return mixed depending on the settings of scriptStart() either a script tag or null
514
* @link http://book.cakephp.org/view/1606/scriptEnd
516
function scriptEnd() {
517
$buffer = ob_get_clean();
518
$options = $this->_scriptBlockOptions;
519
$this->_scriptBlockOptions = array();
520
return $this->scriptBlock($buffer, $options);
524
* Builds CSS style data from an array of CSS properties
529
* echo $html->style(array('margin' => '10px', 'padding' => '10px'), true);
532
* 'margin:10px;padding:10px;'
535
* @param array $data Style data array, keys will be used as property names, values as property values.
536
* @param boolean $oneline Whether or not the style block should be displayed on one line.
537
* @return string CSS styling data
539
* @link http://book.cakephp.org/view/1440/style
541
function style($data, $oneline = true) {
542
if (!is_array($data)) {
546
foreach ($data as $key=> $value) {
547
$out[] = $key.':'.$value.';';
550
return join(' ', $out);
552
return implode("\n", $out);
556
* Returns the breadcrumb trail as a sequence of »-separated links.
558
* @param string $separator Text to separate crumbs.
559
* @param string $startText This will be the first crumb, if false it defaults to first crumb in array
560
* @return string Composed bread crumbs
563
function getCrumbs($separator = '»', $startText = false) {
564
if (!empty($this->_crumbs)) {
567
$out[] = $this->link($startText, '/');
570
foreach ($this->_crumbs as $crumb) {
571
if (!empty($crumb[1])) {
572
$out[] = $this->link($crumb[0], $crumb[1], $crumb[2]);
577
return join($separator, $out);
584
* Creates a formatted IMG element. If `$options['url']` is provided, an image link will be
585
* generated with the link pointed at `$options['url']`. This method will set an empty
586
* alt attribute if one is not supplied.
590
* Create a regular image:
592
* `echo $html->image('cake_icon.png', array('alt' => 'CakePHP'));`
594
* Create an image link:
596
* `echo $html->image('cake_icon.png', array('alt' => 'CakePHP', 'url' => 'http://cakephp.org'));`
598
* @param string $path Path to the image file, relative to the app/webroot/img/ directory.
599
* @param array $options Array of HTML attributes.
600
* @return string completed img tag
602
* @link http://book.cakephp.org/view/1441/image
604
function image($path, $options = array()) {
605
if (is_array($path)) {
606
$path = $this->url($path);
607
} elseif (strpos($path, '://') === false) {
608
if ($path[0] !== '/') {
609
$path = IMAGES_URL . $path;
611
$path = $this->assetTimestamp($this->webroot($path));
614
if (!isset($options['alt'])) {
615
$options['alt'] = '';
619
if (!empty($options['url'])) {
620
$url = $options['url'];
621
unset($options['url']);
624
$image = sprintf($this->tags['image'], $path, $this->_parseAttributes($options, null, '', ' '));
627
return sprintf($this->tags['link'], $this->url($url), null, $image);
633
* Returns a row of formatted and named TABLE headers.
635
* @param array $names Array of tablenames.
636
* @param array $trOptions HTML options for TR elements.
637
* @param array $thOptions HTML options for TH elements.
638
* @return string Completed table headers
640
* @link http://book.cakephp.org/view/1446/tableHeaders
642
function tableHeaders($names, $trOptions = null, $thOptions = null) {
644
foreach ($names as $arg) {
645
$out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg);
647
return sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out));
651
* Returns a formatted string of table rows (TR's with TD's in them).
653
* @param array $data Array of table data
654
* @param array $oddTrOptions HTML options for odd TR elements if true useCount is used
655
* @param array $evenTrOptions HTML options for even TR elements
656
* @param bool $useCount adds class "column-$i"
657
* @param bool $continueOddEven If false, will use a non-static $count variable,
658
* so that the odd/even count is reset to zero just for that call.
659
* @return string Formatted HTML
661
* @link http://book.cakephp.org/view/1447/tableCells
663
function tableCells($data, $oddTrOptions = null, $evenTrOptions = null, $useCount = false, $continueOddEven = true) {
664
if (empty($data[0]) || !is_array($data[0])) {
665
$data = array($data);
668
if ($oddTrOptions === true) {
670
$oddTrOptions = null;
673
if ($evenTrOptions === false) {
674
$continueOddEven = false;
675
$evenTrOptions = null;
678
if ($continueOddEven) {
684
foreach ($data as $line) {
688
foreach ($line as $cell) {
689
$cellOptions = array();
691
if (is_array($cell)) {
692
$cellOptions = $cell[1];
694
} elseif ($useCount) {
695
$cellOptions['class'] = 'column-' . ++$i;
697
$cellsOut[] = sprintf($this->tags['tablecell'], $this->_parseAttributes($cellOptions), $cell);
699
$options = $this->_parseAttributes($count % 2 ? $oddTrOptions : $evenTrOptions);
700
$out[] = sprintf($this->tags['tablerow'], $options, implode(' ', $cellsOut));
702
return implode("\n", $out);
706
* Returns a formatted block tag, i.e DIV, SPAN, P.
710
* - `escape` Whether or not the contents should be html_entity escaped.
712
* @param string $name Tag name.
713
* @param string $text String content that will appear inside the div element.
714
* If null, only a start tag will be printed
715
* @param array $options Additional HTML attributes of the DIV tag, see above.
716
* @return string The formatted tag element
718
* @link http://book.cakephp.org/view/1443/tag
720
function tag($name, $text = null, $options = array()) {
721
if (is_array($options) && isset($options['escape']) && $options['escape']) {
723
unset($options['escape']);
725
if (!is_array($options)) {
726
$options = array('class' => $options);
728
if ($text === null) {
733
return sprintf($this->tags[$tag], $name, $this->_parseAttributes($options, null, ' ', ''), $text, $name);
737
* Returns a formatted DIV tag for HTML FORMs.
741
* - `escape` Whether or not the contents should be html_entity escaped.
743
* @param string $class CSS class name of the div element.
744
* @param string $text String content that will appear inside the div element.
745
* If null, only a start tag will be printed
746
* @param array $options Additional HTML attributes of the DIV tag
747
* @return string The formatted DIV element
749
* @link http://book.cakephp.org/view/1444/div
751
function div($class = null, $text = null, $options = array()) {
752
if (!empty($class)) {
753
$options['class'] = $class;
755
return $this->tag('div', $text, $options);
759
* Returns a formatted P tag.
763
* - `escape` Whether or not the contents should be html_entity escaped.
765
* @param string $class CSS class name of the p element.
766
* @param string $text String content that will appear inside the p element.
767
* @param array $options Additional HTML attributes of the P tag
768
* @return string The formatted P element
770
* @link http://book.cakephp.org/view/1445/para
772
function para($class, $text, $options = array()) {
773
if (isset($options['escape'])) {
776
if ($class != null && !empty($class)) {
777
$options['class'] = $class;
779
if ($text === null) {
784
return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $text);
788
* Build a nested list (UL/OL) out of an associative array.
790
* @param array $list Set of elements to list
791
* @param array $options Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag
792
* @param array $itemOptions Additional HTML attributes of the list item (LI) tag
793
* @param string $tag Type of list tag to use (ol/ul)
794
* @return string The nested list
797
function nestedList($list, $options = array(), $itemOptions = array(), $tag = 'ul') {
798
if (is_string($options)) {
802
$items = $this->__nestedListItem($list, $options, $itemOptions, $tag);
803
return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $items);
807
* Internal function to build a nested list (UL/OL) out of an associative array.
809
* @param array $list Set of elements to list
810
* @param array $options Additional HTML attributes of the list (ol/ul) tag
811
* @param array $itemOptions Additional HTML attributes of the list item (LI) tag
812
* @param string $tag Type of list tag to use (ol/ul)
813
* @return string The nested list element
815
* @see HtmlHelper::nestedList()
817
function __nestedListItem($items, $options, $itemOptions, $tag) {
821
foreach ($items as $key => $item) {
822
if (is_array($item)) {
823
$item = $key . $this->nestedList($item, $options, $itemOptions, $tag);
825
if (isset($itemOptions['even']) && $index % 2 == 0) {
826
$itemOptions['class'] = $itemOptions['even'];
827
} else if (isset($itemOptions['odd']) && $index % 2 != 0) {
828
$itemOptions['class'] = $itemOptions['odd'];
830
$out .= sprintf($this->tags['li'], $this->_parseAttributes($itemOptions, array('even', 'odd'), ' ', ''), $item);