Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: html.php 8004 2009-01-16 20:15:21Z gwoo $ */
3
/**
4
 * Html Helper class file.
5
 *
6
 * Simplifies the construction of HTML elements.
7
 *
8
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
9
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
10
 *
11
 * Licensed under The MIT License
12
 * Redistributions of files must retain the above copyright notice.
13
 *
14
 * @filesource
15
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
16
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
17
 * @package       cake
18
 * @subpackage    cake.cake.libs.view.helpers
19
 * @since         CakePHP(tm) v 0.9.1
20
 * @version       $Revision: 8004 $
21
 * @modifiedby    $LastChangedBy: gwoo $
22
 * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
23
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
24
 */
25
/**
26
 * Html Helper class for easy use of HTML widgets.
27
 *
28
 * HtmlHelper encloses all methods needed while working with HTML pages.
29
 *
30
 * @package       cake
31
 * @subpackage    cake.cake.libs.view.helpers
32
 */
33
class HtmlHelper extends AppHelper {
34
/*************************************************************************
35
 * Public variables
36
 *************************************************************************/
37
/**#@+
38
 * @access public
39
 */
40
/**
41
 * html tags used by this helper.
42
 *
43
 * @var array
44
 */
45
	var $tags = array(
46
		'meta' => '<meta%s/>',
47
		'metalink' => '<link href="%s"%s/>',
48
		'link' => '<a href="%s"%s>%s</a>',
49
		'mailto' => '<a href="mailto:%s" %s>%s</a>',
50
		'form' => '<form %s>',
51
		'formend' => '</form>',
52
		'input' => '<input name="%s" %s/>',
53
		'textarea' => '<textarea name="%s" %s>%s</textarea>',
54
		'hidden' => '<input type="hidden" name="%s" %s/>',
55
		'checkbox' => '<input type="checkbox" name="%s" %s/>',
56
		'checkboxmultiple' => '<input type="checkbox" name="%s[]"%s />',
57
		'radio' => '<input type="radio" name="%s" id="%s" %s />%s',
58
		'selectstart' => '<select name="%s"%s>',
59
		'selectmultiplestart' => '<select name="%s[]"%s>',
60
		'selectempty' => '<option value=""%s>&nbsp;</option>',
61
		'selectoption' => '<option value="%s"%s>%s</option>',
62
		'selectend' => '</select>',
63
		'optiongroup' => '<optgroup label="%s"%s>',
64
		'optiongroupend' => '</optgroup>',
65
		'checkboxmultiplestart' => '',
66
		'checkboxmultipleend' => '',
67
		'password' => '<input type="password" name="%s" %s/>',
68
		'file' => '<input type="file" name="%s" %s/>',
69
		'file_no_model' => '<input type="file" name="%s" %s/>',
70
		'submit' => '<input type="submit" %s/>',
71
		'submitimage' => '<input type="image" src="%s" %s/>',
72
		'button' => '<input type="%s" %s/>',
73
		'image' => '<img src="%s" %s/>',
74
		'tableheader' => '<th%s>%s</th>',
75
		'tableheaderrow' => '<tr%s>%s</tr>',
76
		'tablecell' => '<td%s>%s</td>',
77
		'tablerow' => '<tr%s>%s</tr>',
78
		'block' => '<div%s>%s</div>',
79
		'blockstart' => '<div%s>',
80
		'blockend' => '</div>',
81
		'tag' => '<%s%s>%s</%s>',
82
		'tagstart' => '<%s%s>',
83
		'tagend' => '</%s>',
84
		'para' => '<p%s>%s</p>',
85
		'parastart' => '<p%s>',
86
		'label' => '<label for="%s"%s>%s</label>',
87
		'fieldset' => '<fieldset%s>%s</fieldset>',
88
		'fieldsetstart' => '<fieldset><legend>%s</legend>',
89
		'fieldsetend' => '</fieldset>',
90
		'legend' => '<legend>%s</legend>',
91
		'css' => '<link rel="%s" type="text/css" href="%s" %s/>',
92
		'style' => '<style type="text/css"%s>%s</style>',
93
		'charset' => '<meta http-equiv="Content-Type" content="text/html; charset=%s" />',
94
		'ul' => '<ul%s>%s</ul>',
95
		'ol' => '<ol%s>%s</ol>',
96
		'li' => '<li%s>%s</li>',
97
		'error' => '<div%s>%s</div>'
98
	);
99
/**
100
 * Base URL
101
 *
102
 * @var string
103
 */
104
	var $base = null;
105
/**
106
 * URL to current action.
107
 *
108
 * @var string
109
 */
110
	var $here = null;
111
/**
112
 * Parameter array.
113
 *
114
 * @var array
115
 */
116
	var $params = array();
117
/**
118
 * Current action.
119
 *
120
 * @var string
121
 */
122
	var $action = null;
123
/**
124
 * Enter description here...
125
 *
126
 * @var array
127
 */
128
	var $data = null;
129
/**#@-*/
130
/*************************************************************************
131
 * Private variables
132
 *************************************************************************/
133
/**#@+
134
 * @access private
135
 */
136
/**
137
 * Breadcrumbs.
138
 *
139
 * @var	array
140
 * @access private
141
 */
142
	var $_crumbs = array();
143
/**
144
 * Document type definitions
145
 *
146
 * @var	array
147
 * @access private
148
 */
149
	var $__docTypes = array(
150
		'html4-strict'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
151
		'html4-trans'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
152
		'html4-frame'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
153
		'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
154
		'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
155
		'xhtml-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
156
		'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
157
	);
158
/**
159
 * Adds a link to the breadcrumbs array.
160
 *
161
 * @param string $name Text for link
162
 * @param string $link URL for link (if empty it won't be a link)
163
 * @param mixed $options Link attributes e.g. array('id'=>'selected')
164
 */
165
	function addCrumb($name, $link = null, $options = null) {
166
		$this->_crumbs[] = array($name, $link, $options);
167
	}
168
/**
169
 * Returns a doctype string.
170
 *
171
 * Possible doctypes:
172
 *   + html4-strict:  HTML4 Strict.
173
 *   + html4-trans:  HTML4 Transitional.
174
 *   + html4-frame:  HTML4 Frameset.
175
 *   + xhtml-strict: XHTML1 Strict.
176
 *   + xhtml-trans: XHTML1 Transitional.
177
 *   + xhtml-frame: XHTML1 Frameset.
178
 *   + xhtml11: XHTML1.1.
179
 *
180
 * @param  string $type Doctype to use.
181
 * @return string Doctype.
182
 */
183
	function docType($type = 'xhtml-strict') {
184
		if (isset($this->__docTypes[$type])) {
185
			return $this->output($this->__docTypes[$type]);
186
		}
187
		return null;
188
	}
189
/**
190
 * Creates a link to an external resource and handles basic meta tags
191
 *
192
 * @param  string  $title The title of the external resource
193
 * @param  mixed   $url   The address of the external resource or string for content attribute
194
 * @param  array   $attributes Other attributes for the generated tag. If the type attribute is html, rss, atom, or icon, the mime-type is returned.
195
 * @param  boolean $inline If set to false, the generated tag appears in the head tag of the layout.
196
 * @return string
197
 */
198
	function meta($type, $url = null, $attributes = array(), $inline = true) {
199
		if (!is_array($type)) {
200
			$types = array(
201
				'rss'	=> array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url),
202
				'atom'	=> array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url),
203
				'icon'	=> array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url),
204
				'keywords' => array('name' => 'keywords', 'content' => $url),
205
				'description' => array('name' => 'description', 'content' => $url),
206
			);
