Overview

Packages

  • Tree

Classes

  • Horde_Tree
  • Horde_Tree_Base
  • Horde_Tree_Exception
  • Horde_Tree_Html
  • Horde_Tree_Jquerymobile
  • Horde_Tree_Select
  • Horde_Tree_Simplehtml
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Tree_Html:: class provides HTML specific rendering functions.
  4:  *
  5:  * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author   Marko Djukic <marko@oblo.com>
 11:  * @category Horde
 12:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 13:  * @package  Tree
 14:  */
 15: class Horde_Tree_Html extends Horde_Tree_Base
 16: {
 17:     /**
 18:      * Node position list.
 19:      *
 20:      * @var array
 21:      */
 22:     protected $_node_pos = array();
 23: 
 24:     /**
 25:      * Drop line cache.
 26:      *
 27:      * @var array
 28:      */
 29:     protected $_dropline = array();
 30: 
 31:     /**
 32:      * Current value of the alt tag count.
 33:      *
 34:      * @var integer
 35:      */
 36:     protected $_altCount = 0;
 37: 
 38:     /**
 39:      * Allowed parameters for nodes.
 40:      *
 41:      * @var array
 42:      */
 43:     protected $_allowed = array(
 44:         'class',
 45:         'icon',
 46:         'iconalt',
 47:         'iconopen',
 48:         'url',
 49:         'urlclass',
 50:         'title',
 51:         'target'
 52:     );
 53: 
 54:     /**
 55:      * Images array.
 56:      *
 57:      * @var array
 58:      */
 59:     protected $_images = array(
 60:         'line' => null,
 61:         'blank' => null,
 62:         'join' => null,
 63:         'join_bottom' => null,
 64:         'join_top' => null,
 65:         'plus' => null,
 66:         'plus_bottom' => null,
 67:         'plus_only' => null,
 68:         'minus' => null,
 69:         'minus_bottom' => null,
 70:         'minus_only' => null,
 71:         'null_only' => null,
 72:         'folder' => null,
 73:         'folderopen' => null,
 74:         'leaf' => null
 75:     );
 76: 
 77:     /**
 78:      * Constructor.
 79:      *
 80:      * @param string $name   The name of this tree instance.
 81:      * @param array $params  Additional parameters:
 82:      * <pre>
 83:      * alternate - (boolean) Alternate shading in the table?
 84:      *             DEFAULT: false
 85:      * class - (string) The class to use for the table.
 86:      *         DEFAULT: ''
 87:      * hideHeaders - (boolean) Don't render any HTML for the header row, just
 88:      *               use the widths.
 89:      *               DEFAULT: false
 90:      * lines - (boolean) Show tree lines?
 91:      *         DEFAULT: true
 92:      * lines_base - (boolean) Show tree lines for the base level? Requires
 93:      *              'lines' to be true also.
 94:      *              DEFAULT: false
 95:      * multiline - (boolean) Do the node labels contain linebreaks?
 96:      *             DEFAULT: false
 97:      * </pre>
 98:      */
 99:     public function __construct($name, array $params = array())
100:     {
101:         $params = array_merge(array(
102:             'lines' => true
103:         ), $params);
104: 
105:         parent::__construct($name, $params);
106:     }
107: 
108:     /**
109:      * Returns the tree.
110:      *
111:      * @param boolean $static  If true the tree nodes can't be expanded and
112:      *                         collapsed and the tree gets rendered expanded.
113:      *
114:      * @return string  The HTML code of the rendered tree.
115:      */
116:     public function getTree($static = false)
117:     {
118:         $this->_static = (bool)$static;
119:         $this->_buildIndents($this->_root_nodes);
120: 
121:         $tree = $this->_buildHeader();
122:         foreach ($this->_root_nodes as $node_id) {
123:             $tree .= $this->_buildTree($node_id);
124:         }
125:         return $tree;
126:     }
127: 
128:     /**
129:      * Adds additional parameters to a node.
130:      *
131:      * @param string $id     The unique node id.
132:      * @param array $params  Parameters to set (key/value pairs).
133:      * <pre>
134:      * class - CSS class to use with this node
135:      * icon - Icon to display next node
136:      * iconalt - Alt text to use for the icon
137:      * iconopen - Icon to indicate this node as expanded
138:      * url - URL to link the node to
139:      * urlclass - CSS class for the node's URL
140:      * target - Target for the 'url' link
141:      * title - Link tooltip title
142:      * </pre>
143:      */
144:     public function addNodeParams($id, $params = array())
145:     {
146:         parent::addNodeParams($id, $params);
147:     }
148: 
149:     /**
150:      * Adds column headers to the tree table.
151:      *
152:      * @param array $header  An array containing hashes with header
153:      *                       information. The following keys are allowed:
154:      * <pre>
155:      * class - The CSS class of the header cell
156:      * html - The HTML content of the header cell
157:      * </pre>
158:      */
159:     public function setHeader($header)
160:     {
161:         parent::setHeader($header);
162:     }
163: 
164:     /**
165:      * Returns the HTML code for a header row, if necessary.
166:      *
167:      * @return string  The HTML code of the header row or an empty string.
168:      */
169:     protected function _buildHeader()
170:     {
171:         if (!count($this->_header) ||
172:             $this->getOption('hideHeaders')) {
173:             return '';
174:         }
175: 
176:         $className = 'treeRowHeader';
177: 
178:         /* If using alternating row shading, work out correct
179:          * shade. */
180:         if ($this->getOption('alternate')) {
181:             $className .= ' item' . $this->_altCount;
182:             $this->_altCount = 1 - $this->_altCount;
183:         }
184: 
185:         $html = '<div class="' . $className . '">';
186: 
187:         foreach ($this->_header as $header) {
188:             $html .= '<span';
189:             if (!empty($header['class'])) {
190:                 $html .= ' class="' . $header['class'] . '"';
191:             }
192: 
193:             $html .= '>' .
194:                 (empty($header['html']) ? '&nbsp;' : $header['html'])
195:                 . '</span>';
196:         }
197: 
198:         return $html . '</div>';
199:     }
200: 
201:     /**
202:      * Recursive function to walk through the tree array and build the output.
203:      *
204:      * @param string $node_id  The Node ID.
205:      *
206:      * @return string  The tree rendering.
207:      */
208:     protected function _buildTree($node_id)
209:     {
210:         $node = $this->_nodes[$node_id];
211:         $output = $this->_buildLine($node_id);
212: 
213:         if (isset($node['children']) && $node['expanded']) {
214:             foreach ($node['children'] as $key => $val) {
215:                 $child_node_id = $node['children'][$key];
216:                 $this->_node_pos[$child_node_id] = array(
217:                     'count' => count($node['children']),
218:                     'pos' => $key + 1
219:                 );
220:                 $output .= $this->_buildTree($child_node_id);
221:             }
222:         }
223: 
224:         return $output;
225:     }
226: 
227:     /**
228:      * Function to create a single line of the tree.
229:      *
230:      * @param string $node_id  The Node ID.
231:      *
232:      * @return string  The rendered line.
233:      */
234:     protected function _buildLine($node_id)
235:     {
236:         $node = $this->_nodes[$node_id];
237: 
238:         $className = 'treeRow';
239:         if (!empty($node['class'])) {
240:             $className .= ' ' . $node['class'];
241:         }
242: 
243:         /* If using alternating row shading, work out correct
244:          * shade. */
245:         if ($this->getOption('alternate')) {
246:             $className .= ' item' . $this->_altCount;
247:             $this->_altCount = 1 - $this->_altCount;
248:         }
249: 
250:         $line = '<div class="' . $className . '">';
251: 
252:         /* If we have headers, track which logical "column" we're in
253:          * for any given cell of content. */
254:         $column = 0;
255: 
256:         if (isset($node['extra'][Horde_Tree::EXTRA_LEFT])) {
257:             $extra = $node['extra'][Horde_Tree::EXTRA_LEFT];
258:             $cMax = count($extra);
259:             while ($column < $cMax) {
260:                 $line .= $this->_addColumn($column) . $extra[$column] . '</span>';
261:                 ++$column;
262:             }
263:         }
264: 
265:         $line .= $this->_addColumn($column++);
266: 
267:         if ($this->getOption('multiline')) {
268:             $line .= '<table cellspacing="0"><tr><td>';
269:         }
270: 
271:         for ($i = intval($this->_static); $i < $node['indent']; ++$i) {
272:             $line .= $this->_generateImage(($this->_dropline[$i] && $this->getOption('lines')) ? $this->_images['line'] : $this->_images['blank']);
273:         }
274:         $line .= $this->_setNodeToggle($node_id) . $this->_setNodeIcon($node_id);
275:         if ($this->getOption('multiline')) {
276:             $line .= '</td><td>';
277:         }
278:         $line .= $this->_setLabel($node_id);
279: 
280:         if ($this->getOption('multiline')) {
281:             $line .= '</td></tr></table>';
282:         }
283: 
284:         $line .= '</span>';
285: 
286:         if (isset($node['extra'][Horde_Tree::EXTRA_RIGHT])) {
287:             $extra = $node['extra'][Horde_Tree::EXTRA_RIGHT];
288:             $cMax = count($extra);
289:             for ($c = 0, $cMax = count($extra); $c < $cMax; ++$c) {
290:                 $line .= $this->_addColumn($column++) . $extra[$c] . '</span>';
291:             }
292:         }
293: 
294:         return $line . "</div>\n";
295:     }
296: 
297:     /**
298:      */
299:     protected function _addColumn($column)
300:     {
301:         $line = '<span';
302:         if (isset($this->_header[$column]['class'])) {
303:             $line .= ' class="' . $this->_header[$column]['class'] . '"';
304:         }
305:         return $line . '>';
306:     }
307: 
308:     /**
309:      * Sets the label on the tree line.
310:      *
311:      * @param string $node_id  The Node ID.
312:      *
313:      * @return string  The label for the tree line.
314:      */
315:     protected function _setLabel($node_id)
316:     {
317:         $n = $this->_nodes[$node_id];
318: 
319:         $output = '<span>';
320: 
321:         $label = $n['label'];
322:         if (!empty($n['url'])) {
323:             $target = '';
324:             if (!empty($n['target'])) {
325:                 $target = ' target="' . $n['target'] . '"';
326:             } elseif ($target = $this->getOption('target')) {
327:                 $target = ' target="' . $target . '"';
328:             }
329:             $output .= '<a' . (!empty($n['urlclass']) ? ' class="' . $n['urlclass'] . '"' : '') . ' href="' . $n['url'] . '"' . $target . '>' . $label . '</a>';
330:         } else {
331:             $output .= $label;
332:         }
333: 
334:         return $output . '</span>';
335:     }
336: 
337:     /**
338:      * Sets the node toggle on the tree line.
339:      *
340:      * @param string $node_id  The Node ID.
341:      *
342:      * @return string  The node toggle for the tree line.
343:      */
344:     protected function _setNodeToggle($node_id)
345:     {
346:         $link_start = '';
347:         $node = $this->_nodes[$node_id];
348: 
349:         /* Top level node. */
350:         if ($node['indent'] == 0) {
351:             $this->_dropline[0] = false;
352: 
353:             if ($this->_static) {
354:                 return '';
355:             }
356: 
357:             /* KEY:
358:              * 0: Only node
359:              * 1: Top node
360:              * 2: Middle node
361:              * 3: Bottom node */
362:             $node_type = 0;
363:             if ($this->getOption('lines_base') &&
364:                 (count($this->_root_nodes) > 1)) {
365:                 switch (array_search($node_id, $this->_root_nodes)) {
366:                 case 0:
367:                     $node_type = 1;
368:                     $this->_dropline[0] = true;
369:                     break;
370: 
371:                 case (count($this->_root_nodes) - 1):
372:                     $node_type = 3;
373:                     break;
374: 
375:                 default:
376:                     $node_type = 2;
377:                     $this->_dropline[0] = true;
378:                     break;
379:                 }
380:             }
381: 
382:             if (isset($node['children'])) {
383:                 if (!$this->getOption('lines')) {
384:                     $img = $this->_images['blank'];
385:                 } elseif ($node['expanded']) {
386:                     $img = $node_type
387:                         ? (($node_type == 2) ? $this->_images['minus'] : $this->_images['minus_bottom'])
388:                         : $this->_images['minus_only'];
389:                 } else {
390:                     $img = $node_type
391:                         ? (($node_type == 2) ? $this->_images['plus'] : $this->_images['plus_bottom'])
392:                         : $this->_images['plus_only'];
393:                 }
394: 
395:                 $link_start = $this->_generateUrlTag($node_id);
396:             } else {
397:                 if ($this->getOption('lines')) {
398:                     switch ($node_type) {
399:                     case 0:
400:                         $img = $this->_images['null_only'];
401:                         break;
402: 
403:                     case 1:
404:                         $img = $this->_images['join_top'];
405:                         break;
406: 
407:                     case 2:
408:                         $img = $this->_images['join'];
409:                         break;
410: 
411:                     case 3:
412:                         $img = $this->_images['join_bottom'];
413:                         break;
414:                     }
415:                 } else {
416:                     $img = $this->_images['blank'];
417:                 }
418:             }
419:         } elseif (isset($node['children'])) {
420:             /* Node with children. */
421:             if ($this->_node_pos[$node_id]['pos'] < $this->_node_pos[$node_id]['count']) {
422:                 /* Not last node. */
423:                 if (!$this->getOption('lines')) {
424:                     $img = $this->_images['blank'];
425:                 } elseif ($this->_static) {
426:                     $img = $this->_images['join'];
427:                 } elseif ($node['expanded']) {
428:                     $img = $this->_images['minus'];
429:                 } else {
430:                     $img = $this->_images['plus'];
431:                 }
432:                 $this->_dropline[$node['indent']] = true;
433:             } else {
434:                 /* Last node. */
435:                 if (!$this->getOption('lines')) {
436:                     $img = $this->_images['blank'];
437:                 } elseif ($this->_static) {
438:                     $img = $this->_images['join_bottom'];
439:                 } elseif ($node['expanded']) {
440:                     $img = $this->_images['minus_bottom'];
441:                 } else {
442:                     $img = $this->_images['plus_bottom'];
443:                 }
444:                 $this->_dropline[$node['indent']] = false;
445:             }
446: 
447:             if (!$this->_static) {
448:                 $link_start = $this->_generateUrlTag($node_id);
449:             }
450:         } else {
451:             /* Node without children. */
452:             if ($this->_node_pos[$node_id]['pos'] < $this->_node_pos[$node_id]['count']) {
453:                 /* Not last node. */
454:                 $img = $this->getOption('lines')
455:                     ? $this->_images['join']
456:                     : $this->_images['blank'];
457: 
458:                 $this->_dropline[$node['indent']] = true;
459:             } else {
460:                 /* Last node. */
461:                 $img = $this->getOption('lines')
462:                     ? $this->_images['join_bottom']
463:                     : $this->_images['blank'];
464: 
465:                 $this->_dropline[$node['indent']] = false;
466:             }
467:         }
468: 
469:         return $link_start .
470:             $this->_generateImage($img, 'treeToggle') .
471:             ($link_start ? '</a>' : '');
472:     }
473: 
474:     /**
475:      * Generate a link URL.
476:      *
477:      * @param string $node_id  The node ID.
478:      *
479:      * @return string  The link tag.
480:      */
481:     protected function _generateUrlTag($node_id)
482:     {
483:         $url = new Horde_Url($_SERVER['PHP_SELF']);
484:         return $url->add(Horde_Tree::TOGGLE . $this->_instance, $node_id)->link();
485:     }
486: 
487:     /**
488:      * Generate the icon image.
489:      *
490:      * @param string $src    The source image.
491:      * @param string $class  Additional class to add to image.
492:      * @param string $alt    Alt text to add to the image.
493:      *
494:      * @return string  A HTML tag to display the image.
495:      */
496:     protected function _generateImage($src, $class = '', $alt = null)
497:     {
498:         $img = '<img src="' . $src . '"';
499: 
500:         if ($class) {
501:             $img .= ' class="' . $class . '"';
502:         }
503: 
504:         if (!is_null($alt)) {
505:             $img .= ' alt="' . $alt . '"';
506:         }
507: 
508:         return $img . ' />';
509:     }
510: 
511:     /**
512:      * Sets the icon for the node.
513:      *
514:      * @param string $node_id  The Node ID.
515:      *
516:      * @return string  The node icon for the tree line.
517:      */
518:     protected function _setNodeIcon($node_id)
519:     {
520:         $node = $this->_nodes[$node_id];
521: 
522:         if (isset($node['icon'])) {
523:             if (empty($node['icon'])) {
524:                 return '';
525:             }
526: 
527:             /* Node has a user defined icon. */
528:             $img = (isset($node['iconopen']) && $node['expanded'])
529:                 ? $node['iconopen']
530:                 : $node['icon'];
531:         } elseif (isset($node['children'])) {
532:             /* Standard icon set: node with children. */
533:             $img = $node['expanded']
534:                 ? $this->_images['folderopen']
535:                 : $this->_images['folder'];
536:         } else {
537:             /* Standard icon set: leaf node (no children). */
538:             $img = $this->_images['leaf'];
539:         }
540: 
541:         return $this->_generateImage($img, 'treeIcon', isset($node['iconalt']) ? htmlspecialchars($node['iconalt']) : null);
542:     }
543: 
544: }
545: 
API documentation generated by ApiGen