/* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ var Menubar = function (domNode) { var elementChildren, msgPrefix = 'Menubar constructor argument menubarNode '; // Check whether menubarNode is a DOM element if (!domNode instanceof Element) { throw new TypeError(msgPrefix + 'is not a DOM Element.'); } // Check whether menubarNode has descendant elements if (domNode.childElementCount === 0) { throw new Error(msgPrefix + 'has no element children.'); } // Check whether menubarNode has A elements e = domNode.firstElementChild; while (e) { var menubarItem = e.firstElementChild; if (e && menubarItem && menubarItem.tagName !== 'A') { throw new Error(msgPrefix + 'has child elements are not A elements.'); } e = e.nextElementSibling; } this.isMenubar = true; this.domNode = domNode; this.menubarItems = []; // See Menubar init method this.firstChars = []; // See Menubar init method this.firstItem = null; // See Menubar init method this.lastItem = null; // See Menubar init method this.hasFocus = false; // See MenubarItem handleFocus, handleBlur this.hasHover = false; // See Menubar handleMouseover, handleMouseout }; /* * @method Menubar.prototype.init * * @desc * Adds ARIA role to the menubar node * Traverse menubar children for A elements to configure each A element as a ARIA menuitem * and populate menuitems array. Initialize firstItem and lastItem properties. */ Menubar.prototype.init = function () { var menubarItem, childElement, menuElement, textContent, numItems; // Traverse the element children of menubarNode: configure each with // menuitem role behavior and store reference in menuitems array. elem = this.domNode.firstElementChild; while (elem) { var menuElement = elem.firstElementChild; if (elem && menuElement && menuElement.tagName === 'A') { menubarItem = new MenubarItem(menuElement, this); menubarItem.init(); this.menubarItems.push(menubarItem); textContent = menuElement.textContent.trim(); this.firstChars.push(textContent.substring(0, 1).toLowerCase()); } elem = elem.nextElementSibling; } // Use populated menuitems array to initialize firstItem and lastItem. numItems = this.menubarItems.length; if (numItems > 0) { this.firstItem = this.menubarItems[ 0 ]; this.lastItem = this.menubarItems[ numItems - 1 ]; } this.firstItem.domNode.tabIndex = 0; }; /* FOCUS MANAGEMENT METHODS */ Menubar.prototype.setFocusToItem = function (newItem) { var flag = false; for (var i = 0; i < this.menubarItems.length; i++) { var mbi = this.menubarItems[i]; if (mbi.domNode.tabIndex == 0) { flag = mbi.domNode.getAttribute('aria-expanded') === 'true'; } mbi.domNode.tabIndex = -1; if (mbi.popupMenu) { mbi.popupMenu.close(); } } newItem.domNode.focus(); newItem.domNode.tabIndex = 0; if (flag && newItem.popupMenu) { newItem.popupMenu.open(); } }; Menubar.prototype.setFocusToFirstItem = function (flag) { this.setFocusToItem(this.firstItem); }; Menubar.prototype.setFocusToLastItem = function (flag) { this.setFocusToItem(this.lastItem); }; Menubar.prototype.setFocusToPreviousItem = function (currentItem) { var index; if (currentItem === this.firstItem) { newItem = this.lastItem; } else { index = this.menubarItems.indexOf(currentItem); newItem = this.menubarItems[ index - 1 ]; } this.setFocusToItem(newItem); }; Menubar.prototype.setFocusToNextItem = function (currentItem) { var index; if (currentItem === this.lastItem) { newItem = this.firstItem; } else { index = this.menubarItems.indexOf(currentItem); newItem = this.menubarItems[ index + 1 ]; } this.setFocusToItem(newItem); }; Menubar.prototype.setFocusByFirstCharacter = function (currentItem, char) { var start, index, char = char.toLowerCase(); var flag = currentItem.domNode.getAttribute('aria-expanded') === 'true'; // Get start index for search based on position of currentItem start = this.menubarItems.indexOf(currentItem) + 1; if (start === this.menubarItems.length) { start = 0; } // Check remaining slots in the menu index = this.getIndexFirstChars(start, char); // If not found in remaining slots, check from beginning if (index === -1) { index = this.getIndexFirstChars(0, char); } // If match was found... if (index > -1) { this.setFocusToItem(this.menubarItems[ index ]); } }; Menubar.prototype.getIndexFirstChars = function (startIndex, char) { for (var i = startIndex; i < this.firstChars.length; i++) { if (char === this.firstChars[ i ]) { return i; } } return -1; };