// Avoid `console` errors in browsers that lack a console. (function() { var method; var noop = function () {}; var methods = [ 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn' ]; var length = methods.length; var console = (window.console = window.console || {}); while (length--) { method = methods[length]; // Only stub undefined methods. if (!console[method]) { console[method] = noop; } } }()); // Start Adobe's jquery-accessibleMegaMenu /* Copyright 2013 Adobe Systems Incorporated. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Licencse info per https://github.com/adobe-accessibility/Accessible-Mega-Menu/blob/master/LICENSE */ /*jslint browser: true, devel: true, plusplus: true, nomen: true */ /*global jQuery */ (function ($, window, document) { "use strict"; var pluginName = "accessibleMegaMenu", defaults = { uuidPrefix: "accessible-megamenu", // unique ID's are required to indicate aria-owns, aria-controls and aria-labelledby menuClass: "accessible-megamenu", // default css class used to define the megamenu styling topNavItemClass: "accessible-megamenu-top-menu-item-has-children", // default css class for a top-level navigation item in the megamenu panelClass: "accessible-megamenu-panel", // default css class for a megamenu panel panelGroupClass: "accessible-megamenu-panel-group", // default css class for a group of items within a megamenu panel hoverClass: "hover", // default css class for the hover state focusClass: "focus", // default css class for the focus state openClass: "open" // default css class for the open state }, Keyboard = { BACKSPACE: 8, COMMA: 188, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, LEFT: 37, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SPACE: 32, TAB: 9, UP: 38, keyMap: { 48: "0", 49: "1", 50: "2", 51: "3", 52: "4", 53: "5", 54: "6", 55: "7", 56: "8", 57: "9", 59: ";", 65: "a", 66: "b", 67: "c", 68: "d", 69: "e", 70: "f", 71: "g", 72: "h", 73: "i", 74: "j", 75: "k", 76: "l", 77: "m", 78: "n", 79: "o", 80: "p", 81: "q", 82: "r", 83: "s", 84: "t", 85: "u", 86: "v", 87: "w", 88: "x", 89: "y", 90: "z", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9", 190: "." } }; /** * @desc Creates a new accessible mega menu instance. * @param {jquery} element * @param {object} [options] Mega Menu options * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling * @param {string} [options.topNavItemClass=accessible-megamenu-top-menu-item-has-children] - CSS class for a top-level navigation item in the megamenu * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel * @param {string} [options.hoverClass=hover] - CSS class for the hover state * @param {string} [options.focusClass=focus] - CSS class for the focus state * @param {string} [options.openClass=open] - CSS class for the open state * @constructor */ function AccessibleMegaMenu(element, options) { this.element = element; // merge optional settings and defaults into settings this.settings = $.extend({}, defaults, options); this._defaults = defaults; this._name = pluginName; this.init(); } AccessibleMegaMenu.prototype = (function () { /* private attributes and methods ------------------------ */ var uuid = 0, keydownTimeoutDuration = 1000, keydownSearchString = "", isTouch = typeof window.hasOwnProperty === "function" && !!window.hasOwnProperty("ontouchstart"), _getPlugin, _addUniqueId, _togglePanel, _clickHandler, _clickOutsideHandler, _DOMAttrModifiedHandler, _focusInHandler, _focusOutHandler, _keyDownHandler, _mouseDownHandler, _mouseOverHandler, _mouseOutHandler, _toggleExpandedEventHandlers; /** * @name jQuery.fn.accessibleMegaMenu~_getPlugin * @desc Returns the parent accessibleMegaMenu instance for a given element * @param {jQuery} element * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _getPlugin = function (element) { return $(element).closest(':data(plugin_' + pluginName + ')').data("plugin_" + pluginName); }; /** * @name jQuery.fn.accessibleMegaMenu~_addUniqueId * @desc Adds a unique id and element. * The id string starts with the * string defined in settings.uuidPrefix. * @param {jQuery} element * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _addUniqueId = function (element) { element = $(element); var settings = this.settings; if (!element.attr("id")) { element.attr("id", settings.uuidPrefix + "-" + new Date().getTime() + "-" + (++uuid)); } }; /** * @name jQuery.fn.accessibleMegaMenu~_togglePanel * @desc Toggle the display of mega menu panels in response to an event. * The optional boolean value 'hide' forces all panels to hide. * @param {event} event * @param {Boolean} [hide] Hide all mega menu panels when true * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _togglePanel = function (event, hide) { var target = $(event.target), that = this, settings = this.settings, menu = this.menu, topli = target.closest('.' + settings.topNavItemClass), panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass), newfocus; _toggleExpandedEventHandlers.call(this, hide); $('html').off('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu, pointerup.outside-accessible-megamenu', _clickOutsideHandler); menu.find('[aria-expanded].' + this.settings.panelClass).off('DOMAttrModified.accessible-megamenu'); if (hide) { topli = menu.find('.' + settings.topNavItemClass + ' .' + settings.openClass + ':first').closest('.' + settings.topNavItemClass); if (!(topli.is(event.relatedTarget) || topli.has(event.relatedTarget).length > 0)) { if ((event.type === 'mouseout' || event.type === 'focusout') && topli.has(document.activeElement).length > 0) { return; } topli.find('[aria-expanded]') .attr('aria-expanded', 'false') .removeClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'true'); if ((event.type === 'keydown' && event.keyCode === Keyboard.ESCAPE) || event.type === 'DOMAttrModified') { newfocus = topli.find(':tabbable:first'); setTimeout(function () { menu.find('[aria-expanded].' + that.settings.panelClass).off('DOMAttrModified.accessible-megamenu'); newfocus.focus(); that.justFocused = false; }, 99); } } else if (topli.length === 0) { menu.find('[aria-expanded=true]') .attr('aria-expanded', 'false') .removeClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'true'); } } else { clearTimeout(that.focusTimeoutID); topli.siblings() .find('[aria-expanded]') .attr('aria-expanded', 'false') .removeClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'true'); topli.find('[aria-expanded]') .attr('aria-expanded', 'true') .addClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'false'); if (event.type === 'mouseover' && target.is(':tabbable') && topli.length === 1 && panel.length === 0 && menu.has(document.activeElement).length > 0) { target.focus(); that.justFocused = false; } _toggleExpandedEventHandlers.call(that); } }; /** * @name jQuery.fn.accessibleMegaMenu~_clickHandler * @desc Handle click event on mega menu item * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _clickHandler = function (event) { var target = $(event.target), topli = target.closest('.' + this.settings.topNavItemClass), panel = target.closest('.' + this.settings.panelClass); if (topli.length === 1 && panel.length === 0 && topli.find('.' + this.settings.panelClass).length === 1) { if (!target.hasClass(this.settings.openClass)) { event.preventDefault(); event.stopPropagation(); _togglePanel.call(this, event); } else { if (this.justFocused) { event.preventDefault(); event.stopPropagation(); this.justFocused = false; } else if (isTouch) { event.preventDefault(); event.stopPropagation(); _togglePanel.call(this, event, target.hasClass(this.settings.openClass)); } } } }; /** * @name jQuery.fn.accessibleMegaMenu~_clickOutsideHandler * @desc Handle click event outside of a the megamenu * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _clickOutsideHandler = function (event) { if (this.menu.has($(event.target)).length === 0) { event.preventDefault(); event.stopPropagation(); _togglePanel.call(this, event, true); } }; /** * @name jQuery.fn.accessibleMegaMenu~_DOMAttrModifiedHandler * @desc Handle DOMAttrModified event on panel to respond to Windows 8 Narrator ExpandCollapse pattern * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _DOMAttrModifiedHandler = function (event) { if (event.originalEvent.attrName === 'aria-expanded' && event.originalEvent.newValue === 'false' && $(event.target).hasClass(this.settings.openClass)) { event.preventDefault(); event.stopPropagation(); _togglePanel.call(this, event, true); } }; /** * @name jQuery.fn.accessibleMegaMenu~_focusInHandler * @desc Handle focusin event on mega menu item. * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _focusInHandler = function (event) { clearTimeout(this.focusTimeoutID); $(event.target) .addClass(this.settings.focusClass) .on('click.accessible-megamenu', $.proxy(_clickHandler, this)); this.justFocused = true; if (this.panels.filter('.' + this.settings.openClass).length) { _togglePanel.call(this, event); } }; /** * @name jQuery.fn.accessibleMegaMenu~_focusOutHandler * @desc Handle focusout event on mega menu item. * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _focusOutHandler = function (event) { this.justFocused = false; var that = this, target = $(event.target), topli = target.closest('.' + this.settings.topNavItemClass), keepOpen = false; target .removeClass(this.settings.focusClass) .off('click.accessible-megamenu', _clickHandler); if (window.cvox) { // If ChromeVox is running... that.focusTimeoutID = setTimeout(function () { window.cvox.Api.getCurrentNode(function (node) { if (topli.has(node).length) { // and the current node being voiced is in // the mega menu, clearTimeout, // so the panel stays open. clearTimeout(that.focusTimeoutID); } else { that.focusTimeoutID = setTimeout(function (scope, event, hide) { _togglePanel.call(scope, event, hide); }, 275, that, event, true); } }); }, 25); } else { that.focusTimeoutID = setTimeout(function () { _togglePanel.call(that, event, true); }, 300); } }; /** * @name jQuery.fn.accessibleMegaMenu~_keyDownHandler * @desc Handle keydown event on mega menu. * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _keyDownHandler = function (event) { var target = $($(this).is('.hover:tabbable') ? this : event.target), that = target.is(event.target) ? this : _getPlugin(target), settings = that.settings, menu = that.menu, topnavitems = that.topnavitems, topli = target.closest('.' + settings.topNavItemClass), tabbables = menu.find(':tabbable'), panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass), panelGroups = panel.find('.' + settings.panelGroupClass), currentPanelGroup = target.closest('.' + settings.panelGroupClass), next, keycode = event.keyCode || event.which, start, i, o, label, found = false, newString = Keyboard.keyMap[event.keyCode] || '', regex, isTopNavItem = (topli.length === 1 && panel.length === 0); if (target.is('.hover:tabbable')) { $('html').off('keydown.accessible-megamenu'); } switch (keycode) { case Keyboard.ESCAPE: _togglePanel.call(that, event, true); break; case Keyboard.DOWN: event.preventDefault(); if (isTopNavItem) { _togglePanel.call(that, event); found = (topli.find('.' + settings.panelClass + ' :tabbable:first').focus().length === 1); } else { found = (tabbables.filter(':gt(' + tabbables.index(target) + '):first').focus().length === 1); } if (!found && window.opera && opera.toString() === "[object Opera]" && (event.ctrlKey || event.metaKey)) { tabbables = $(':tabbable'); i = tabbables.index(target); found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1); } break; case Keyboard.UP: event.preventDefault(); if (isTopNavItem && target.hasClass(settings.openClass)) { _togglePanel.call(that, event, true); next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last'); if (next.children('.' + settings.panelClass).length) { found = (next.children() .attr('aria-expanded', 'true') .addClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'false') .find(':tabbable:last') .focus() === 1); } } else if (!isTopNavItem) { found = (tabbables.filter(':lt(' + tabbables.index(target) + '):last').focus().length === 1); } if (!found && window.opera && opera.toString() === "[object Opera]" && (event.ctrlKey || event.metaKey)) { tabbables = $(':tabbable'); i = tabbables.index(target); found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):first').focus().length === 1); } break; case Keyboard.RIGHT: event.preventDefault(); if (isTopNavItem) { found = (topnavitems.filter(':gt(' + topnavitems.index(topli) + '):first').find(':tabbable:first').focus().length === 1); } else { if (panelGroups.length && currentPanelGroup.length) { // if the current panel contains panel groups, and we are able to focus the first tabbable element of the next panel group found = (panelGroups.filter(':gt(' + panelGroups.index(currentPanelGroup) + '):first').find(':tabbable:first').focus().length === 1); } if (!found) { found = (topli.find(':tabbable:first').focus().length === 1); } } break; case Keyboard.LEFT: event.preventDefault(); if (isTopNavItem) { found = (topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last').find(':tabbable:first').focus().length === 1); } else { if (panelGroups.length && currentPanelGroup.length) { // if the current panel contains panel groups, and we are able to focus the first tabbable element of the previous panel group found = (panelGroups.filter(':lt(' + panelGroups.index(currentPanelGroup) + '):last').find(':tabbable:first').focus().length === 1); } if (!found) { found = (topli.find(':tabbable:first').focus().length === 1); } } break; case Keyboard.TAB: i = tabbables.index(target); if (event.shiftKey && isTopNavItem && target.hasClass(settings.openClass)) { _togglePanel(event, true); next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last'); if (next.children('.' + settings.panelClass).length) { found = next.children() .attr('aria-expanded', 'true') .addClass(settings.openClass) .filter('.' + settings.panelClass) .attr('aria-hidden', 'false') .find(':tabbable:last') .focus(); } } else if (event.shiftKey && i > 0) { found = (tabbables.filter(':lt(' + i + '):last').focus().length === 1); } else if (!event.shiftKey && i < tabbables.length - 1) { found = (tabbables.filter(':gt(' + i + '):first').focus().length === 1); } else if (window.opera && opera.toString() === "[object Opera]") { tabbables = $(':tabbable'); i = tabbables.index(target); if (event.shiftKey) { found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):last').focus().length === 1); } else { found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1); } } if (found) { event.preventDefault(); } break; case Keyboard.SPACE: if (isTopNavItem) { event.preventDefault(); _clickHandler.call(that, event); } break; default: // alphanumeric filter clearTimeout(this.keydownTimeoutID); keydownSearchString += newString !== keydownSearchString ? newString : ''; if (keydownSearchString.length === 0) { return; } this.keydownTimeoutID = setTimeout(function () { keydownSearchString = ''; }, keydownTimeoutDuration); if (isTopNavItem && !target.hasClass(settings.openClass)) { tabbables = tabbables.filter('.' + settings.topNavItemClass + ' > :tabbable'); } else { tabbables = topli.find(':tabbable'); } if (event.shiftKey) { tabbables = $(tabbables.get() .reverse()); } for (i = 0; i < tabbables.length; i++) { o = tabbables.eq(i); if (o.is(target)) { start = (keydownSearchString.length === 1) ? i + 1 : i; break; } } regex = new RegExp('^' + keydownSearchString.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'), 'i'); for (i = start; i < tabbables.length; i++) { o = tabbables.eq(i); label = $.trim(o.text()); if (regex.test(label)) { found = true; o.focus(); break; } } if (!found) { for (i = 0; i < start; i++) { o = tabbables.eq(i); label = $.trim(o.text()); if (regex.test(label)) { o.focus(); break; } } } break; } that.justFocused = false; }; /** * @name jQuery.fn.accessibleMegaMenu~_mouseDownHandler * @desc Handle mousedown event on mega menu. * @param {event} Event object * @memberof accessibleMegaMenu * @inner * @private */ _mouseDownHandler = function (event) { this.mouseTimeoutID = setTimeout(function () { clearTimeout(this.focusTimeoutID); }, 1); }; /** * @name jQuery.fn.accessibleMegaMenu~_mouseOverHandler * @desc Handle mouseover event on mega menu. * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _mouseOverHandler = function (event) { clearTimeout(this.mouseTimeoutID); $(event.target) .addClass(this.settings.hoverClass); _togglePanel.call(this, event); if ($(event.target).is(':tabbable')) { $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target)); } }; /** * @name jQuery.fn.accessibleMegaMenu~_mouseOutHandler * @desc Handle mouseout event on mega menu. * @param {event} Event object * @memberof jQuery.fn.accessibleMegaMenu * @inner * @private */ _mouseOutHandler = function (event) { var that = this; $(event.target) .removeClass(that.settings.hoverClass); that.mouseTimeoutID = setTimeout(function () { _togglePanel.call(that, event, true); }, 250); if ($(event.target).is(':tabbable')) { $('html').off('keydown.accessible-megamenu'); } }; _toggleExpandedEventHandlers = function (hide) { var menu = this.menu; if (hide) { $('html').off('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu, pointerup.outside-accessible-megamenu', _clickOutsideHandler); menu.find('[aria-expanded].' + this.settings.panelClass).off('DOMAttrModified.accessible-megamenu', _DOMAttrModifiedHandler); } else { $('html').on('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu, pointerup.outside-accessible-megamenu', $.proxy(_clickOutsideHandler, this)); /* Narrator in Windows 8 automatically toggles the aria-expanded property on double tap or click. To respond to the change to collapse the panel, we must add a listener for a DOMAttrModified event. */ menu.find('[aria-expanded=true].' + this.settings.panelClass).on('DOMAttrModified.accessible-megamenu', $.proxy(_DOMAttrModifiedHandler, this)); } }; /* public attributes and methods ------------------------- */ return { constructor: AccessibleMegaMenu, /** * @lends jQuery.fn.accessibleMegaMenu * @desc Initializes an instance of the accessibleMegaMenu plugins * @memberof jQuery.fn.accessibleMegaMenu * @instance */ init: function () { var that = this, settings = this.settings, justFocused = this.justFocused = false, nav = this.nav = $(this.element), menu = this.menu = nav.children().first(), topnavitems = this.topnavitems = menu.children(); nav.attr("role", "navigation"); menu.addClass(settings.menuClass); topnavitems.each(function (i, topnavitem) { var topnavitemlink, topnavitempanel; topnavitem = $(topnavitem); topnavitem.addClass(settings.topNavItemClass); topnavitemlink = topnavitem.find(":tabbable:first"); topnavitempanel = topnavitem.children(":not(:tabbable):last"); _addUniqueId.call(that, topnavitemlink); if (topnavitempanel.length) { _addUniqueId.call(that, topnavitempanel); topnavitemlink.attr({ "aria-haspopup": true, "aria-owns": topnavitempanel.attr("id"), "aria-controls": topnavitempanel.attr("id"), "aria-expanded": false }); topnavitempanel.attr({ "role": "group", "aria-expanded": false, "aria-hidden": true }) .addClass(settings.panelClass) .not("[aria-labelledby]") .attr("aria-labelledby", topnavitemlink.attr("id")); } }); this.panels = menu.find("." + settings.panelClass); menu.on("focusin.accessible-megamenu", ":tabbable, :focusable, ." + settings.panelClass, $.proxy(_focusInHandler, this)) .on("focusout.accessible-megamenu", ":tabbable, :focusable, ." + settings.panelClass, $.proxy(_focusOutHandler, this)) .on("keydown.accessible-megamenu", $.proxy(_keyDownHandler, this)) .on("mouseover.accessible-megamenu", $.proxy(_mouseOverHandler, this)) .on("mouseout.accessible-megamenu", $.proxy(_mouseOutHandler, this)) .on("mousedown.accessible-megamenu", $.proxy(_mouseDownHandler, this)); if (isTouch) { menu.on("touchstart.accessible-megamenu", $.proxy(_clickHandler, this)); } menu.find("hr").attr("role", "separator"); }, /** * @desc Get default values * @example $(selector).accessibleMegaMenu("getDefaults"); * @return {object} * @memberof jQuery.fn.accessibleMegaMenu * @instance */ getDefaults: function () { return this._defaults; }, /** * @desc Get any option set to plugin using its name (as string) * @example $(selector).accessibleMegaMenu("getOption", some_option); * @param {string} opt * @return {string} * @memberof jQuery.fn.accessibleMegaMenu * @instance */ getOption: function (opt) { return this.settings[opt]; }, /** * @desc Get all options * @example $(selector).accessibleMegaMenu("getAllOptions"); * @return {object} * @memberof jQuery.fn.accessibleMegaMenu * @instance */ getAllOptions: function () { return this.settings; }, /** * @desc Set option * @example $(selector).accessibleMegaMenu("setOption", "option_name", "option_value", reinitialize); * @param {string} opt - Option name * @param {string} val - Option value * @param {boolean} [reinitialize] - boolean to re-initialize the menu. * @memberof jQuery.fn.accessibleMegaMenu * @instance */ setOption: function (opt, value, reinitialize) { this.settings[opt] = value; if (reinitialize) { this.init(); } } }; }()); $.fn[pluginName] = function (options) { return this.each(function () { if (!$.data(this, "plugin_" + pluginName)) { $.data(this, "plugin_" + pluginName, new AccessibleMegaMenu(this, options)); } }); }; /* :focusable and :tabbable selectors from https://raw.github.com/jquery/jquery-ui/master/ui/jquery.ui.core.js */ /** * @private */ function visible(element) { return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () { return $.css(this, "visibility") === "hidden"; }).length; } /** * @private */ function focusable(element, isTabIndexNotNaN) { var map, mapName, img, nodeName = element.nodeName.toLowerCase(); if ("area" === nodeName) { map = element.parentNode; mapName = map.name; if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") { return false; } img = $("img[usemap=#" + mapName + "]")[0]; return !!img && visible(img); } return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled : "a" === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) && // the element and all of its ancestors must be visible visible(element); } $.extend($.expr[":"], { data: $.expr.createPseudo ? $.expr.createPseudo(function (dataName) { return function (elem) { return !!$.data(elem, dataName); }; }) : // support: jQuery <1.8 function (elem, i, match) { return !!$.data(elem, match[3]); }, focusable: function (element) { return focusable(element, !isNaN($.attr(element, "tabindex"))); }, tabbable: function (element) { var tabIndex = $.attr(element, "tabindex"), isTabIndexNaN = isNaN(tabIndex); return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN); } }); }(jQuery, window, document)); // End Adobe's jquery-accessibleMegaMenu