[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * Aria menubar functionality. Enhances a simple nested list structure into a full aria widget. 18 * Based on the open ajax example: http://oaa-accessibility.org/example/26/ 19 * 20 * @module tool_lp/menubar 21 * @package tool_lp 22 * @copyright 2015 Damyon Wiese <damyon@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 define(['jquery'], function($) { 26 27 /** @property {boolean} Flag to indicate if we have already registered a click event handler for the document. */ 28 var documentClickHandlerRegistered = false; 29 30 /** @property {boolean} Flag to indicate whether there's an active, open menu. */ 31 var menuActive = false; 32 33 /** 34 * Close all open submenus anywhere in the page (there should only ever be one open at a time). 35 * 36 * @method closeAllSubMenus 37 */ 38 var closeAllSubMenus = function() { 39 $('.tool-lp-menu .tool-lp-sub-menu').attr('aria-hidden', 'true'); 40 // Every menu's closed at this point, so set the menu active flag to false. 41 menuActive = false; 42 }; 43 44 /** 45 * Constructor 46 * 47 * @param {$} menuRoot Jquery collection matching the root of the menu. 48 * @param {Function[]} handlers, called when a menu item is chosen. 49 */ 50 var Menubar = function(menuRoot, handlers) { 51 // Setup private class variables. 52 this.menuRoot = menuRoot; 53 this.handlers = handlers; 54 this.rootMenus = this.menuRoot.children('li'); 55 this.subMenus = this.rootMenus.children('ul'); 56 this.subMenuItems = this.subMenus.children('li'); 57 this.allItems = this.rootMenus.add(this.subMenuItems); 58 this.activeItem = null; 59 this.isChildOpen = false; 60 61 this.keys = { 62 tab: 9, 63 enter: 13, 64 esc: 27, 65 space: 32, 66 left: 37, 67 up: 38, 68 right: 39, 69 down: 40 70 }; 71 72 this.addAriaAttributes(); 73 // Add the event listeners. 74 this.addEventListeners(); 75 }; 76 77 /** 78 * Open a submenu, first it closes all other sub-menus and sets the open direction. 79 * @method openSubMenu 80 * @param {Node} menu 81 */ 82 Menubar.prototype.openSubMenu = function(menu) { 83 this.setOpenDirection(); 84 closeAllSubMenus(); 85 menu.attr('aria-hidden', 'false'); 86 // Set menu active flag to true when a menu is opened. 87 menuActive = true; 88 }; 89 90 91 /** 92 * Bind the event listeners to the DOM 93 * @method addEventListeners 94 */ 95 Menubar.prototype.addEventListeners = function() { 96 var currentThis = this; 97 98 // When clicking outside the menubar. 99 if (documentClickHandlerRegistered === false) { 100 $(document).click(function() { 101 // Check if a menu is opened. 102 if (menuActive) { 103 // Close menu. 104 closeAllSubMenus(); 105 } 106 }); 107 // Set this flag to true so that we won't need to add a document click handler for the other Menubar instances. 108 documentClickHandlerRegistered = true; 109 } 110 111 // Hovers. 112 this.subMenuItems.mouseenter(function() { 113 $(this).addClass('menu-hover'); 114 return true; 115 }); 116 117 this.subMenuItems.mouseout(function() { 118 $(this).removeClass('menu-hover'); 119 return true; 120 }); 121 122 // Mouse listeners. 123 this.allItems.click(function(e) { 124 return currentThis.handleClick($(this), e); 125 }); 126 127 // Key listeners. 128 this.allItems.keydown(function(e) { 129 return currentThis.handleKeyDown($(this), e); 130 }); 131 132 this.allItems.focus(function() { 133 return currentThis.handleFocus($(this)); 134 }); 135 136 this.allItems.blur(function() { 137 return currentThis.handleBlur($(this)); 138 }); 139 }; 140 141 /** 142 * Process click events for the top menus. 143 * 144 * @method handleClick 145 * @param {Object} item is the jquery object of the item firing the event 146 * @param {Event} e is the associated event object 147 * @return {boolean} Returns false 148 */ 149 Menubar.prototype.handleClick = function(item, e) { 150 e.stopPropagation(); 151 152 var parentUL = item.parent(); 153 154 if (parentUL.is('.tool-lp-menu')) { 155 // Toggle the child menu open/closed. 156 if (item.children('ul').first().attr('aria-hidden') == 'true') { 157 this.openSubMenu(item.children('ul').first()); 158 } else { 159 item.children('ul').first().attr('aria-hidden', 'true'); 160 } 161 } else { 162 // Remove hover and focus styling. 163 this.allItems.removeClass('menu-hover menu-focus'); 164 165 // Clear the active item. 166 this.activeItem = null; 167 168 // Close the menu. 169 this.menuRoot.find('ul').not('.root-level').attr('aria-hidden', 'true'); 170 // Follow any link, or call the click handlers. 171 var anchor = item.find('a').first(); 172 var clickEvent = new $.Event('click'); 173 clickEvent.target = anchor; 174 var eventHandled = false; 175 if (this.handlers) { 176 $.each(this.handlers, function(selector, handler) { 177 if (eventHandled) { 178 return; 179 } 180 if (item.find(selector).length > 0) { 181 var callable = $.proxy(handler, anchor); 182 // False means stop propogatting events. 183 eventHandled = (callable(clickEvent) === false) || clickEvent.isDefaultPrevented(); 184 } 185 }); 186 } 187 // If we didn't find a handler, and the HREF is # that probably means that 188 // we are handling it from somewhere else. Let's just do nothing in that case. 189 if (!eventHandled && anchor.attr('href') !== '#') { 190 window.location.href = anchor.attr('href'); 191 } 192 } 193 return false; 194 }; 195 196 /* 197 * Process focus events for the menu. 198 * 199 * @method handleFocus 200 * @param {Object} item is the jquery object of the item firing the event 201 * @return boolean Returns false 202 */ 203 Menubar.prototype.handleFocus = function(item) { 204 205 // If activeItem is null, we are getting focus from outside the menu. Store 206 // the item that triggered the event. 207 if (this.activeItem === null) { 208 this.activeItem = item; 209 } else if (item[0] != this.activeItem[0]) { 210 return true; 211 } 212 213 // Get the set of jquery objects for all the parent items of the active item. 214 var parentItems = this.activeItem.parentsUntil('ul.tool-lp-menu').filter('li'); 215 216 // Remove focus styling from all other menu items. 217 this.allItems.removeClass('menu-focus'); 218 219 // Add focus styling to the active item. 220 this.activeItem.addClass('menu-focus'); 221 222 // Add focus styling to all parent items. 223 parentItems.addClass('menu-focus'); 224 225 // If the bChildOpen flag has been set, open the active item's child menu (if applicable). 226 if (this.isChildOpen === true) { 227 228 var itemUL = item.parent(); 229 230 // If the itemUL is a root-level menu and item is a parent item, 231 // show the child menu. 232 if (itemUL.is('.tool-lp-menu') && (item.attr('aria-haspopup') == 'true')) { 233 this.openSubMenu(item.children('ul').first()); 234 } 235 } 236 237 return true; 238 }; 239 240 /* 241 * Process blur events for the menu. 242 * 243 * @method handleBlur 244 * @param {Object} item is the jquery object of the item firing the event 245 * @return boolean Returns false 246 */ 247 Menubar.prototype.handleBlur = function(item) { 248 item.removeClass('menu-focus'); 249 250 return true; 251 }; 252 253 /* 254 * Determine if the menu should open to the left, or the right, 255 * based on the screen size and menu position. 256 * @method setOpenDirection 257 */ 258 Menubar.prototype.setOpenDirection = function() { 259 var pos = this.menuRoot.offset(); 260 var isRTL = $(document.body).hasClass('dir-rtl'); 261 var openLeft = false; 262 var heightmenuRoot = this.rootMenus.outerHeight(); 263 var widthmenuRoot = this.rootMenus.outerWidth(); 264 // Sometimes the menuMinWidth is not enough to figure out if menu exceeds the window width. 265 // So we have to calculate the real menu width. 266 var subMenuContainer = this.rootMenus.find('ul.tool-lp-sub-menu'); 267 268 // Reset margins. 269 subMenuContainer.css('margin-right', ''); 270 subMenuContainer.css('margin-left', ''); 271 subMenuContainer.css('margin-top', ''); 272 273 subMenuContainer.attr('aria-hidden', false); 274 var menuRealWidth = subMenuContainer.outerWidth(), 275 menuRealHeight = subMenuContainer.outerHeight(); 276 277 var margintop = null, 278 marginright = null, 279 marginleft = null; 280 var top = pos.top - $(window).scrollTop(); 281 // Top is the same for RTL and LTR. 282 if (top + menuRealHeight > $(window).height()) { 283 margintop = menuRealHeight + heightmenuRoot; 284 subMenuContainer.css('margin-top', '-' + margintop + 'px'); 285 } 286 287 if (isRTL) { 288 if (pos.left - menuRealWidth < 0) { 289 marginright = menuRealWidth - widthmenuRoot; 290 subMenuContainer.css('margin-right', '-' + marginright + 'px'); 291 } 292 } else { 293 if (pos.left + menuRealWidth > $(window).width()) { 294 marginleft = menuRealWidth - widthmenuRoot; 295 subMenuContainer.css('margin-left', '-' + marginleft + 'px'); 296 } 297 } 298 299 if (openLeft) { 300 this.menuRoot.addClass('tool-lp-menu-open-left'); 301 } else { 302 this.menuRoot.removeClass('tool-lp-menu-open-left'); 303 } 304 305 }; 306 307 /* 308 * Process keyDown events for the menu. 309 * 310 * @method handleKeyDown 311 * @param {Object} item is the jquery object of the item firing the event 312 * @param {Event} e is the associated event object 313 * @return boolean Returns false if consuming the event 314 */ 315 Menubar.prototype.handleKeyDown = function(item, e) { 316 317 if (e.altKey || e.ctrlKey) { 318 // Modifier key pressed: Do not process. 319 return true; 320 } 321 322 switch (e.keyCode) { 323 case this.keys.tab: { 324 325 // Hide all menu items and update their aria attributes. 326 this.menuRoot.find('ul').attr('aria-hidden', 'true'); 327 328 // Remove focus styling from all menu items. 329 this.allItems.removeClass('menu-focus'); 330 331 this.activeItem = null; 332 333 this.isChildOpen = false; 334 335 break; 336 } 337 case this.keys.esc: { 338 var itemUL = item.parent(); 339 340 if (itemUL.is('.tool-lp-menu')) { 341 // Hide the child menu and update the aria attributes. 342 item.children('ul').first().attr('aria-hidden', 'true'); 343 } else { 344 345 // Move up one level. 346 this.activeItem = itemUL.parent(); 347 348 // Reset the isChildOpen flag. 349 this.isChildOpen = false; 350 351 // Set focus on the new item. 352 this.activeItem.focus(); 353 354 // Hide the active menu and update the aria attributes. 355 itemUL.attr('aria-hidden', 'true'); 356 } 357 358 e.stopPropagation(); 359 return false; 360 } 361 case this.keys.enter: 362 case this.keys.space: { 363 // Trigger click handler. 364 return this.handleClick(item, e); 365 } 366 367 case this.keys.left: { 368 369 this.activeItem = this.moveToPrevious(item); 370 371 this.activeItem.focus(); 372 373 e.stopPropagation(); 374 return false; 375 } 376 case this.keys.right: { 377 378 this.activeItem = this.moveToNext(item); 379 380 this.activeItem.focus(); 381 382 e.stopPropagation(); 383 return false; 384 } 385 case this.keys.up: { 386 387 this.activeItem = this.moveUp(item); 388 389 this.activeItem.focus(); 390 391 e.stopPropagation(); 392 return false; 393 } 394 case this.keys.down: { 395 396 this.activeItem = this.moveDown(item); 397 398 this.activeItem.focus(); 399 400 e.stopPropagation(); 401 return false; 402 } 403 } 404 405 return true; 406 407 }; 408 409 410 /** 411 * Move to the next menu level. 412 * This will be either the next root-level menu or the child of a menu parent. If 413 * at the root level and the active item is the last in the menu, this function will loop 414 * to the first menu item. 415 * 416 * If the menu is a horizontal menu, the first child element of the newly selected menu will 417 * be selected 418 * 419 * @method moveToNext 420 * @param {Object} item is the active menu item 421 * @return {Object} Returns the item to move to. Returns item is no move is possible 422 */ 423 Menubar.prototype.moveToNext = function(item) { 424 // Item's containing menu. 425 var itemUL = item.parent(); 426 427 // The items in the currently active menu. 428 var menuItems = itemUL.children('li'); 429 430 // The number of items in the active menu. 431 var menuNum = menuItems.length; 432 // The items index in its menu. 433 var menuIndex = menuItems.index(item); 434 var newItem = null; 435 var childMenu = null; 436 437 if (itemUL.is('.tool-lp-menu')) { 438 // This is the root level move to next sibling. This will require closing 439 // the current child menu and opening the new one. 440 441 if (menuIndex < menuNum - 1) { 442 // Not the last root menu. 443 newItem = item.next(); 444 } else { // Wrap to first item. 445 newItem = menuItems.first(); 446 } 447 448 // Close the current child menu (if applicable). 449 if (item.attr('aria-haspopup') == 'true') { 450 451 childMenu = item.children('ul').first(); 452 453 if (childMenu.attr('aria-hidden') == 'false') { 454 // Update the child menu's aria-hidden attribute. 455 childMenu.attr('aria-hidden', 'true'); 456 this.isChildOpen = true; 457 } 458 } 459 460 // Remove the focus styling from the current menu. 461 item.removeClass('menu-focus'); 462 463 // Open the new child menu (if applicable). 464 if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) { 465 466 childMenu = newItem.children('ul').first(); 467 468 // Update the child's aria-hidden attribute. 469 this.openSubMenu(childMenu); 470 } 471 } else { 472 // This is not the root level. If there is a child menu to be moved into, do that; 473 // otherwise, move to the next root-level menu if there is one. 474 if (item.attr('aria-haspopup') == 'true') { 475 476 childMenu = item.children('ul').first(); 477 478 newItem = childMenu.children('li').first(); 479 480 // Show the child menu and update its aria attributes. 481 this.openSubMenu(childMenu); 482 } else { 483 // At deepest level, move to the next root-level menu. 484 485 var parentMenus = null; 486 var rootItem = null; 487 488 // Get list of all parent menus for item, up to the root level. 489 parentMenus = item.parentsUntil('ul.tool-lp-menu').filter('ul').not('.tool-lp-menu'); 490 491 // Hide the current menu and update its aria attributes accordingly. 492 parentMenus.attr('aria-hidden', 'true'); 493 494 // Remove the focus styling from the active menu. 495 parentMenus.find('li').removeClass('menu-focus'); 496 parentMenus.last().parent().removeClass('menu-focus'); 497 498 // The containing root for the menu. 499 rootItem = parentMenus.last().parent(); 500 501 menuIndex = this.rootMenus.index(rootItem); 502 503 // If this is not the last root menu item, move to the next one. 504 if (menuIndex < this.rootMenus.length - 1) { 505 newItem = rootItem.next(); 506 } else { 507 // Loop. 508 newItem = this.rootMenus.first(); 509 } 510 511 // Add the focus styling to the new menu. 512 newItem.addClass('menu-focus'); 513 514 if (newItem.attr('aria-haspopup') == 'true') { 515 childMenu = newItem.children('ul').first(); 516 517 newItem = childMenu.children('li').first(); 518 519 // Show the child menu and update it's aria attributes. 520 this.openSubMenu(childMenu); 521 this.isChildOpen = true; 522 } 523 } 524 } 525 526 return newItem; 527 }; 528 529 /** 530 * Member function to move to the previous menu level. 531 * This will be either the previous root-level menu or the child of a menu parent. If 532 * at the root level and the active item is the first in the menu, this function will loop 533 * to the last menu item. 534 * 535 * If the menu is a horizontal menu, the first child element of the newly selected menu will 536 * be selected 537 * 538 * @method moveToPrevious 539 * @param {Object} item is the active menu item 540 * @return {Object} Returns the item to move to. Returns item is no move is possible 541 */ 542 Menubar.prototype.moveToPrevious = function(item) { 543 // Item's containing menu. 544 var itemUL = item.parent(); 545 // The items in the currently active menu. 546 var menuItems = itemUL.children('li'); 547 // The items index in its menu. 548 var menuIndex = menuItems.index(item); 549 var newItem = null; 550 var childMenu = null; 551 552 if (itemUL.is('.tool-lp-menu')) { 553 // This is the root level move to previous sibling. This will require closing 554 // the current child menu and opening the new one. 555 556 if (menuIndex > 0) { 557 // Not the first root menu. 558 newItem = item.prev(); 559 } else { 560 // Wrap to last item. 561 newItem = menuItems.last(); 562 } 563 564 // Close the current child menu (if applicable). 565 if (item.attr('aria-haspopup') == 'true') { 566 childMenu = item.children('ul').first(); 567 568 if (childMenu.attr('aria-hidden') == 'false') { 569 // Update the child menu's aria-hidden attribute. 570 childMenu.attr('aria-hidden', 'true'); 571 this.isChildOpen = true; 572 } 573 } 574 575 // Remove the focus styling from the current menu. 576 item.removeClass('menu-focus'); 577 578 // Open the new child menu (if applicable). 579 if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) { 580 581 childMenu = newItem.children('ul').first(); 582 583 // Update the child's aria-hidden attribute. 584 this.openSubMenu(childMenu); 585 586 } 587 } else { 588 // This is not the root level. If there is a parent menu that is not the 589 // root menu, move up one level; otherwise, move to first item of the previous 590 // root menu. 591 592 var parentLI = itemUL.parent(); 593 var parentUL = parentLI.parent(); 594 595 // If this is a vertical menu or is not the first child menu 596 // of the root-level menu, move up one level. 597 if (!parentUL.is('.tool-lp-menu')) { 598 599 newItem = itemUL.parent(); 600 601 // Hide the active menu and update aria-hidden. 602 itemUL.attr('aria-hidden', 'true'); 603 604 // Remove the focus highlight from the item. 605 item.removeClass('menu-focus'); 606 607 } else { 608 // Move to previous root-level menu. 609 610 // Hide the current menu and update the aria attributes accordingly. 611 itemUL.attr('aria-hidden', 'true'); 612 613 // Remove the focus styling from the active menu. 614 item.removeClass('menu-focus'); 615 parentLI.removeClass('menu-focus'); 616 617 menuIndex = this.rootMenus.index(parentLI); 618 619 if (menuIndex > 0) { 620 // Move to the previous root-level menu. 621 newItem = parentLI.prev(); 622 } else { 623 // Loop to last root-level menu. 624 newItem = this.rootMenus.last(); 625 } 626 627 // Add the focus styling to the new menu. 628 newItem.addClass('menu-focus'); 629 630 if (newItem.attr('aria-haspopup') == 'true') { 631 childMenu = newItem.children('ul').first(); 632 633 // Show the child menu and update it's aria attributes. 634 this.openSubMenu(childMenu); 635 this.isChildOpen = true; 636 637 newItem = childMenu.children('li').first(); 638 } 639 } 640 } 641 642 return newItem; 643 }; 644 645 /** 646 * Member function to select the next item in a menu. 647 * If the active item is the last in the menu, this function will loop to the 648 * first menu item. 649 * 650 * @method moveDown 651 * @param {Object} item is the active menu item 652 * @param {String} startChr is the character to attempt to match against the beginning of the 653 * menu item titles. If found, focus moves to the next menu item beginning with that character. 654 * @return {Object} Returns the item to move to. Returns item is no move is possible 655 */ 656 Menubar.prototype.moveDown = function(item, startChr) { 657 // Item's containing menu. 658 var itemUL = item.parent(); 659 // The items in the currently active menu. 660 var menuItems = itemUL.children('li').not('.separator'); 661 // The number of items in the active menu. 662 var menuNum = menuItems.length; 663 // The items index in its menu. 664 var menuIndex = menuItems.index(item); 665 var newItem = null; 666 var newItemUL = null; 667 668 if (itemUL.is('.tool-lp-menu')) { 669 // This is the root level menu. 670 671 if (item.attr('aria-haspopup') != 'true') { 672 // No child menu to move to. 673 return item; 674 } 675 676 // Move to the first item in the child menu. 677 newItemUL = item.children('ul').first(); 678 newItem = newItemUL.children('li').first(); 679 680 // Make sure the child menu is visible. 681 this.openSubMenu(newItemUL); 682 683 return newItem; 684 } 685 686 // If $item is not the last item in its menu, move to the next item. If startChr is specified, move 687 // to the next item with a title that begins with that character. 688 if (startChr) { 689 var match = false; 690 var curNdx = menuIndex + 1; 691 692 // Check if the active item was the last one on the list. 693 if (curNdx == menuNum) { 694 curNdx = 0; 695 } 696 697 // Iterate through the menu items (starting from the current item and wrapping) until a match is found 698 // or the loop returns to the current menu item. 699 while (curNdx != menuIndex) { 700 701 var titleChr = menuItems.eq(curNdx).html().charAt(0); 702 703 if (titleChr.toLowerCase() == startChr) { 704 match = true; 705 break; 706 } 707 708 curNdx = curNdx + 1; 709 710 if (curNdx == menuNum) { 711 // Reached the end of the list, start again at the beginning. 712 curNdx = 0; 713 } 714 } 715 716 if (match === true) { 717 newItem = menuItems.eq(curNdx); 718 719 // Remove the focus styling from the current item. 720 item.removeClass('menu-focus'); 721 722 return newItem; 723 } else { 724 return item; 725 } 726 } else { 727 if (menuIndex < menuNum - 1) { 728 newItem = menuItems.eq(menuIndex + 1); 729 } else { 730 newItem = menuItems.first(); 731 } 732 } 733 734 // Remove the focus styling from the current item. 735 item.removeClass('menu-focus'); 736 737 return newItem; 738 }; 739 740 /** 741 * Function moveUp() is a member function to select the previous item in a menu. 742 * If the active item is the first in the menu, this function will loop to the 743 * last menu item. 744 * 745 * @method moveUp 746 * @param {Object} item is the active menu item 747 * @return {Object} Returns the item to move to. Returns item is no move is possible 748 */ 749 Menubar.prototype.moveUp = function(item) { 750 // Item's containing menu. 751 var itemUL = item.parent(); 752 // The items in the currently active menu. 753 var menuItems = itemUL.children('li').not('.separator'); 754 // The items index in its menu. 755 var menuIndex = menuItems.index(item); 756 var newItem = null; 757 758 if (itemUL.is('.tool-lp-menu')) { 759 // This is the root level menu. 760 // Nothing to do. 761 return item; 762 } 763 764 // If item is not the first item in its menu, move to the previous item. 765 if (menuIndex > 0) { 766 newItem = menuItems.eq(menuIndex - 1); 767 } else { 768 // Loop to top of menu. 769 newItem = menuItems.last(); 770 } 771 772 // Remove the focus styling from the current item. 773 item.removeClass('menu-focus'); 774 775 return newItem; 776 }; 777 778 /** 779 * Enhance the dom with aria attributes. 780 * @method addAriaAttributes 781 */ 782 Menubar.prototype.addAriaAttributes = function() { 783 this.menuRoot.attr('role', 'menubar'); 784 this.rootMenus.attr('role', 'menuitem'); 785 this.rootMenus.attr('tabindex', '0'); 786 this.rootMenus.attr('aria-haspopup', 'true'); 787 this.subMenus.attr('role', 'menu'); 788 this.subMenus.attr('aria-hidden', 'true'); 789 this.subMenuItems.attr('role', 'menuitem'); 790 this.subMenuItems.attr('tabindex', '-1'); 791 792 // For CSS styling and effects. 793 this.menuRoot.addClass('tool-lp-menu'); 794 this.allItems.addClass('tool-lp-menu-item'); 795 this.rootMenus.addClass('tool-lp-root-menu'); 796 this.subMenus.addClass('tool-lp-sub-menu'); 797 }; 798 799 return /** @alias module:tool_lp/menubar */ { 800 /** 801 * Create a menu bar object for every node matching the selector. 802 * 803 * The expected DOM structure is shown below. 804 * <ul> <- This is the target of the selector parameter. 805 * <li> <- This is repeated for each top level menu. 806 * Text <- This is the text for the top level menu. 807 * <ul> <- This is a list of the entries in this top level menu. 808 * <li> <- This is repeated for each menu entry. 809 * <a href="someurl">Choice 1</a> <- The anchor for the menu. 810 * </li> 811 * </ul> 812 * </li> 813 * </ul> 814 * 815 * @method enhance 816 * @param {String} selector - The selector for the outer most menu node. 817 * @param {Function} handler - Javascript handler for when a menu item was chosen. If the 818 * handler returns true (or does not exist), the 819 * menu will look for an anchor with a link to follow. 820 * For example, if the menu entry has a "data-action" attribute 821 * and we want to call a javascript function when that entry is chosen, 822 * we could pass a list of handlers like this: 823 * { "[data-action='add']" : callAddFunction } 824 */ 825 enhance: function(selector, handler) { 826 $(selector).each(function(index, element) { 827 var menuRoot = $(element); 828 // Don't enhance the same menu twice. 829 if (menuRoot.data("menubarEnhanced") !== true) { 830 (new Menubar(menuRoot, handler)); 831 menuRoot.data("menubarEnhanced", true); 832 } 833 }); 834 }, 835 836 /** 837 * Handy function to close all open menus anywhere on the page. 838 * @method closeAll 839 */ 840 closeAll: closeAllSubMenus 841 }; 842 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |