PageSourceSearch

https://www.heubach.de/site/Heubach-2024/resourceCached/24.2.9/js/…/adobemegamenu/jquery-accessibleMegaMenu.js

js heubach.de collected 2026-10-02 19:02:18 UTC 51,695 bytes, 1,188 lines download raw bytes

1/*
2Copyright © 2013 Adobe Systems Incorporated.
3
4Licensed under the Apache License, Version 2.0 (the “License”);
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an “AS IS” BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17/**
18 * See <a href="http://jquery.com">http://jquery.com</a>.
19 * @name jquery
20 * @class
21 * See the jQuery Library  (<a href="http://jquery.com">http://jquery.com</a>) for full details.  This just
22 * documents the function and classes that are added to jQuery by this plug-in.
23 */
24
25/**
26 * See <a href="http://jquery.com">http://jquery.com</a>
27 * @name fn
28 * @class
29 * See the jQuery Library  (<a href="http://jquery.com">http://jquery.com</a>) for full details.  This just
30 * documents the function and classes that are added to jQuery by this plug-in.
31 * @memberOf jquery
32 */
33
34/**
35 * @fileOverview accessibleMegaMenu plugin
36 *
37 *<p>Licensed under the Apache License, Version 2.0 (the “License”)
38 *<br />Copyright © 2013 Adobe Systems Incorporated.
39 *<br />Project page <a href="https://github.com/adobe-accessibility/Accessible-Mega-Menu">https://github.com/adobe-accessibility/Accessible-Mega-Menu</a>
40 * @version 0.1
41 * @author Michael Jordan
42 * @requires jquery
43 */
44
45/*jslint browser: true, devel: true, plusplus: true, nomen: true */
46/*global jQuery, window, document */
47
48
49
50
51(function ($, window, document) {
52    "use strict";
53    var pluginName = "accessibleMegaMenu",
54        defaults = {
55            uuidPrefix: "accessible-megamenu", // unique ID's are required to indicate aria-owns, aria-controls and aria-labelledby
56            menuClass: "accessible-megamenu", // default css class used to define the megamenu styling
57            topNavItemClass: "accessible-megamenu-top-nav-item", // default css class for a top-level navigation item in the megamenu
58            panelClass: "accessible-megamenu-panel", // default css class for a megamenu panel
59            panelGroupClass: "accessible-megamenu-panel-group", // default css class for a group of items within a megamenu panel
60            hoverClass: "hover", // default css class for the hover state
61            focusClass: "focus", // default css class for the focus state
62            openClass: "open", // default css class for the open state,
63            toggleButtonClass: "accessible-megamenu-toggle", // default css class responsive toggle button
64            openDelay: 0, // default open delay when opening menu via mouseover
65            closeDelay: 0, // default open delay when opening menu via mouseover
66            openOnMouseover: false // default setting for whether menu should open on mouseover
67        },
68        Keyboard = {
69            BACKSPACE: 8,
70            COMMA: 188,
71            DELETE: 46,
72            DOWN: 40,
73            END: 35,
74            ENTER: 13,
75            ESCAPE: 27,
76            HOME: 36,
77            LEFT: 37,
78            PAGE_DOWN: 34,
79            PAGE_UP: 33,
80            PERIOD: 190,
81            RIGHT: 39,
82            SPACE: 32,
83            TAB: 9,
84            UP: 38,
85            keyMap: {
86                48: "0",
87                49: "1",
88                50: "2",
89                51: "3",
90                52: "4",
91                53: "5",
92                54: "6",
93                55: "7",
94                56: "8",
95                57: "9",
96                59: ";",
97                65: "a",
98                66: "b",
99                67: "c",
100                68: "d",
101                69: "e",
102                70: "f",
103                71: "g",
104                72: "h",
105                73: "i",
106                74: "j",
107                75: "k",
108                76: "l",
109                77: "m",
110                78: "n",
111                79: "o",
112                80: "p",
113                81: "q",
114                82: "r",
115                83: "s",
116                84: "t",
117                85: "u",
118                86: "v",
119                87: "w",
120                88: "x",
121                89: "y",
122                90: "z",
123                96: "0",
124                97: "1",
125                98: "2",
126                99: "3",
127                100: "4",
128                101: "5",
129                102: "6",
130                103: "7",
131                104: "8",
132                105: "9",
133                190: "."
134            }
135        },
136        clearTimeout = window.clearTimeout,
137        setTimeout = window.setTimeout,
138        isOpera = window.opera && window.opera.toString() === '[object Opera]';
139    /**
140     * @desc Creates a new accessible mega menu instance.
141     * @param {jquery} element
142     * @param {object} [options] Mega Menu options
143     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
144     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
145     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
146     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
147     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
148     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
149     * @param {string} [options.focusClass=focus] - CSS class for the focus state
150     * @param {string} [options.openClass=open] - CSS class for the open state
151     * @constructor
152     */
153    function AccessibleMegaMenu(element, options) {
154        this.element = element;
155
156        // merge optional settings and defaults into settings
157        this.settings = $.extend({}, defaults, options);
158
159        this._defaults = defaults;
160        this._name = pluginName;
161
162        this.mouseTimeoutID = null;
163        this.focusTimeoutID = null;
164        this.mouseFocused = false;
165        this.justFocused = false;
166
167        this.init();
168    }
169
170    AccessibleMegaMenu.prototype = (function () {
171
172        /* private attributes and methods ------------------------ */
173        var uuid = 0,
174            keydownTimeoutDuration = 500,
175            keydownSearchString = "",
176            isTouch = 'ontouchstart' in window || window.navigator.msMaxTouchPoints,
177            _getPlugin,
178            _addUniqueId,
179            _togglePanel,
180            _clickHandler,
181            _touchmoveHandler,
182            _clickOutsideHandler,
183            _DOMAttrModifiedHandler,
184            _focusInHandler,
185            _focusOutHandler,
186            _keyDownHandler,
187            _mouseDownHandler,
188            _mouseOverHandler,
189            _mouseOutHandler,
190            _clickToggleHandler,
191            _toggleExpandedEventHandlers,
192            _addEventHandlers,
193            _removeEventHandlers;
194
195        /**
196         * @name jQuery.fn.accessibleMegaMenu~_getPlugin
197         * @desc Returns the parent accessibleMegaMenu instance for a given element
198         * @param {jQuery} element
199         * @memberof jQuery.fn.accessibleMegaMenu
200         * @inner
201         * @private
202         */
203        _getPlugin = function (element) {
204            return $(element).closest(':data(plugin_' + pluginName + ')').data("plugin_" + pluginName);
205        };
206
207        /**
208         * @name jQuery.fn.accessibleMegaMenu~_addUniqueId
209         * @desc Adds a unique id and element.
210         * The id string starts with the
211         * string defined in settings.uuidPrefix.
212         * @param {jQuery} element
213         * @memberof jQuery.fn.accessibleMegaMenu
214         * @inner
215         * @private
216         */
217        _addUniqueId = function (element) {
218            element = $(element);
219            var settings = this.settings;
220            if (!element.attr("id")) {
221                element.attr("id", settings.uuidPrefix + "-" + new Date().getTime() + "-" + (++uuid));
222            }
223        };
224
225        /**
226         * @name jQuery.fn.accessibleMegaMenu~_togglePanel
227         * @desc Toggle the display of mega menu panels in response to an event.
228         * The optional boolean value 'hide' forces all panels to hide.
229         * @param {event} event
230         * @param {Boolean} [hide] Hide all mega menu panels when true
231         * @memberof jQuery.fn.accessibleMegaMenu
232         * @inner
233         * @private
234         */
235        _togglePanel = function (event, hide) {
236            // var target = $(event.target),
237            //     that = this,
238            //     settings = this.settings,
239            //     menu = this.menu,
240            //     topli = target.closest('.' + settings.topNavItemClass),
241            //     panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass),
242            //     newfocus;
243
244            // _toggleExpandedEventHandlers.call(this, true);
245
246            // if (hide) {
247            //     jQuery('body').removeClass('menuOpen');
248            //     console.log('leave 22')
249            //     topli = menu.find('.' + settings.topNavItemClass + ' .' + settings.openClass + ':first').closest('.' + settings.topNavItemClass);
250            //     if (!(topli.is(event.relatedTarget) || topli.has(event.relatedTarget).length > 0)) {
251            //         if ((event.type === 'mouseout' || event.type === 'focusout') && topli.has(document.activeElement).length > 0) {
252            //             return;
253            //         }
254            //         topli.find('[aria-expanded]')
255            //             .attr('aria-expanded', 'false')
256            //             .removeClass(settings.openClass)
257            //             .filter('.' + settings.panelClass)
258            //             .attr('aria-hidden', 'true');
259            //         if ((event.type === 'keydown' && event.keyCode === Keyboard.ESCAPE) || event.type === 'DOMAttrModified') {
260            //             newfocus = topli.find(':tabbable:first');
261            //             setTimeout(function () {
262            //                 menu.find('[aria-expanded].' + that.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
263            //                 newfocus.focus();
264            //                 that.justFocused = false;
265            //             }, 99);
266            //         }
267            //     } else if (topli.length === 0) {
268            //         menu.find('[aria-expanded=true]')
269            //             .attr('aria-expanded', 'false')
270            //             .removeClass(settings.openClass)
271            //             .filter('.' + settings.panelClass)
272            //             .attr('aria-hidden', 'true');
273            //     }
274            // } else {
275            //     jQuery('body').addClass('menuOpen');
276            //     clearTimeout(that.focusTimeoutID);
277            //     topli.siblings()
278            //         .find('[aria-expanded]')
279            //         .attr('aria-expanded', 'false')
280            //         .removeClass(settings.openClass)
281            //         .filter('.' + settings.panelClass)
282            //         .attr('aria-hidden', 'true');
283            //     topli.find('[aria-expanded]')
284            //         .attr('aria-expanded', 'true')
285            //         .addClass(settings.openClass)
286            //         .filter('.' + settings.panelClass)
287            //         .attr('aria-hidden', 'false');
288
289            //     var pageScrollPosition = $('html')[0].scrollTop;
290            //     var openPanelTopPosition = $('.' + settings.panelClass + '.' + settings.openClass).parent().offset().top;
291
292            //     if(pageScrollPosition > openPanelTopPosition) {
293            //         $('html')[0].scrollTop = openPanelTopPosition;
294            //     }
295
296            //     if (event.type === 'mouseover' && target.is(':tabbable') && topli.length === 1 && panel.length === 0 && menu.has(document.activeElement).length > 0) {
297            //         target.focus();
298            //         that.justFocused = false;
299            //     }
300
301            //     _toggleExpandedEventHandlers.call(that);
302            // }
303        };
304
305        /**
306         * @name jQuery.fn.accessibleMegaMenu~_clickHandler
307         * @desc Handle click event on mega menu item
308         * @param {event} Event object
309         * @memberof jQuery.fn.accessibleMegaMenu
310         * @inner
311         * @private
312         */
313        _clickHandler = function (event) {
314            var target = $(event.target).closest(':tabbable'),
315                topli = target.closest('.' + this.settings.topNavItemClass),
316                panel = target.closest('.' + this.settings.panelClass);
317            if (topli.length === 1
318                    && panel.length === 0
319                    && topli.find('.' + this.settings.panelClass).length === 1) {
320                if (!target.hasClass(this.settings.openClass)) {
321                    //event.preventDefault();
322                    event.stopPropagation();
323                    //_togglePanel.call(this, event);
324                    this.justFocused = false;
325                } else {
326                    if (this.justFocused) {
327                        //event.preventDefault();
328                        event.stopPropagation();
329                        this.justFocused = false;
330                    } else if (isTouch || !isTouch && !this.settings.openOnMouseover) {
331                        //event.preventDefault();
332                        event.stopPropagation();
333                        //_togglePanel.call(this, event, target.hasClass(this.settings.openClass));
334                    }
335                }
336            }
337        };
338
339        /**
340         * @name jQuery.fn.accessibleMegaMenu~_touchmoveHandler
341         * @desc Handle touch move event on menu
342         * @memberof jQuery.fn.accessibleMegaMenu
343         * @inner
344         * @private
345         */
346        _touchmoveHandler = function () {
347            this.justMoved = true;
348        };
349
350        /**
351         * @name jQuery.fn.accessibleMegaMenu~_clickOutsideHandler
352         * @desc Handle click event outside of a the megamenu
353         * @param {event} Event object
354         * @memberof jQuery.fn.accessibleMegaMenu
355         * @inner
356         * @private
357         */
358        _clickOutsideHandler = function (event) {
359            if ($(event.target).closest(this.menu).length === 0) {
360                event.preventDefault();
361                event.stopPropagation();
362                _togglePanel.call(this, event, true);
363            }
364        };
365
366        /**
367         * @name jQuery.fn.accessibleMegaMenu~_DOMAttrModifiedHandler
368         * @desc Handle DOMAttrModified event on panel to respond to Windows 8 Narrator ExpandCollapse pattern
369         * @param {event} Event object
370         * @memberof jQuery.fn.accessibleMegaMenu
371         * @inner
372         * @private
373         */
374        _DOMAttrModifiedHandler = function (event) {
375            if (event.originalEvent.attrName === 'aria-expanded'
376                    && event.originalEvent.newValue === 'false'
377                    && $(event.target).hasClass(this.settings.openClass)) {
378                event.preventDefault();
379                event.stopPropagation();
380                _togglePanel.call(this, event, true);
381            }
382        };
383
384        /**
385         * @name jQuery.fn.accessibleMegaMenu~_focusInHandler
386         * @desc Handle focusin event on mega menu item.
387         * @param {event} Event object
388         * @memberof jQuery.fn.accessibleMegaMenu
389         * @inner
390         * @private
391         */
392        _focusInHandler = function (event) {
393            clearTimeout(this.focusTimeoutID);
394            var target = $(event.target),
395                panel = target.closest('.' + this.settings.panelClass);
396            target
397                .addClass(this.settings.focusClass);
398            this.justFocused = !this.mouseFocused || (!this.settings.openOnMouseover && this.mouseFocused);
399            this.mouseFocused = false;
400            if (this.justFocused && this.panels.not(panel).filter('.' + this.settings.openClass).length) {
401                _togglePanel.call(this, event);
402            }
403        };
404
405        /**
406         * @name jQuery.fn.accessibleMegaMenu~_focusOutHandler
407         * @desc Handle focusout event on mega menu item.
408         * @param {event} Event object
409         * @memberof jQuery.fn.accessibleMegaMenu
410         * @inner
411         * @private
412         */
413        _focusOutHandler = function (event) {
414            this.justFocused = false;
415            var that = this,
416                target = $(event.target),
417                topli = target.closest('.' + this.settings.topNavItemClass);
418            target
419                .removeClass(this.settings.focusClass);
420
421            if (window.cvox) {
422                // If ChromeVox is running...
423                that.focusTimeoutID = setTimeout(function () {
424                    window.cvox.Api.getCurrentNode(function (node) {
425                        if (topli.has(node).length) {
426                            // and the current node being voiced is in
427                            // the mega menu, clearTimeout,
428                            // so the panel stays open.
429                            clearTimeout(that.focusTimeoutID);
430                        } else {
431                            that.focusTimeoutID = setTimeout(function (scope, event, hide) {
432                                _togglePanel.call(scope, event, hide);
433                            }, 275, that, event, true);
434                        }
435                    });
436                }, 25);
437            } else {
438                that.focusTimeoutID = setTimeout(function () {
439                    if (that.mouseFocused && event.relatedTarget === null) {
440                        return;
441                    }
442                    _togglePanel.call(that, event, true);
443                }, 300);
444            }
445        };
446
447        /**
448         * @name jQuery.fn.accessibleMegaMenu~_keyDownHandler
449         * @desc Handle keydown event on mega menu.
450         * @param {event} Event object
451         * @memberof jQuery.fn.accessibleMegaMenu
452         * @inner
453         * @private
454         */
455        _keyDownHandler = function (event) {
456            var that = (this.constructor === AccessibleMegaMenu) ? this : _getPlugin(this), // determine the AccessibleMegaMenu plugin instance
457                settings = that.settings,
458                target = $($(this).is('.' + settings.hoverClass + ':tabbable') ? this : event.target), // if the element is hovered the target is this, otherwise, its the focused element
459                menu = that.menu,
460                topnavitems = that.topnavitems,
461                topli = target.closest('.' + settings.topNavItemClass),
462                tabbables = menu.find(':tabbable'),
463                panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass),
464                panelGroups = panel.find('.' + settings.panelGroupClass),
465                currentPanelGroup = target.closest('.' + settings.panelGroupClass),
466                next,
467                keycode = event.keyCode || event.which,
468                start,
469                i,
470                o,
471                label,
472                found = false,
473                newString = Keyboard.keyMap[event.keyCode] || '',
474                regex,
475                isTopNavItem = (topli.length === 1 && panel.length === 0);
476
477            if (target.is("input:focus, select:focus, textarea:focus, button:focus")) {
478                // if the event target is a form element we should handle keydown normally
479                return;
480            }
481
482            if (target.is('.' + settings.hoverClass + ':tabbable')) {
483                $('html').off('keydown.accessible-megamenu');
484            }
485
486            switch (keycode) {
487            case Keyboard.ESCAPE:
488                this.mouseFocused = false;
489                _togglePanel.call(that, event, true);
490                break;
491            case Keyboard.DOWN:
492                event.preventDefault();
493                this.mouseFocused = false;
494                if (isTopNavItem) {
495                    _togglePanel.call(that, event);
496                    found = (topli.find('.' + settings.panelClass + ' :tabbable:first').focus().length === 1);
497                } else {
498                    found = (tabbables.filter(':gt(' + tabbables.index(target) + '):first').focus().length === 1);
499                }
500
501                if (!found && isOpera && (event.ctrlKey || event.metaKey)) {
502                    tabbables = $(':tabbable');
503                    i = tabbables.index(target);
504                    found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
505                }
506                break;
507            case Keyboard.UP:
508                event.preventDefault();
509                this.mouseFocused = false;
510                if (isTopNavItem && target.hasClass(settings.openClass)) {
511                    _togglePanel.call(that, event, true);
512                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
513                    if (next.children('.' + settings.panelClass).length) {
514                        found = (next.find('[aria-expanded]')
515                            .attr('aria-expanded', 'true')
516                            .addClass(settings.openClass)
517                            .filter('.' + settings.panelClass)
518                            .attr('aria-hidden', 'false')
519                            .find(':tabbable:last')
520                            .focus() === 1);
521                    }
522                } else if (!isTopNavItem) {
523                    found = (tabbables.filter(':lt(' + tabbables.index(target) + '):last').focus().length === 1);
524                }
525
526                if (!found && isOpera && (event.ctrlKey || event.metaKey)) {
527                    tabbables = $(':tabbable');
528                    i = tabbables.index(target);
529                    found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
530                }
531                break;
532            case Keyboard.RIGHT:
533                event.preventDefault();
534                this.mouseFocused = false;
535                if (isTopNavItem) {
536                    found = (topnavitems.filter(':gt(' + topnavitems.index(topli) + '):first').find(':tabbable:first').focus().length === 1);
537                } else {
538                    if (panelGroups.length && currentPanelGroup.length) {
539                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the next panel group
540                        found = (panelGroups.filter(':gt(' + panelGroups.index(currentPanelGroup) + '):first').find(':tabbable:first').focus().length === 1);
541                    }
542
543                    if (!found) {
544                        found = (topli.find(':tabbable:first').focus().length === 1);
545                    }
546                }
547                break;
548            case Keyboard.LEFT:
549                event.preventDefault();
550                this.mouseFocused = false;
551                if (isTopNavItem) {
552                    found = (topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last').find(':tabbable:first').focus().length === 1);
553                } else {
554                    if (panelGroups.length && currentPanelGroup.length) {
555                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the previous panel group
556                        found = (panelGroups.filter(':lt(' + panelGroups.index(currentPanelGroup) + '):last').find(':tabbable:first').focus().length === 1);
557                    }
558
559                    if (!found) {
560                        found = (topli.find(':tabbable:first').focus().length === 1);
561                    }
562                }
563                break;
564            case Keyboard.TAB:
565                this.mouseFocused = false;
566                i = tabbables.index(target);
567                if (event.shiftKey && isTopNavItem && target.hasClass(settings.openClass)) {
568                    _togglePanel.call(that, event, true);
569                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
570                    if (next.children('.' + settings.panelClass).length) {
571                        found = next.children()
572                            .attr('aria-expanded', 'true')
573                            .addClass(settings.openClass)
574                            .filter('.' + settings.panelClass)
575                            .attr('aria-hidden', 'false')
576                            .find(':tabbable:last')
577                            .focus();
578                    }
579                } else if (event.shiftKey && i > 0) {
580                    found = (tabbables.filter(':lt(' + i + '):last').focus().length === 1);
581                } else if (!event.shiftKey && i < tabbables.length - 1) {
582                    found = (tabbables.filter(':gt(' + i + '):first').focus().length === 1);
583                } else if (isOpera) {
584                    tabbables = $(':tabbable');
585                    i = tabbables.index(target);
586                    if (event.shiftKey) {
587                        found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):last').focus().length === 1);
588                    } else {
589                        found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
590                    }
591                }
592
593                if (found) {
594                    event.preventDefault();
595                }
596                break;
597// wdw 02.07.2018 Enter auf TopNavItem war kaputt				
598            // case Keyboard.SPACE:
599            case Keyboard.ENTER:
600					//                 if (isTopNavItem) {
601					// event.preventDefault();
602					// _clickHandler.call(that, event);
603					//                 } else {
604                    return true;
605                // }
606                break;
607            case Keyboard.SPACE:
608            // case Keyboard.ENTER:
609                if (isTopNavItem) {
610					event.preventDefault();
611					_clickHandler.call(that, event);
612                } else {
613                    return true;
614                }
615                break;
616            default:
617                // alphanumeric filter
618                clearTimeout(this.keydownTimeoutID);
619
620                keydownSearchString += newString !== keydownSearchString ? newString : '';
621
622                if (keydownSearchString.length === 0) {
623                    return;
624                }
625
626                this.keydownTimeoutID = setTimeout(function () {
627                    keydownSearchString = '';
628                }, keydownTimeoutDuration);
629
630                if (isTopNavItem && !target.hasClass(settings.openClass)) {
631                    tabbables = tabbables.filter(':not(.' + settings.panelClass + ' :tabbable)');
632                } else {
633                    tabbables = topli.find(':tabbable');
634                }
635
636                if (event.shiftKey) {
637                    tabbables = $(tabbables.get()
638                        .reverse());
639                }
640
641                for (i = 0; i < tabbables.length; i++) {
642                    o = tabbables.eq(i);
643                    if (o.is(target)) {
644                        start = (keydownSearchString.length === 1) ? i + 1 : i;
645                        break;
646                    }
647                }
648
649                regex = new RegExp('^' + keydownSearchString.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'), 'i');
650
651                for (i = start; i < tabbables.length; i++) {
652                    o = tabbables.eq(i);
653                    label = $.trim(o.text());
654                    if (regex.test(label)) {
655                        found = true;
656                        o.focus();
657                        break;
658                    }
659                }
660                if (!found) {
661                    for (i = 0; i < start; i++) {
662                        o = tabbables.eq(i);
663                        label = $.trim(o.text());
664                        if (regex.test(label)) {
665                            o.focus();
666                            break;
667                        }
668                    }
669                }
670                break;
671            }
672            that.justFocused = false;
673        };
674
675        /**
676         * @name jQuery.fn.accessibleMegaMenu~_mouseDownHandler
677         * @desc Handle mousedown event on mega menu.
678         * @param {event} Event object
679         * @memberof accessibleMegaMenu
680         * @inner
681         * @private
682         */
683        _mouseDownHandler = function (event) {
684            if ($(event.target).closest(this.settings.panelClass) || $(event.target).closest(":focusable").length) {
685                this.mouseFocused = true;
686                if ($(event.target).closest(this.settings.menuClass)) {
687                    $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target));
688                }
689            }
690            clearTimeout(this.mouseTimeoutID);
691            this.mouseTimeoutID = setTimeout(function () {
692                clearTimeout(this.focusTimeoutID);
693            }, 1);
694        };
695
696        /**
697         * @name jQuery.fn.accessibleMegaMenu~_mouseOverHandler
698         * @desc Handle mouseover event on mega menu.
699         * @param {event} Event object
700         * @memberof jQuery.fn.accessibleMegaMenu
701         * @inner
702         * @private
703         */
704        _mouseOverHandler = function (event) {
705            clearTimeout(this.mouseTimeoutID);
706            var that = this;
707
708            if (!that.settings.openOnMouseover) {
709                return;
710            }
711
712            this.mouseTimeoutID = setTimeout(function () {
713                $(event.target).addClass(that.settings.hoverClass);
714                //_togglePanel.call(that, event);
715                if ($(event.target).closest(that.settings.menuClass)) {
716                    $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target));
717                }
718            }, this.settings.openDelay);
719        };
720
721        /**
722         * @name jQuery.fn.accessibleMegaMenu~_mouseOutHandler
723         * @desc Handle mouseout event on mega menu.
724         * @param {event} Event object
725         * @memberof jQuery.fn.accessibleMegaMenu
726         * @inner
727         * @private
728         */
729        _mouseOutHandler = function (event) {
730            clearTimeout(this.mouseTimeoutID);
731            var that = this;
732
733            if (!that.settings.openOnMouseover) {
734                return;
735            }
736
737            $(event.target)
738                .removeClass(that.settings.hoverClass);
739
740            that.mouseTimeoutID = setTimeout(function () {
741               // _togglePanel.call(that, event, true);
742            }, this.settings.closeDelay);
743            if ($(event.target).is(':tabbable')) {
744                $('html').off('keydown.accessible-megamenu');
745            }
746        };
747
748        /**
749         * @name jQuery.fn.accessibleMegaMenu~_clickToggleHandler
750         * @desc Handle click event on menu toggle button.
751         * @memberof jQuery.fn.accessibleMegaMenu
752         * @inner
753         * @private
754         */
755        _clickToggleHandler = function () {
756            var isExpanded = this.toggleButton.attr('aria-expanded') === 'true';
757            this.toggleButton.attr({'aria-expanded': !isExpanded, 'aria-pressed': !isExpanded});
758        };
759
760        _toggleExpandedEventHandlers = function (hide) {
761            var menu = this.menu;
762            if (hide) {
763                $('html').off('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu');
764
765                menu.find('[aria-expanded].' + this.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
766            } else {
767                $('html').on('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu', $.proxy(_clickOutsideHandler, this));
768
769                /* Narrator in Windows 8 automatically toggles the aria-expanded property on double tap or click.
770                   To respond to the change to collapse the panel, we must add a listener for a DOMAttrModified event. */
771                menu.find('[aria-expanded=true].' + this.settings.panelClass).on('DOMAttrModified.accessible-megamenu', $.proxy(_DOMAttrModifiedHandler, this));
772            }
773        };
774
775        _addEventHandlers = function() {
776            var menu = this.menu,
777                toggleButton = this.toggleButton;
778            menu.on("focusin.accessible-megamenu", ":focusable, ." + this.settings.panelClass, $.proxy(_focusInHandler, this))
779                .on("focusout.accessible-megamenu", ":focusable, ." + this.settings.panelClass, $.proxy(_focusOutHandler, this))
780                .on("keydown.accessible-megamenu", $.proxy(_keyDownHandler, this))
781                .on("mouseover.accessible-megamenu", $.proxy(_mouseOverHandler, this))
782                .on("mouseout.accessible-megamenu", $.proxy(_mouseOutHandler, this))
783                .on("mousedown.accessible-megamenu", $.proxy(_mouseDownHandler, this))
784                .on("click.accessible-megamenu", $.proxy(_clickHandler, this));
785
786            toggleButton.on('click.accessible-megamenu', $.proxy(_clickToggleHandler, this));
787
788            if (isTouch) {
789                menu.on("touchmove.accessible-megamenu", $.proxy(_touchmoveHandler, this));
790            }
791
792            if ($(document.activeElement).closest(menu).length) {
793              $(document.activeElement).trigger("focusin.accessible-megamenu");
794            }
795        };
796
797        _removeEventHandlers = function () {
798            var menu = this.menu,
799                toggleButton = this.toggleButton;
800            menu.off('.accessible-megamenu');
801            if (menu.find('[aria-expanded=true].' + this.settings.panelClass).length) {
802                _toggleExpandedEventHandlers.call(this, true);
803            }
804
805            toggleButton.off('.accessible-megamenu');
806        };
807
808        /* public attributes and methods ------------------------- */
809        return {
810            constructor: AccessibleMegaMenu,
811
812            /**
813             * @lends jQuery.fn.accessibleMegaMenu
814             * @desc Initializes an instance of the accessibleMegaMenu plugins
815             * @memberof jQuery.fn.accessibleMegaMenu
816             * @instance
817             */
818            init: function () {
819                var settings = this.settings,
820                    nav = $(this.element),
821                    menu = nav.children('ol,ul').first(),
822                    topnavitems = menu.children(),
823                    toggleButton = nav.children('button').first();
824                this.start(settings, nav, menu, topnavitems, toggleButton);
825            },
826
827            start: function(settings, nav, menu, topnavitems, toggleButton) {
828                var that = this;
829                this.settings = settings;
830                this.menu = menu;
831                this.topnavitems = topnavitems;
832                this.toggleButton = toggleButton;
833
834                nav.attr("role", "navigation");
835                _addUniqueId.call(that, menu);
836                menu.addClass(settings.menuClass);
837                menu.addClass(['js', settings.menuClass].join('-'));
838                topnavitems.each(function (i, topnavitem) {
839                    var topnavitemlink, topnavitempanel;
840                    topnavitem = $(topnavitem);
841                    topnavitem.addClass(settings.topNavItemClass);
842                    topnavitemlink = topnavitem.find(":tabbable:first");
843                    topnavitempanel = topnavitem.children(":not(:tabbable):last");
844                    _addUniqueId.call(that, topnavitemlink);
845                    if (topnavitempanel.length) {
846                        _addUniqueId.call(that, topnavitempanel);
847                        topnavitemlink.attr({
848                            "role": "button",
849                            "aria-controls": topnavitempanel.attr("id"),
850                            "aria-expanded": false,
851                            "tabindex": 0
852                        });
853
854                        topnavitempanel.attr({
855                            "role": "region",
856                            "aria-expanded": false,
857                            "aria-hidden": true
858                        })
859                            .addClass(settings.panelClass)
860                            .not("[aria-labelledby]")
861                            .attr("aria-labelledby", topnavitemlink.attr("id"));
862                    }
863                });
864
865                this.panels = menu.find("." + settings.panelClass);
866
867                menu.find("hr").attr("role", "separator");
868
869                toggleButton.addClass(settings.toggleButtonClass);
870                toggleButton.attr({'aria-expanded': false, 'aria-pressed': false, 'aria-controls': menu.attr('id')});
871
872                _addEventHandlers.call(this);
873            },
874
875            /**
876             * @desc Removes maga menu javascript behavior
877             * @example $(selector).accessibleMegaMenu("destroy");
878             * @return {object}
879             * @memberof jQuery.fn.accessibleMegaMenu
880             * @instance
881             */
882            destroy: function () {
883                this.menu.removeClass(['js', this.settings.menuClass].join('-'));
884                _removeEventHandlers.call(this, true);
885            },
886
887            /**
888             * @desc Get default values
889             * @example $(selector).accessibleMegaMenu("getDefaults");
890             * @return {object}
891             * @memberof jQuery.fn.accessibleMegaMenu
892             * @instance
893             */
894            getDefaults: function () {
895                return this._defaults;
896            },
897
898            /**
899             * @desc Get any option set to plugin using its name (as string)
900             * @example $(selector).accessibleMegaMenu("getOption", some_option);
901             * @param {string} opt
902             * @return {string}
903             * @memberof jQuery.fn.accessibleMegaMenu
904             * @instance
905             */
906            getOption: function (opt) {
907                return this.settings[opt];
908            },
909
910            /**
911             * @desc Get all options
912             * @example $(selector).accessibleMegaMenu("getAllOptions");
913             * @return {object}
914             * @memberof jQuery.fn.accessibleMegaMenu
915             * @instance
916             */
917            getAllOptions: function () {
918                return this.settings;
919            },
920
921            /**
922             * @desc Set option
923             * @example $(selector).accessibleMegaMenu("setOption", "option_name",  "option_value",  reinitialize);
924             * @param {string} opt - Option name
925             * @param {string} val - Option value
926             * @param {boolean} [reinitialize] - boolean to re-initialize the menu.
927             * @memberof jQuery.fn.accessibleMegaMenu
928             * @instance
929             */
930            setOption: function (opt, value, reinitialize) {
931                this.settings[opt] = value;
932                if (reinitialize) {
933                    this.init();
934                }
935            }
936        };
937    }());
938
939    /* lightweight plugin wrapper around the constructor,
940       to prevent against multiple instantiations */
941
942    /**
943     * @class accessibleMegaMenu
944     * @memberOf jQuery.fn
945     * @classdesc Implements an accessible mega menu as a jQuery plugin.
946     * <p>The mega-menu It is modeled after the mega menu on {@link http://adobe.com|adobe.com} but has been simplified for use by others. A brief description of the interaction design choices can be found in a blog post at {@link http://blogs.adobe.com/accessibility/2013/05/adobe-com.html|Mega menu accessibility on adobe.com}.</p>
947     * <h3>Keyboard Accessibility</h3>
948     * <p>The accessible mega menu supports keyboard interaction modeled after the behavior described in the {@link http://www.w3.org/TR/wai-aria-practices/#menu|WAI-ARIA Menu or Menu bar (widget) design pattern}, however we also try to respect users' general expectations for the behavior of links in a global navigation. To this end, the accessible mega menu implementation permits tab focus on each of the six top-level menu items. When one of the menu items has focus, pressing the Enter key, Spacebar or Down arrow will open the submenu panel, and pressing the Left or Right arrow key will shift focus to the adjacent menu item. Links within the submenu panels are included in the tab order when the panel is open. They can also be navigated with the arrow keys or by typing the first character in the link name, which speeds up keyboard navigation considerably. Pressing the Escape key closes the submenu and restores focus to the parent menu item.</p>
949     * <h3>Screen Reader Accessibility</h3>
950     * <p>The accessible mega menu models its use of WAI-ARIA Roles, States, and Properties after those described in the {@link http://www.w3.org/TR/wai-aria-practices/#menu|WAI-ARIA Menu or Menu bar (widget) design pattern} with some notable exceptions, so that it behaves better with screen reader user expectations for global navigation. We don't use <code class="prettyprint prettyprinted" style=""><span class="pln">role</span><span class="pun">=</span><span class="str">"menu"</span></code> for the menu container and <code class="prettyprint prettyprinted" style=""><span class="pln">role</span><span class="pun">=</span><span class="str">"menuitem"</span></code> for each of the links therein, because if we do, assistive technology will no longer interpret the links as links, but instead, as menu items, and the links in our global navigation will no longer show up when a screen reader user executes a shortcut command to bring up a list of links in the page.</p>
951     * @example <h4>HTML</h4><hr/>
952&lt;nav&gt;
953    &lt;ul class=&quot;nav-menu&quot;&gt;
954        &lt;li class=&quot;nav-item&quot;&gt;
955            &lt;a href=&quot;?movie&quot;&gt;Movies&lt;/a&gt;
956            &lt;div class=&quot;sub-nav&quot;&gt;
957                &lt;ul class=&quot;sub-nav-group&quot;&gt;
958                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=0&quot;&gt;Action &amp;amp; Adventure&lt;/a&gt;&lt;/li&gt;
959                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=2&quot;&gt;Children &amp;amp; Family&lt;/a&gt;&lt;/li&gt;
960                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
961                &lt;/ul&gt;
962                &lt;ul class=&quot;sub-nav-group&quot;&gt;
963                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=7&quot;&gt;Dramas&lt;/a&gt;&lt;/li&gt;
964                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=9&quot;&gt;Foreign&lt;/a&gt;&lt;/li&gt;
965                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
966                &lt;/ul&gt;
967                &lt;ul class=&quot;sub-nav-group&quot;&gt;
968                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=14&quot;&gt;Musicals&lt;/a&gt;&lt;/li&gt;
969                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=15&quot;&gt;Romance&lt;/a&gt;&lt;/li&gt;
970                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
971                &lt;/ul&gt;
972            &lt;/div&gt;
973        &lt;/li&gt;
974        &lt;li class=&quot;nav-item&quot;&gt;
975            &lt;a href=&quot;?tv&quot;&gt;TV Shows&lt;/a&gt;
976            &lt;div class=&quot;sub-nav&quot;&gt;
977                &lt;ul class=&quot;sub-nav-group&quot;&gt;
978                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=20&quot;&gt;Classic TV&lt;/a&gt;&lt;/li&gt;
979                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=21&quot;&gt;Crime TV&lt;/a&gt;&lt;/li&gt;
980                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
981                &lt;/ul&gt;
982                &lt;ul class=&quot;sub-nav-group&quot;&gt;
983                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=27&quot;&gt;Reality TV&lt;/a&gt;&lt;/li&gt;
984                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=30&quot;&gt;TV Action&lt;/a&gt;&lt;/li&gt;
985                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
986                &lt;/ul&gt;
987                &lt;ul class=&quot;sub-nav-group&quot;&gt;
988                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=33&quot;&gt;TV Dramas&lt;/a&gt;&lt;/li&gt;
989                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=34&quot;&gt;TV Horror&lt;/a&gt;&lt;/li&gt;
990                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
991                &lt;/ul&gt;
992            &lt;/div&gt;
993        &lt;/li&gt;
994    &lt;/ul&gt;
995&lt;/nav&gt;
996     * @example <h4>CSS</h4><hr/>
997&#47;* Rudimentary mega menu CSS for demonstration *&#47;
998
999&#47;* mega menu list *&#47;
1000.nav-menu {
1001    display: block;
1002    position: relative;
1003    list-style: none;
1004    margin: 0;
1005    padding: 0;
1006    z-index: 15;
1007}
1008
1009&#47;* a top level navigation item in the mega menu *&#47;
1010.nav-item {
1011    list-style: none;
1012    display: inline-block;
1013    padding: 0;
1014    margin: 0;
1015}
1016
1017&#47;* first descendant link within a top level navigation item *&#47;
1018.nav-item &gt; a {
1019    position: relative;
1020    display: inline-block;
1021    padding: 0.5em 1em;
1022    margin: 0 0 -1px 0;
1023    border: 1px solid transparent;
1024}
1025
1026&#47;* focus/open states of first descendant link within a top level
1027   navigation item *&#47;
1028.nav-item &gt; a:focus,
1029.nav-item &gt; a.open {
1030    border: 1px solid #dedede;
1031}
1032
1033&#47;* open state of first descendant link within a top level
1034   navigation item *&#47;
1035.nav-item &gt; a.open {
1036    background-color: #fff;
1037    border-bottom: none;
1038    z-index: 1;
1039}
1040
1041&#47;* sub-navigation panel *&#47;
1042.sub-nav {
1043    position: absolute;
1044    display: none;
1045    top: 2.2em;
1046    margin-top: -1px;
1047    padding: 0.5em 1em;
1048    border: 1px solid #dedede;
1049    background-color: #fff;
1050}
1051
1052&#47;* sub-navigation panel open state *&#47;
1053.sub-nav.open {
1054    display: block;
1055}
1056
1057&#47;* list of items within sub-navigation panel *&#47;
1058.sub-nav ul {
1059    display: inline-block;
1060    vertical-align: top;
1061    margin: 0 1em 0 0;
1062    padding: 0;
1063}
1064
1065&#47;* list item within sub-navigation panel *&#47;
1066.sub-nav li {
1067    display: block;
1068    list-style-type: none;
1069    margin: 0;
1070    padding: 0;
1071}
1072     * @example <h4>JavaScript</h4><hr/>
1073&lt;!-- include jquery --&gt;
1074&lt;script src=&quot;http://code.jquery.com/jquery-1.10.1.min.js&quot;&gt;&lt;/script&gt;
1075
1076&lt;!-- include the jquery-accessibleMegaMenu plugin script --&gt;
1077&lt;script src=&quot;js/jquery-accessibleMegaMenu.js&quot;&gt;&lt;/script&gt;
1078
1079&lt;!-- initialize a selector as an accessibleMegaMenu --&gt;
1080&lt;script&gt;
1081    $(&quot;nav:first&quot;).accessibleMegaMenu({
1082        &#47;* prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby *&#47;
1083        uuidPrefix: &quot;accessible-megamenu&quot;,
1084
1085        &#47;* css class used to define the megamenu styling *&#47;
1086        menuClass: &quot;nav-menu&quot;,
1087
1088        &#47;* css class for a top-level navigation item in the megamenu *&#47;
1089        topNavItemClass: &quot;nav-item&quot;,
1090
1091        &#47;* css class for a megamenu panel *&#47;
1092        panelClass: &quot;sub-nav&quot;,
1093
1094        &#47;* css class for a group of items within a megamenu panel *&#47;
1095        panelGroupClass: &quot;sub-nav-group&quot;,
1096
1097        &#47;* css class for the hover state *&#47;
1098        hoverClass: &quot;hover&quot;,
1099
1100        &#47;* css class for the focus state *&#47;
1101        focusClass: &quot;focus&quot;,
1102
1103        &#47;* css class for the open state *&#47;
1104        openClass: &quot;open&quot;
1105    });
1106&lt;/script&gt;
1107     * @param {object} [options] Mega Menu options
1108     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
1109     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
1110     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
1111     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
1112     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
1113     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
1114     * @param {string} [options.focusClass=focus] - CSS class for the focus state
1115     * @param {string} [options.openClass=open] - CSS class for the open state
1116     * @param {string} [options.openDelay=0] - Open delay when opening menu via mouseover
1117     * @param {string} [options.closeDelay=250] - Open delay when opening menu via mouseover
1118     * @param {boolean} [options.openOnMouseover=false] - Should menu open on mouseover
1119     */
1120    $.fn[pluginName] = function (options) {
1121        return this.each(function () {
1122            var pluginInstance = $.data(this, "plugin_" + pluginName);
1123            if (!pluginInstance) {
1124                $.data(this, "plugin_" + pluginName, new $.fn[pluginName].Acce
1124ssibleMegaMenu(this, options));
1125            } else if (typeof pluginInstance[options] === 'function') {
1126                pluginInstance[options].apply(pluginInstance, Array.prototype.slice.call(arguments, 1));
1127            }
1128        });
1129    };
1130
1131    $.fn[pluginName].AccessibleMegaMenu = AccessibleMegaMenu;
1132
1133    /* :focusable and :tabbable selectors from
1134       https://raw.github.com/jquery/jquery-ui/master/ui/jquery.ui.core.js */
1135
1136    /**
1137     * @private
1138     */
1139    function visible(element) {
1140        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () {
1141            return $.css(this, "visibility") === "hidden";
1142        }).length;
1143    }
1144
1145    /**
1146     * @private
1147     */
1148    function focusable(element, isTabIndexNotNaN) {
1149        var map, mapName, img,
1150            nodeName = element.nodeName.toLowerCase();
1151        if ("area" === nodeName) {
1152            map = element.parentNode;
1153            mapName = map.name;
1154            if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
1155                return false;
1156            }
1157            img = $("img[usemap=#" + mapName + "]")[0];
1158            return !!img && visible(img);
1159        }
1160        return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled :
1161                "a" === nodeName ?
1162                        element.href || isTabIndexNotNaN :
1163                        isTabIndexNotNaN) &&
1164                            // the element and all of its ancestors must be visible
1165                            visible(element);
1166    }
1167
1168    $.extend($.expr[":"], {
1169        data: $.expr.createPseudo ? $.expr.createPseudo(function (dataName) {
1170            return function (elem) {
1171                return !!$.data(elem, dataName);
1172            };
1173        }) : // support: jQuery <1.8
1174                function (elem, i, match) {
1175                    return !!$.data(elem, match[3]);
1176                },
1177
1178        focusable: function (element) {
1179            return focusable(element, !isNaN($.attr(element, "tabindex")));
1180        },
1181
1182        tabbable: function (element) {
1183            var tabIndex = $.attr(element, "tabindex"),
1184                isTabIndexNaN = isNaN(tabIndex);
1185            return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN);
1186        }
1187    });
1188}(jQuery, window, document));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.