207
 
208
			if ($type === 'icon' && $url === null) {
209
				$types['icon']['link'] = $this->webroot('favicon.ico');
210
			}
211
 
212
			if (isset($types[$type])) {
213
				$type = $types[$type];
214
			} elseif (!isset($attributes['type']) && $url !== null) {
215
				if (is_array($url) && isset($url['ext'])) {
216
					$type = $types[$url['ext']];
217
				} else {
218
					$type = $types['rss'];
219
				}
220
			} elseif (isset($attributes['type']) && isset($types[$attributes['type']])) {
221
				$type = $types[$attributes['type']];
222
				unset($attributes['type']);
223
			} else {
224
				$type = array();
225
			}
226
		} elseif ($url !== null) {
227
			$inline = $url;
228
		}
229
		$attributes = array_merge($type, $attributes);
230
		$out = null;
231
 
232
		if (isset($attributes['link'])) {
233
			if (isset($attributes['rel']) && $attributes['rel'] === 'icon') {
234
				$out = sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' '));
235
				$attributes['rel'] = 'shortcut icon';
236
			} else {
237
				$attributes['link'] = $this->url($attributes['link'], true);
238
			}
239
			$out .= sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' '));
240
		} else {
241
			$out = sprintf($this->tags['meta'], $this->_parseAttributes($attributes, array('type')));
242
		}
