1: <?php
2: /**
3: * TbNav class file.
4: * @author Christoffer Niska <[email protected]>
5: * @copyright Copyright © Christoffer Niska 2013-
6: * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
7: * @package bootstrap.widgets
8: */
9:
10: Yii::import('bootstrap.behaviors.TbWidget');
11: Yii::import('bootstrap.helpers.TbHtml');
12:
13: /**
14: * Bootstrap navigation menu widget.
15: * @see http://twitter.github.com/bootstrap/components.html#navbar
16: */
17: class TbNav extends CWidget
18: {
19: /**
20: * @var string the menu type.
21: */
22: public $type;
23: /**
24: * @var boolean whether the menu items should be stacked on top of each other.
25: */
26: public $stacked = false;
27: /**
28: * @var string|array the scrollspy target or configuration.
29: */
30: public $scrollspy;
31: /**
32: * @var array list of menu items. Each menu item is specified as an array of name-value pairs.
33: */
34: public $items = array();
35: /**
36: * @var boolean whether the labels for menu items should be HTML-encoded. Defaults to true.
37: */
38: public $encodeLabel = true;
39: /**
40: * @var boolean whether to automatically activate items according to whether their route setting
41: * matches the currently requested route. Defaults to true.
42: */
43: public $activateItems = true;
44: /**
45: * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active.
46: */
47: public $activateParents = false;
48: /**
49: * @var boolean whether to hide empty menu items.
50: */
51: public $hideEmptyItems = true;
52: /**
53: * @var array HTML attributes for the menu's root container tag.
54: */
55: public $htmlOptions = array();
56:
57: // todo: consider supporting these.
58: //public $submenuHtmlOptions = array();
59: //public $linkLabelWrapper;
60: //public $linkLabelWrapperHtmlOptions=array();
61: //public $itemCssClass;
62:
63: /**
64: * Initializes the widget.
65: */
66: public function init()
67: {
68: $this->attachBehavior('TbWidget', new TbWidget);
69: $this->copyId();
70: $route = $this->controller->getRoute();
71: if ($this->stacked) {
72: TbHtml::addCssClass('nav-stacked', $this->htmlOptions);
73: }
74: if (isset($this->scrollspy)) {
75: if (is_string($this->scrollspy)) {
76: $this->scrollspy = array('target' => $this->scrollspy);
77: }
78: $this->widget('bootstrap.widgets.TbScrollspy', $this->scrollspy);
79: }
80: $this->items = $this->normalizeItems($this->items, $route, $hasActiveChild);
81: }
82:
83: /**
84: * Runs the widget.
85: */
86: public function run()
87: {
88: if (!empty($this->items)) {
89: echo TbHtml::nav($this->type, $this->items, $this->htmlOptions);
90: }
91: }
92:
93: /**
94: * Normalizes the menu items.
95: * @param array $items the items to be normalized.
96: * @param string $route the route of the current request.
97: * @param boolean $active whether there is an active child menu item.
98: * @return array the normalized menu items.
99: */
100: protected function normalizeItems($items, $route, &$active)
101: {
102: foreach ($items as $i => $item) {
103: // skip dividers
104: if (is_string($item)) {
105: continue;
106: }
107:
108: if (isset($item['visible']) && !$item['visible']) {
109: unset($items[$i]);
110: continue;
111: }
112:
113: TbArray::defaultValue('label', '', $item);
114:
115: if ($this->encodeLabel) {
116: $items[$i]['label'] = CHtml::encode($item['label']);
117: }
118:
119: $hasActiveChild = false;
120:
121: if (isset($item['items']) && !empty($item['items'])) {
122: $items[$i]['items'] = $this->normalizeItems($item['items'], $route, $hasActiveChild);
123:
124: if (empty($items[$i]['items']) && $this->hideEmptyItems) {
125: unset($items[$i]['items']);
126: if (!isset($item['url'])) {
127: unset($items[$i]);
128: continue;
129: }
130: }
131: }
132:
133: if (!isset($item['active'])) {
134: if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive(
135: $item,
136: $route
137: )
138: ) {
139: $active = $items[$i]['active'] = true;
140: } else {
141: $items[$i]['active'] = false;
142: }
143: } else {
144: if ($item['active']) {
145: $active = true;
146: }
147: }
148: }
149:
150: return array_values($items);
151: }
152:
153: /**
154: * Checks whether a menu item is active.
155: * @param array $item the menu item to be checked.
156: * @param string $route the route of the current request.
157: * @return boolean whether the menu item is active.
158: */
159: protected function isItemActive($item, $route)
160: {
161: if (isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0], '/'), $route)) {
162: unset($item['url']['#']);
163: if (count($item['url']) > 1) {
164: foreach (array_splice($item['url'], 1) as $name => $value) {
165: if (!isset($_GET[$name]) || $_GET[$name] != $value) {
166: return false;
167: }
168: }
169: }
170: return true;
171: }
172: return false;
173: }
174: }
175: