/js/yii/zii/widgets/CMenu.js
JavaScript | 292 lines | 142 code | 4 blank | 146 comment | 45 complexity | df762f186334d9f412bd859bcc00c13e MD5 | raw file
1/*global Yii, php, $, jQuery, alert, clearInterval, clearTimeout, document, event, frames, history, Image, location, name, navigator, Option, parent, screen, setInterval, setTimeout, window, XMLHttpRequest */ 2/** 3 * CMenu displays a multi-level menu using nested HTML lists. 4 * 5 * The main property of CMenu is {@link items}, which specifies the possible items in the menu. 6 * A menu item has three main properties: visible, active and items. The "visible" property 7 * specifies whether the menu item is currently visible. The "active" property specifies whether 8 * the menu item is currently selected. And the "items" property specifies the child menu items. 9 * 10 * The following example shows how to use CMenu: 11 * <pre> 12 * this.widget('zii.widgets.CMenu', { 13 * 'items':{ 14 * // Important: you need to specify url as 'controller/action', 15 * // not just as 'controller' even if default acion is used. 16 * {'label':'Home', 'url':{'site/index'}), 17 * {'label':'Products', 'url':{'product/index'}, 'items':{ 18 * {'label':'New Arrivals', 'url':{'product/new', 'tag':'new'}), 19 * {'label':'Most Popular', 'url':{'product/index', 'tag':'popular'}), 20 * )), 21 * {'label':'Login', 'url':{'site/login'}, 'visible':Yii.app().user.isGuest), 22 * ), 23 * }); 24 * </pre> 25 * 26 * 27 * @originalAuthor Jonah Turnquist <poppitypop@gmail.com> 28 * @originalAuthor Qiang Xue <qiang.xue@gmail.com> 29 * @version $Id: CMenu.php 3034 2011-03-08 18:22:29Z qiang.xue $ 30 * @package zii.widgets 31 * @since 1.1 32 * @author Charles Pick 33 * @class 34 * @extends Yii.CWidget 35 */ 36Yii.CMenu = function CMenu () { 37 38}; 39Yii.CMenu.prototype = new Yii.CWidget(false); 40Yii.CMenu.prototype.constructor = Yii.CMenu; 41/** 42 * @var {Array} list of menu items. Each menu item is specified as an array of name-value pairs. 43 * Possible option names include the following: 44 * <ul> 45 * <li>label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label 46 * will be HTML-encoded. If the label is not specified, it defaults to an empty string.</li> 47 * <li>url: string or array, optional, specifies the URL of the menu item. It is passed to {@link CHtml::normalizeUrl} 48 * to generate a valid URL. If this is not set, the menu item will be rendered as a span text.</li> 49 * <li>visible: boolean, optional, whether this menu item is visible. Defaults to true. 50 * This can be used to control the visibility of menu items based on user permissions.</li> 51 * <li>items: array, optional, specifies the sub-menu items. Its format is the same as the parent items.</li> 52 * <li>active: boolean, optional, whether this menu item is in active state (currently selected). 53 * If a menu item is active and {@link activeClass} is not empty, its CSS class will be appended with {@link activeClass}. 54 * If this option is not set, the menu item will be set active automatically when the current request 55 * is triggered by {@link url}. Note that the GET parameters not specified in the 'url' option will be ignored.</li> 56 * <li>template: string, optional, the template used to render this menu item. 57 * When this option is set, it will override the global setting {@link itemTemplate}. 58 * Please see {@link itemTemplate} for more details. This option has been available since version 1.1.1.</li> 59 * <li>linkOptions: array, optional, additional HTML attributes to be rendered for the link or span tag of the menu item.</li> 60 * <li>itemOptions: array, optional, additional HTML attributes to be rendered for the container tag of the menu item.</li> 61 * <li>submenuOptions: array, optional, additional HTML attributes to be rendered for the container of the submenu if this menu item has one. 62 * When this option is set, the {@link submenuHtmlOptions} property will be ignored for this particular submenu. 63 * This option has been available since version 1.1.6.</li> 64 * </ul> 65 */ 66Yii.CMenu.prototype.items = []; 67/** 68 * @var {String} the template used to render an individual menu item. In this template, 69 * the token "{menu}" will be replaced with the corresponding menu link or text. 70 * If this property is not set, each menu will be rendered without any decoration. 71 * This property will be overridden by the 'template' option set in individual menu items via {@items}. 72 * @since 1.1.1 73 */ 74Yii.CMenu.prototype.itemTemplate = undefined; 75/** 76 * @var {Boolean} whether the labels for menu items should be HTML-encoded. Defaults to true. 77 */ 78Yii.CMenu.prototype.encodeLabel = true; 79/** 80 * @var {String} the CSS class to be appended to the active menu item. Defaults to 'active'. 81 * If empty, the CSS class of menu items will not be changed. 82 */ 83Yii.CMenu.prototype.activeCssClass = 'active'; 84/** 85 * @var {Boolean} whether to automatically activate items according to whether their route setting 86 * matches the currently requested route. Defaults to true. 87 * @since 1.1.3 88 */ 89Yii.CMenu.prototype.activateItems = true; 90/** 91 * @var {Boolean} whether to activate parent menu items when one of the corresponding child menu items is active. 92 * The activated parent menu items will also have its CSS classes appended with {@link activeCssClass}. 93 * Defaults to false. 94 */ 95Yii.CMenu.prototype.activateParents = false; 96/** 97 * @var {Boolean} whether to hide empty menu items. An empty menu item is one whose 'url' option is not 98 * set and which doesn't contain visible child menu items. Defaults to true. 99 */ 100Yii.CMenu.prototype.hideEmptyItems = true; 101/** 102 * @var {Object} HTML attributes for the menu's root container tag 103 */ 104Yii.CMenu.prototype.htmlOptions = {}; 105/** 106 * @var {Object} HTML attributes for the submenu's container tag. 107 */ 108Yii.CMenu.prototype.submenuHtmlOptions = {}; 109/** 110 * @var {String} the HTML element name that will be used to wrap the label of all menu links. 111 * For example, if this property is set as 'span', a menu item may be rendered as 112 * <li><a href="url"><span>label</span></a></li> 113 * This is useful when implementing menu items using the sliding window technique. 114 * Defaults to null, meaning no wrapper tag will be generated. 115 * @since 1.1.4 116 */ 117Yii.CMenu.prototype.linkLabelWrapper = null; 118/** 119 * @var {String} the CSS class that will be assigned to the first item in the main menu or each submenu. 120 * Defaults to null, meaning no such CSS class will be assigned. 121 * @since 1.1.4 122 */ 123Yii.CMenu.prototype.firstItemCssClass = null; 124/** 125 * @var {String} the CSS class that will be assigned to the last item in the main menu or each submenu. 126 * Defaults to null, meaning no such CSS class will be assigned. 127 * @since 1.1.4 128 */ 129Yii.CMenu.prototype.lastItemCssClass = null; 130 131/** 132 * Calls {@link renderMenu} to render the menu. 133 */ 134Yii.CMenu.prototype.run = function () { 135 var route, hasActiveChild; 136 this.htmlOptions.id=this.getId(); 137 route=this.getController().getRoute(); 138 this.items=this.normalizeItems(this.items,route,hasActiveChild); 139 return this.renderMenu(this.items); 140 }; 141/** 142 * Renders the menu items. 143 * @param {Array} items menu items. Each menu item will be an array with at least two elements: 'label' and 'active'. 144 * It may have three other optional elements: 'items', 'linkOptions' and 'itemOptions'. 145 */ 146Yii.CMenu.prototype.renderMenu = function (items) { 147 var ret = ""; 148 if(items !== undefined && items !== null && items.length > 0) { 149 ret += Yii.CHtml.openTag('ul',this.htmlOptions)+"\n"; 150 ret += this.renderMenuRecursive(items); 151 ret += Yii.CHtml.closeTag('ul'); 152 } 153 return ret; 154 }; 155/** 156 * Recursively renders the menu items. 157 * @param {Array} items the menu items to be rendered recursively 158 */ 159Yii.CMenu.prototype.renderMenuRecursive = function (items) { 160 var count, n, i, limit, options, item, classVar, menu, template, ret = ""; 161 count=0; 162 163 n=items.length; 164 limit = items.length; 165 for (i = 0; i < limit; i++) { 166 item = items[i]; 167 count++; 168 options=item.itemOptions !== undefined ? item.itemOptions : {}; 169 classVar=[]; 170 if(item['active'] && this.activeCssClass!='') { 171 classVar.push(this.activeCssClass); 172 } 173 if(count===1 && this.firstItemCssClass!='') { 174 classVar.push(this.firstItemCssClass); 175 } 176 if(count===n && this.lastItemCssClass!='') { 177 classVar.push(this.lastItemCssClass); 178 } 179 if(classVar!==[]) { 180 if(php.empty(options['class'])) { 181 options['class']=classVar.join(' '); 182 } 183 else { 184 options['class']+=' '+classVar.join(' '); 185 } 186 } 187 ret += Yii.CHtml.openTag('li', options); 188 menu=this.renderMenuItem(item); 189 if(this.itemTemplate !== undefined || item.template !== undefined) { 190 template=item.template !== undefined ? item.template : this.itemTemplate; 191 ret += php.strtr(template,{'{menu}':menu}); 192 } 193 else { 194 ret += menu; 195 } 196 if(item.items !== undefined && php.count(item.items)) { 197 ret += "\n"+Yii.CHtml.openTag('ul',item.submenuOptions !== undefined ? item.submenuOptions : this.submenuHtmlOptions)+"\n"; 198 this.renderMenuRecursive(item.items); 199 ret += Yii.CHtml.closeTag('ul')+"\n"; 200 } 201 ret += Yii.CHtml.closeTag('li')+"\n"; 202 } 203 return ret; 204 }; 205/** 206 * Renders the content of a menu item. 207 * Note that the container and the sub-menus are not rendered here. 208 * @param {Array} item the menu item to be rendered. Please see {@link items} on what data might be in the item. 209 * @since 1.1.6 210 */ 211Yii.CMenu.prototype.renderMenuItem = function (item) { 212 var label; 213 if(item.url !== undefined) { 214 label=this.linkLabelWrapper===null ? item.label : '<'+this.linkLabelWrapper+'>'+item.label+'</'+this.linkLabelWrapper+'>'; 215 return Yii.CHtml.link(label,item.url,item.linkOptions !== undefined ? item.linkOptions : {}); 216 } 217 else { 218 return Yii.CHtml.tag('span',item.linkOptions !== undefined ? item.linkOptions : {}, item.label); 219 } 220 }; 221/** 222 * Normalizes the {@link items} property so that the 'active' state is properly identified for every menu item. 223 * @param {Array} items the items to be normalized. 224 * @param {String} route the route of the current request. 225 * @param {Boolean} active whether there is an active child menu item. 226 * @returns {Array} the normalized menu items 227 */ 228Yii.CMenu.prototype.normalizeItems = function (items, route, active) { 229 var item, i, hasActiveChild; 230 for (i in items) { 231 if (items.hasOwnProperty(i)) { 232 item = items[i]; 233 if(item.visible !== undefined && !item.visible) { 234 delete items[i]; 235 continue; 236 } 237 if(item.label === undefined) { 238 item.label=''; 239 } 240 if(this.encodeLabel) { 241 items[i].label=Yii.CHtml.encode(item.label); 242 } 243 hasActiveChild=false; 244 if(item.items !== undefined) { 245 items[i].items=this.normalizeItems(item.items,route,hasActiveChild); 246 if(php.empty(items[i].items) && this.hideEmptyItems) { 247 delete items[i].items; 248 } 249 } 250 if(item.active === undefined) { 251 if(this.activateParents && hasActiveChild || this.activateItems && this.isItemActive(item,route)) { 252 active=items[i].active=true; 253 } 254 else { 255 items[i].active=false; 256 } 257 } 258 else if(item.active) { 259 active=true; 260 } 261 } 262 } 263 return items; 264 //return php.array_values(items); 265 }; 266/** 267 * Checks whether a menu item is active. 268 * This is done by checking if the currently requested URL is generated by the 'url' option 269 * of the menu item. Note that the GET parameters not specified in the 'url' option will be ignored. 270 * @param {Array} item the menu item to be checked 271 * @param {String} route the route of the current request 272 * @returns {Boolean} whether the menu item is active 273 */ 274Yii.CMenu.prototype.isItemActive = function (item, route) { 275 var nameValue, name, value; 276 if(item.url !== undefined && Object.prototype.toString.call(item.url) === '[object Array]' && !php.strcasecmp(php.trim(item.url[0],'/'),route)) { 277 278 if(php.count(item.url)>1) { 279 nameValue = php.array_splice(item.url,1); 280 for (name in nameValue) { 281 if (nameValue.hasOwnProperty(name)) { 282 value = nameValue[name]; 283 if(Yii.app().getRequest().params[name] === undefined || Yii.app().getRequest().params[name]!=value) { 284 return false; 285 } 286 } 287 } 288 } 289 return true; 290 } 291 return false; 292 };