243
 
244
		if ($inline) {
245
			return $this->output($out);
246
		} else {
247
			$view =& ClassRegistry::getObject('view');
248
			$view->addScript($out);
249
		}
250
	}
251
/**
252
 * Returns a charset META-tag.
253
 *
254
 * @param  string  $charset The character set to be used in the meta tag. Example: "utf-8".
255
 * @return string A meta tag containing the specified character set.
256
 */
257
	function charset($charset = null) {
258
		if (empty($charset)) {
259
			$charset = strtolower(Configure::read('App.encoding'));
260
		}
261
		return $this->output(sprintf($this->tags['charset'], (!empty($charset) ? $charset : 'utf-8')));
262
	}
263
/**
264
 * Creates an HTML link.
265
 *
266
 * If $url starts with "http://" this is treated as an external link. Else,
267
 * it is treated as a path to controller/action and parsed with the
268
 * HtmlHelper::url() method.
269
 *
270
 * If the $url is empty, $title is used instead.
271
 *
272
 * @param  string  $title The content to be wrapped by <a> tags.
273
 * @param  mixed   $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
274
 * @param  array   $htmlAttributes Array of HTML attributes.
275
 * @param  string  $confirmMessage JavaScript confirmation message.
276
 * @param  boolean $escapeTitle	Whether or not $title should be HTML escaped.
277
 * @return string	An <a /> element.
278
 */
279
	function link($title, $url = null, $htmlAttributes = array(), $confirmMessage = false, $escapeTitle = true) {
280
		if ($url !== null) {
281
			$url = $this->url($url);
282
		} else {
283
			$url = $this->url($title);
284
			$title = $url;
285
			$escapeTitle = false;
286
		}
287
 
288
		if (isset($htmlAttributes['escape'])) {
289
			$escapeTitle = $htmlAttributes['escape'];
290
			unset($htmlAttributes['escape']);
291
		}
292
 
293
		if ($escapeTitle === true) {
294
			$title = h($title);
295
		} elseif (is_string($escapeTitle)) {
296
			$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
297
		}
298
 
299
		if (!empty($htmlAttributes['confirm'])) {
300
			$confirmMessage = $htmlAttributes['confirm'];
301
			unset($htmlAttributes['confirm']);
302
		}
303
		if ($confirmMessage) {
304
			$confirmMessage = str_replace("'", "\'", $confirmMessage);
305
			$confirmMessage = str_replace('"', '\"', $confirmMessage);
306
			$htmlAttributes['onclick'] = "return confirm('{$confirmMessage}');";
307
		} elseif (isset($htmlAttributes['default']) && $htmlAttributes['default'] == false) {
308
			if (isset($htmlAttributes['onclick'])) {
309
				$htmlAttributes['onclick'] .= ' event.returnValue = false; return false;';
310
			} else {
311
				$htmlAttributes['onclick'] = 'event.returnValue = false; return false;';
312
			}
313
			unset($htmlAttributes['default']);
314
		}
315
		return $this->output(sprintf($this->tags['link'], $url, $this->_parseAttributes($htmlAttributes), $title));
316
	}
317
/**
318
 * Creates a link element for CSS stylesheets.
319
 *
320
 * @param mixed $path The name of a CSS style sheet in /app/webroot/css, or an array containing names of CSS stylesheets in that directory.
321
 * @param string $rel Rel attribute. Defaults to "stylesheet".
322
 * @param array $htmlAttributes Array of HTML attributes.
323
 * @param boolean $inline If set to false, the generated tag appears in the head tag of the layout.
324
 * @return string CSS <link /> or <style /> tag, depending on the type of link.
325
 */
326
	function css($path, $rel = null, $htmlAttributes = array(), $inline = true) {
327
		if (is_array($path)) {
328
			$out = '';
329
			foreach ($path as $i) {
330
				$out .= "\n\t" . $this->css($i, $rel, $htmlAttributes, $inline);
331
			}
332
			if ($inline)  {
333
				return $out . "\n";
334
			}
335
			return;
336
		}
337
 
338
		if (strpos($path, '://') !== false) {
339
			$url = $path;
340
		} else {
341
			if ($path[0] !== '/') {
342
				$path = CSS_URL . $path;
343
			}
344
 
345
			if (strpos($path, '?') === false) {
346
				if (strpos($path, '.css') === false) {
347
					$path .= '.css';
348
				}
349
			}
350
 
351
			$path = $this->webroot($path);
352
 
353
			$url = $path;
354
			if (strpos($path, '?') === false && ((Configure::read('Asset.timestamp') === true && Configure::read() > 0) || Configure::read('Asset.timestamp') === 'force')) {
355
				$url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $path));
356
			}
357
 
358
			if (Configure::read('Asset.filter.css')) {
359
				$url = str_replace(CSS_URL, 'ccss/', $url);
360
			}
361
		}
362
 
363
		if ($rel == 'import') {
364
			$out = sprintf($this->tags['style'], $this->_parseAttributes($htmlAttributes, null, '', ' '), '@import url(' . $url . ');');
365
		} else {
366
			if ($rel == null) {
367
				$rel = 'stylesheet';
368
			}
369
			$out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($htmlAttributes, null, '', ' '));
370
		}
371
		$out = $this->output($out);
372
 
373
		if ($inline) {
374
			return $out;
375
		} else {
376
			$view =& ClassRegistry::getObject('view');
377
			$view->addScript($out);
378
		}
379
	}
380
/**
381
 * Builds CSS style data from an array of CSS properties
382
 *
383
 * @param array $data
384
 * @return string CSS styling data
385
 */
386
	function style($data, $inline = true) {
387
		if (!is_array($data)) {
388
			return $data;
389
		}
390
		$out = array();
391
		foreach ($data as $key=> $value) {
392
			$out[] = $key.':'.$value.';';
393
		}
394
		if ($inline) {
395
			return join(' ', $out);
396
		}
397
		return join("\n", $out);
398
	}
399
/**
400
 * Returns the breadcrumb trail as a sequence of &raquo;-separated links.
401
 *
402
 * @param  string  $separator Text to separate crumbs.
403
 * @param  string  $startText This will be the first crumb, if false it defaults to first crumb in array
404
 * @return string
405
 */
406
	function getCrumbs($separator = '&raquo;', $startText = false) {
407
		if (count($this->_crumbs)) {
408
			$out = array();
409
			if ($startText) {
410
				$out[] = $this->link($startText, '/');
411
			}
412
 
413
			foreach ($this->_crumbs as $crumb) {
414
				if (!empty($crumb[1])) {
415
					$out[] = $this->link($crumb[0], $crumb[1], $crumb[2]);
416
				} else {
417
					$out[] = $crumb[0];
418
				}
419
			}
420
			return $this->output(join($separator, $out));
421
		} else {
422
			return null;
423
		}
424
	}
425
/**
426
 * Creates a formatted IMG element.
427
 *
428
 * @param string $path Path to the image file, relative to the app/webroot/img/ directory.
429
 * @param array	$htmlAttributes Array of HTML attributes.
430
 * @return string
431
 */
432
	function image($path, $options = array()) {
433
		if (is_array($path)) {
434
			$path = $this->url($path);
435
		} elseif ($path[0] === '/') {
436
			$path = $this->webroot($path);
437
		} elseif (strpos($path, '://') === false) {
438
			if (Configure::read('Asset.timestamp') == true && Configure::read() > 0) {
439
				$path .= '?' . @filemtime(str_replace('/', DS, WWW_ROOT . IMAGES_URL . $path));
440
			}
441
			$path = $this->webroot(IMAGES_URL . $path);
442
		}
443
 
444
		if (!isset($options['alt'])) {
445
			$options['alt'] = '';
446
		}
447
 
448
		$url = false;
449
		if (!empty($options['url'])) {
450
			$url = $options['url'];
451
			unset($options['url']);
452
		}
453
 
454
		$image = sprintf($this->tags['image'], $path, $this->_parseAttributes($options, null, '', ' '));
455
 
456
		if ($url) {
457
			return $this->output(sprintf($this->tags['link'], $this->url($url), null, $image));
458
		}
459
 
460
		return $this->output($image);
461
	}
462
/**
463
 * Returns a row of formatted and named TABLE headers.
464
 *
465
 * @param array $names		Array of tablenames.
466
 * @param array $trOptions	HTML options for TR elements.
467
 * @param array $thOptions	HTML options for TH elements.
468
 * @return string
469
 */
470
	function tableHeaders($names, $trOptions = null, $thOptions = null) {
471
		$out = array();
472
		foreach ($names as $arg) {
473
			$out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg);
474
		}
475
		$data = sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out));
476
		return $this->output($data);
477
	}
478
/**
479
 * Returns a formatted string of table rows (TR's with TD's in them).
480
 *
481
 * @param array $data		Array of table data
482
 * @param array $oddTrOptions HTML options for odd TR elements if true useCount is used
483
 * @param array $evenTrOptions HTML options for even TR elements
484
 * @param bool $useCount adds class "column-$i"
485
 * @param bool $continueOddEven If false, will use a non-static $count variable, so that the odd/even count is reset to zero just for that call
486
 * @return string	Formatted HTML
487
 */
488
	function tableCells($data, $oddTrOptions = null, $evenTrOptions = null, $useCount = false, $continueOddEven = true) {
489
		if (empty($data[0]) || !is_array($data[0])) {
490
			$data = array($data);
491
		}
492
 
493
		if ($oddTrOptions === true) {
494
			$useCount = true;
495
			$oddTrOptions = null;
496
		}
497
 
498
		if ($evenTrOptions === false) {
499
			$continueOddEven = false;
500
			$evenTrOptions = null;
501
		}
502
 
503
		if ($continueOddEven) {
504
			static $count = 0;
505
		} else {
506
			$count = 0;
507
		}
508
 
509
		foreach ($data as $line) {
510
			$count++;
511
			$cellsOut = array();
512
			$i = 0;
513
			foreach ($line as $cell) {
514
				$cellOptions = array();
515
 
516
				if (is_array($cell)) {
517
					$cellOptions = $cell[1];
518
					$cell = $cell[0];
519
				} elseif ($useCount) {
520
					$cellOptions['class'] = 'column-' . ++$i;
521
				}
522
				$cellsOut[] = sprintf($this->tags['tablecell'], $this->_parseAttributes($cellOptions), $cell);
523
			}
524
			$options = $this->_parseAttributes($count % 2 ? $oddTrOptions : $evenTrOptions);
525
			$out[] = sprintf($this->tags['tablerow'], $options, join(' ', $cellsOut));
526
		}
527
		return $this->output(join("\n", $out));
528
	}
529
/**
530
 * Returns a formatted block tag, i.e DIV, SPAN, P.
531
 *
532
 * @param string $name Tag name.
533
 * @param string $text String content that will appear inside the div element.
534
 *			If null, only a start tag will be printed
535
 * @param array $attributes Additional HTML attributes of the DIV tag
536
 * @param boolean $escape If true, $text will be HTML-escaped
537
 * @return string The formatted tag element
538
 */
539
	function tag($name, $text = null, $attributes = array(), $escape = false) {
540
		if ($escape) {
541
			$text = h($text);
542
		}
543
		if (!is_array($attributes)) {
544
			$attributes = array('class' => $attributes);
545
		}
546
		if ($text === null) {
547
			$tag = 'tagstart';
548
		} else {
549
			$tag = 'tag';
550
		}
551
		return $this->output(sprintf($this->tags[$tag], $name, $this->_parseAttributes($attributes, null, ' ', ''), $text, $name));
552
	}
553
/**
554
 * Returns a formatted DIV tag for HTML FORMs.
555
 *
556
 * @param string $class CSS class name of the div element.
557
 * @param string $text String content that will appear inside the div element.
558
 *			If null, only a start tag will be printed
559
 * @param array $attributes Additional HTML attributes of the DIV tag
560
 * @param boolean $escape If true, $text will be HTML-escaped
561
 * @return string The formatted DIV element
562
 */
563
	function div($class = null, $text = null, $attributes = array(), $escape = false) {
564
		if ($class != null && !empty($class)) {
565
			$attributes['class'] = $class;
566
		}
567
		return $this->tag('div', $text, $attributes, $escape);
568
	}
569
/**
570
 * Returns a formatted P tag.
571
 *
572
 * @param string $class CSS class name of the p element.
573
 * @param string $text String content that will appear inside the p element.
574
 * @param array $attributes Additional HTML attributes of the P tag
575
 * @param boolean $escape If true, $text will be HTML-escaped
576
 * @return string The formatted P element
577
 */
578
	function para($class, $text, $attributes = array(), $escape = false) {
579
		if ($escape) {
580
			$text = h($text);
581
		}
582
		if ($class != null && !empty($class)) {
583
			$attributes['class'] = $class;
584
		}
585
		if ($text === null) {
586
			$tag = 'parastart';
587
		} else {
588
			$tag = 'para';
589
		}
590
		return $this->output(sprintf($this->tags[$tag], $this->_parseAttributes($attributes, null, ' ', ''), $text));
591
	}
592
/**
593
 * Build a nested list (UL/OL) out of an associative array.
594
 *
595
 * @param array $list Set of elements to list
596
 * @param array $attributes Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag
597
 * @param array $itemAttributes Additional HTML attributes of the list item (LI) tag
598
 * @param string $tag Type of list tag to use (ol/ul)
599
 * @return string The nested list
600
 * @access public
601
 */
602
	function nestedList($list, $attributes = array(), $itemAttributes = array(), $tag = 'ul') {
603
		if (is_string($attributes)) {
604
			$tag = $attributes;
605
			$attributes = array();
606
		}
607
		$items = $this->__nestedListItem($list, $attributes, $itemAttributes, $tag);
608
		return sprintf($this->tags[$tag], $this->_parseAttributes($attributes, null, ' ', ''), $items);
609
	}
610
/**
611
 * Internal function to build a nested list (UL/OL) out of an associative array.
612
 *
613
 * @param array $list Set of elements to list
614
 * @param array $attributes Additional HTML attributes of the list (ol/ul) tag
615
 * @param array $itemAttributes Additional HTML attributes of the list item (LI) tag
616
 * @param string $tag Type of list tag to use (ol/ul)
617
 * @return string The nested list element
618
 * @access private
619
 * @see nestedList()
620
 */
621
	function __nestedListItem($items, $attributes, $itemAttributes, $tag) {
622
		$out = '';
623
 
624
		$index = 1;
625
		foreach ($items as $key => $item) {
626
			if (is_array($item)) {
627
				$item = $key . $this->nestedList($item, $attributes, $itemAttributes, $tag);
628
			}
629
			if (isset($itemAttributes['even']) && $index % 2 == 0) {
630
				$itemAttributes['class'] = $itemAttributes['even'];
631
			} else if (isset($itemAttributes['odd']) && $index % 2 != 0) {
632
				$itemAttributes['class'] = $itemAttributes['odd'];
633
			}
634
			$out .= sprintf($this->tags['li'], $this->_parseAttributes(array_diff_key($itemAttributes, array_flip(array('even', 'odd'))), null, ' ', ''), $item);
635
			$index++;
636
		}
637
		return $out;
638
	}
639
}
640
?>