PageSourceSearch

https://www.schliengen.de/site/Schliengen-2024/resourceCached/10.4…/adobemegamenu/jquery-accessibleMegaMenu.js

js schliengen.de collected 2026-10-02 18:43:48 UTC 51,461 bytes, 1,187 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: 250, // 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 = 1000,
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                topli = menu.find('.' + settings.topNavItemClass + ' .' + settings.openClass + ':first').closest('.' + settings.topNavItemClass);
249                if (!(topli.is(event.relatedTarget) || topli.has(event.relatedTarget).length > 0)) {
250                    if ((event.type === 'mouseout' || event.type === 'focusout') && topli.has(document.activeElement).length > 0) {
251                        return;
252                    }
253                    topli.find('[aria-expanded]')
254                        .attr('aria-expanded', 'false')
255                        .removeClass(settings.openClass)
256                        .filter('.' + settings.panelClass)
257                        .attr('aria-hidden', 'true');
258                    if ((event.type === 'keydown' && event.keyCode === Keyboard.ESCAPE) || event.type === 'DOMAttrModified') {
259                        newfocus = topli.find(':tabbable:first');
260                        setTimeout(function () {
261                            menu.find('[aria-expanded].' + that.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
262                            newfocus.focus();
263                            that.justFocused = false;
264                        }, 99);
265                    }
266                } else if (topli.length === 0) {
267                    menu.find('[aria-expanded=true]')
268                        .attr('aria-expanded', 'false')
269                        .removeClass(settings.openClass)
270                        .filter('.' + settings.panelClass)
271                        .attr('aria-hidden', 'true');
272                }
273            } else {
274                jQuery('body').addClass('menuOpen');
275                clearTimeout(that.focusTimeoutID);
276                topli.siblings()
277                    .find('[aria-expanded]')
278                    .attr('aria-expanded', 'false')
279                    .removeClass(settings.openClass)
280                    .filter('.' + settings.panelClass)
281                    .attr('aria-hidden', 'true');
282                topli.find('[aria-expanded]')
283                    .attr('aria-expanded', 'true')
284                    .addClass(settings.openClass)
285                    .filter('.' + settings.panelClass)
286                    .attr('aria-hidden', 'false');
287
288                var pageScrollPosition = $('html')[0].scrollTop;
289                var openPanelTopPosition = $('.' + settings.panelClass + '.' + settings.openClass).parent().offset().top;
290
291                if(pageScrollPosition > openPanelTopPosition) {
292                    $('html')[0].scrollTop = openPanelTopPosition;
293                }
294
295                if (event.type === 'mouseover' && target.is(':tabbable') && topli.length === 1 && panel.length === 0 && menu.has(document.activeElement).length > 0) {
296                    target.focus();
297                    that.justFocused = false;
298                }
299
300                _toggleExpandedEventHandlers.call(that);
301            }
302        };
303
304        /**
305         * @name jQuery.fn.accessibleMegaMenu~_clickHandler
306         * @desc Handle click event on mega menu item
307         * @param {event} Event object
308         * @memberof jQuery.fn.accessibleMegaMenu
309         * @inner
310         * @private
311         */
312        _clickHandler = function (event) {
313            var target = $(event.target).closest(':tabbable'),
314                topli = target.closest('.' + this.settings.topNavItemClass),
315                panel = target.closest('.' + this.settings.panelClass);
316            if (topli.length === 1
317                    && panel.length === 0
318                    && topli.find('.' + this.settings.panelClass).length === 1) {
319                if (!target.hasClass(this.settings.openClass)) {
320                    event.preventDefault();
321                    event.stopPropagation();
322                    _togglePanel.call(this, event);
323                    this.justFocused = false;
324                } else {
325                    if (this.justFocused) {
326                        event.preventDefault();
327                        event.stopPropagation();
328                        this.justFocused = false;
329                    } else if (isTouch || !isTouch && !this.settings.openOnMouseover) {
330                        event.preventDefault();
331                        event.stopPropagation();
332                        _togglePanel.call(this, event, target.hasClass(this.settings.openClass));
333                    }
334                }
335            }
336        };
337
338        /**
339         * @name jQuery.fn.accessibleMegaMenu~_touchmoveHandler
340         * @desc Handle touch move event on menu
341         * @memberof jQuery.fn.accessibleMegaMenu
342         * @inner
343         * @private
344         */
345        _touchmoveHandler = function () {
346            this.justMoved = true;
347        };
348
349        /**
350         * @name jQuery.fn.accessibleMegaMenu~_clickOutsideHandler
351         * @desc Handle click event outside of a the megamenu
352         * @param {event} Event object
353         * @memberof jQuery.fn.accessibleMegaMenu
354         * @inner
355         * @private
356         */
357        _clickOutsideHandler = function (event) {
358            if ($(event.target).closest(this.menu).length === 0) {
359                event.preventDefault();
360                event.stopPropagation();
361                _togglePanel.call(this, event, true);
362            }
363        };
364
365        /**
366         * @name jQuery.fn.accessibleMegaMenu~_DOMAttrModifiedHandler
367         * @desc Handle DOMAttrModified event on panel to respond to Windows 8 Narrator ExpandCollapse pattern
368         * @param {event} Event object
369         * @memberof jQuery.fn.accessibleMegaMenu
370         * @inner
371         * @private
372         */
373        _DOMAttrModifiedHandler = function (event) {
374            if (event.originalEvent.attrName === 'aria-expanded'
375                    && event.originalEvent.newValue === 'false'
376                    && $(event.target).hasClass(this.settings.openClass)) {
377                event.preventDefault();
378                event.stopPropagation();
379                _togglePanel.call(this, event, true);
380            }
381        };
382
383        /**
384         * @name jQuery.fn.accessibleMegaMenu~_focusInHandler
385         * @desc Handle focusin event on mega menu item.
386         * @param {event} Event object
387         * @memberof jQuery.fn.accessibleMegaMenu
388         * @inner
389         * @private
390         */
391        _focusInHandler = function (event) {
392            clearTimeout(this.focusTimeoutID);
393            var target = $(event.target),
394                panel = target.closest('.' + this.settings.panelClass);
395            target
396                .addClass(this.settings.focusClass);
397            this.justFocused = !this.mouseFocused || (!this.settings.openOnMouseover && this.mouseFocused);
398            this.mouseFocused = false;
399            if (this.justFocused && this.panels.not(panel).filter('.' + this.settings.openClass).length) {
400                _togglePanel.call(this, event);
401            }
402        };
403
404        /**
405         * @name jQuery.fn.accessibleMegaMenu~_focusOutHandler
406         * @desc Handle focusout event on mega menu item.
407         * @param {event} Event object
408         * @memberof jQuery.fn.accessibleMegaMenu
409         * @inner
410         * @private
411         */
412        _focusOutHandler = function (event) {
413            this.justFocused = false;
414            var that = this,
415                target = $(event.target),
416                topli = target.closest('.' + this.settings.topNavItemClass);
417            target
418                .removeClass(this.settings.focusClass);
419
420            if (window.cvox) {
421                // If ChromeVox is running...
422                that.focusTimeoutID = setTimeout(function () {
423                    window.cvox.Api.getCurrentNode(function (node) {
424                        if (topli.has(node).length) {
425                            // and the current node being voiced is in
426                            // the mega menu, clearTimeout,
427                            // so the panel stays open.
428                            clearTimeout(that.focusTimeoutID);
429                        } else {
430                            that.focusTimeoutID = setTimeout(function (scope, event, hide) {
431                                _togglePanel.call(scope, event, hide);
432                            }, 275, that, event, true);
433                        }
434                    });
435                }, 25);
436            } else {
437                that.focusTimeoutID = setTimeout(function () {
438                    if (that.mouseFocused && event.relatedTarget === null) {
439                        return;
440                    }
441                    _togglePanel.call(that, event, true);
442                }, 300);
443            }
444        };
445
446        /**
447         * @name jQuery.fn.accessibleMegaMenu~_keyDownHandler
448         * @desc Handle keydown event on mega menu.
449         * @param {event} Event object
450         * @memberof jQuery.fn.accessibleMegaMenu
451         * @inner
452         * @private
453         */
454        _keyDownHandler = function (event) {
455            var that = (this.constructor === AccessibleMegaMenu) ? this : _getPlugin(this), // determine the AccessibleMegaMenu plugin instance
456                settings = that.settings,
457                target = $($(this).is('.' + settings.hoverClass + ':tabbable') ? this : event.target), // if the element is hovered the target is this, otherwise, its the focused element
458                menu = that.menu,
459                topnavitems = that.topnavitems,
460                topli = target.closest('.' + settings.topNavItemClass),
461                tabbables = menu.find(':tabbable'),
462                panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass),
463                panelGroups = panel.find('.' + settings.panelGroupClass),
464                currentPanelGroup = target.closest('.' + settings.panelGroupClass),
465                next,
466                keycode = event.keyCode || event.which,
467                start,
468                i,
469                o,
470                label,
471                found = false,
472                newString = Keyboard.keyMap[event.keyCode] || '',
473                regex,
474                isTopNavItem = (topli.length === 1 && panel.length === 0);
475
476            if (target.is("input:focus, select:focus, textarea:focus, button:focus")) {
477                // if the event target is a form element we should handle keydown normally
478                return;
479            }
480
481            if (target.is('.' + settings.hoverClass + ':tabbable')) {
482                $('html').off('keydown.accessible-megamenu');
483            }
484
485            switch (keycode) {
486            case Keyboard.ESCAPE:
487                this.mouseFocused = false;
488                _togglePanel.call(that, event, true);
489                break;
490            case Keyboard.DOWN:
491                event.preventDefault();
492                this.mouseFocused = false;
493                if (isTopNavItem) {
494                    _togglePanel.call(that, event);
495                    found = (topli.find('.' + settings.panelClass + ' :tabbable:first').focus().length === 1);
496                } else {
497                    found = (tabbables.filter(':gt(' + tabbables.index(target) + '):first').focus().length === 1);
498                }
499
500                if (!found && isOpera && (event.ctrlKey || event.metaKey)) {
501                    tabbables = $(':tabbable');
502                    i = tabbables.index(target);
503                    found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
504                }
505                break;
506            case Keyboard.UP:
507                event.preventDefault();
508                this.mouseFocused = false;
509                if (isTopNavItem && target.hasClass(settings.openClass)) {
510                    _togglePanel.call(that, event, true);
511                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
512                    if (next.children('.' + settings.panelClass).length) {
513                        found = (next.find('[aria-expanded]')
514                            .attr('aria-expanded', 'true')
515                            .addClass(settings.openClass)
516                            .filter('.' + settings.panelClass)
517                            .attr('aria-hidden', 'false')
518                            .find(':tabbable:last')
519                            .focus() === 1);
520                    }
521                } else if (!isTopNavItem) {
522                    found = (tabbables.filter(':lt(' + tabbables.index(target) + '):last').focus().length === 1);
523                }
524
525                if (!found && isOpera && (event.ctrlKey || event.metaKey)) {
526                    tabbables = $(':tabbable');
527                    i = tabbables.index(target);
528                    found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
529                }
530                break;
531            case Keyboard.RIGHT:
532                event.preventDefault();
533                this.mouseFocused = false;
534                if (isTopNavItem) {
535                    found = (topnavitems.filter(':gt(' + topnavitems.index(topli) + '):first').find(':tabbable:first').focus().length === 1);
536                } else {
537                    if (panelGroups.length && currentPanelGroup.length) {
538                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the next panel group
539                        found = (panelGroups.filter(':gt(' + panelGroups.index(currentPanelGroup) + '):first').find(':tabbable:first').focus().length === 1);
540                    }
541
542                    if (!found) {
543                        found = (topli.find(':tabbable:first').focus().length === 1);
544                    }
545                }
546                break;
547            case Keyboard.LEFT:
548                event.preventDefault();
549                this.mouseFocused = false;
550                if (isTopNavItem) {
551                    found = (topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last').find(':tabbable:first').focus().length === 1);
552                } else {
553                    if (panelGroups.length && currentPanelGroup.length) {
554                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the previous panel group
555                        found = (panelGroups.filter(':lt(' + panelGroups.index(currentPanelGroup) + '):last').find(':tabbable:first').focus().length === 1);
556                    }
557
558                    if (!found) {
559                        found = (topli.find(':tabbable:first').focus().length === 1);
560                    }
561                }
562                break;
563            case Keyboard.TAB:
564                this.mouseFocused = false;
565                i = tabbables.index(target);
566                if (event.shiftKey && isTopNavItem && target.hasClass(settings.openClass)) {
567                    _togglePanel.call(that, event, true);
568                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
569                    if (next.children('.' + settings.panelClass).length) {
570                        found = next.children()
571                            .attr('aria-expanded', 'true')
572                            .addClass(settings.openClass)
573                            .filter('.' + settings.panelClass)
574                            .attr('aria-hidden', 'false')
575                            .find(':tabbable:last')
576                            .focus();
577                    }
578                } else if (event.shiftKey && i > 0) {
579                    found = (tabbables.filter(':lt(' + i + '):last').focus().length === 1);
580                } else if (!event.shiftKey && i < tabbables.length - 1) {
581                    found = (tabbables.filter(':gt(' + i + '):first').focus().length === 1);
582                } else if (isOpera) {
583                    tabbables = $(':tabbable');
584                    i = tabbables.index(target);
585                    if (event.shiftKey) {
586                        found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):last').focus().length === 1);
587                    } else {
588                        found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
589                    }
590                }
591
592                if (found) {
593                    event.preventDefault();
594                }
595                break;
596// wdw 02.07.2018 Enter auf TopNavItem war kaputt				
597            // case Keyboard.SPACE:
598            case Keyboard.ENTER:
599					//                 if (isTopNavItem) {
600					// event.preventDefault();
601					// _clickHandler.call(that, event);
602					//                 } else {
603                    return true;
604                // }
605                break;
606            case Keyboard.SPACE:
607            // case Keyboard.ENTER:
608                if (isTopNavItem) {
609					event.preventDefault();
610					_clickHandler.call(that, event);
611                } else {
612                    return true;
613                }
614                break;
615            default:
616                // alphanumeric filter
617                clearTimeout(this.keydownTimeoutID);
618
619                keydownSearchString += newString !== keydownSearchString ? newString : '';
620
621                if (keydownSearchString.length === 0) {
622                    return;
623                }
624
625                this.keydownTimeoutID = setTimeout(function () {
626                    keydownSearchString = '';
627                }, keydownTimeoutDuration);
628
629                if (isTopNavItem && !target.hasClass(settings.openClass)) {
630                    tabbables = tabbables.filter(':not(.' + settings.panelClass + ' :tabbable)');
631                } else {
632                    tabbables = topli.find(':tabbable');
633                }
634
635                if (event.shiftKey) {
636                    tabbables = $(tabbables.get()
637                        .reverse());
638                }
639
640                for (i = 0; i < tabbables.length; i++) {
641                    o = tabbables.eq(i);
642                    if (o.is(target)) {
643                        start = (keydownSearchString.length === 1) ? i + 1 : i;
644                        break;
645                    }
646                }
647
648                regex = new RegExp('^' + keydownSearchString.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'), 'i');
649
650                for (i = start; i < tabbables.length; i++) {
651                    o = tabbables.eq(i);
652                    label = $.trim(o.text());
653                    if (regex.test(label)) {
654                        found = true;
655                        o.focus();
656                        break;
657                    }
658                }
659                if (!found) {
660                    for (i = 0; i < start; i++) {
661                        o = tabbables.eq(i);
662                        label = $.trim(o.text());
663                        if (regex.test(label)) {
664                            o.focus();
665                            break;
666                        }
667                    }
668                }
669                break;
670            }
671            that.justFocused = false;
672        };
673
674        /**
675         * @name jQuery.fn.accessibleMegaMenu~_mouseDownHandler
676         * @desc Handle mousedown event on mega menu.
677         * @param {event} Event object
678         * @memberof accessibleMegaMenu
679         * @inner
680         * @private
681         */
682        _mouseDownHandler = function (event) {
683            if ($(event.target).closest(this.settings.panelClass) || $(event.target).closest(":focusable").length) {
684                this.mouseFocused = true;
685                if ($(event.target).closest(this.settings.menuClass)) {
686                    $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target));
687                }
688            }
689            clearTimeout(this.mouseTimeoutID);
690            this.mouseTimeoutID = setTimeout(function () {
691                clearTimeout(this.focusTimeoutID);
692            }, 1);
693        };
694
695        /**
696         * @name jQuery.fn.accessibleMegaMenu~_mouseOverHandler
697         * @desc Handle mouseover event on mega menu.
698         * @param {event} Event object
699         * @memberof jQuery.fn.accessibleMegaMenu
700         * @inner
701         * @private
702         */
703        _mouseOverHandler = function (event) {
704            clearTimeout(this.mouseTimeoutID);
705            var that = this;
706
707            if (!that.settings.openOnMouseover) {
708                return;
709            }
710
711            this.mouseTimeoutID = setTimeout(function () {
712                $(event.target).addClass(that.settings.hoverClass);
713                _togglePanel.call(that, event);
714                if ($(event.target).closest(that.settings.menuClass)) {
715                    $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target));
716                }
717            }, this.settings.openDelay);
718        };
719
720        /**
721         * @name jQuery.fn.accessibleMegaMenu~_mouseOutHandler
722         * @desc Handle mouseout event on mega menu.
723         * @param {event} Event object
724         * @memberof jQuery.fn.accessibleMegaMenu
725         * @inner
726         * @private
727         */
728        _mouseOutHandler = function (event) {
729            clearTimeout(this.mouseTimeoutID);
730            var that = this;
731
732            if (!that.settings.openOnMouseover) {
733                return;
734            }
735
736            $(event.target)
737                .removeClass(that.settings.hoverClass);
738
739            that.mouseTimeoutID = setTimeout(function () {
740                _togglePanel.call(that, event, true);
741            }, this.settings.closeDelay);
742            if ($(event.target).is(':tabbable')) {
743                $('html').off('keydown.accessible-megamenu');
744            }
745        };
746
747        /**
748         * @name jQuery.fn.accessibleMegaMenu~_clickToggleHandler
749         * @desc Handle click event on menu toggle button.
750         * @memberof jQuery.fn.accessibleMegaMenu
751         * @inner
752         * @private
753         */
754        _clickToggleHandler = function () {
755            var isExpanded = this.toggleButton.attr('aria-expanded') === 'true';
756            this.toggleButton.attr({'aria-expanded': !isExpanded, 'aria-pressed': !isExpanded});
757        };
758
759        _toggleExpandedEventHandlers = function (hide) {
760            var menu = this.menu;
761            if (hide) {
762                $('html').off('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu');
763
764                menu.find('[aria-expanded].' + this.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
765            } else {
766                $('html').on('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu', $.proxy(_clickOutsideHandler, this));
767
768                /* Narrator in Windows 8 automatically toggles the aria-expanded property on double tap or click.
769                   To respond to the change to collapse the panel, we must add a listener for a DOMAttrModified event. */
770                menu.find('[aria-expanded=true].' + this.settings.panelClass).on('DOMAttrModified.accessible-megamenu', $.proxy(_DOMAttrModifiedHandler, this));
771            }
772        };
773
774        _addEventHandlers = function() {
775            var menu = this.menu,
776                toggleButton = this.toggleButton;
777            menu.on("focusin.accessible-megamenu", ":focusable, ." + this.settings.panelClass, $.proxy(_focusInHandler, this))
778                .on("focusout.accessible-megamenu", ":focusable, ." + this.settings.panelClass, $.proxy(_focusOutHandler, this))
779                .on("keydown.accessible-megamenu", $.proxy(_keyDownHandler, this))
780                .on("mouseover.accessible-megamenu", $.proxy(_mouseOverHandler, this))
781                .on("mouseout.accessible-megamenu", $.proxy(_mouseOutHandler, this))
782                .on("mousedown.accessible-megamenu", $.proxy(_mouseDownHandler, this))
783                .on("click.accessible-megamenu", $.proxy(_clickHandler, this));
784
785            toggleButton.on('click.accessible-megamenu', $.proxy(_clickToggleHandler, this));
786
787            if (isTouch) {
788                menu.on("touchmove.accessible-megamenu", $.proxy(_touchmoveHandler, this));
789            }
790
791            if ($(document.activeElement).closest(menu).length) {
792              $(document.activeElement).trigger("focusin.accessible-megamenu");
793            }
794        };
795
796        _removeEventHandlers = function () {
797            var menu = this.menu,
798                toggleButton = this.toggleButton;
799            menu.off('.accessible-megamenu');
800            if (menu.find('[aria-expanded=true].' + this.settings.panelClass).length) {
801                _toggleExpandedEventHandlers.call(this, true);
802            }
803
804            toggleButton.off('.accessible-megamenu');
805        };
806
807        /* public attributes and methods ------------------------- */
808        return {
809            constructor: AccessibleMegaMenu,
810
811            /**
812             * @lends jQuery.fn.accessibleMegaMenu
813             * @desc Initializes an instance of the accessibleMegaMenu plugins
814             * @memberof jQuery.fn.accessibleMegaMenu
815             * @instance
816             */
817            init: function () {
818                var settings = this.settings,
819                    nav = $(this.element),
820                    menu = nav.children('ol,ul').first(),
821                    topnavitems = menu.children(),
822                    toggleButton = nav.children('button').first();
823                this.start(settings, nav, menu, topnavitems, toggleButton);
824            },
825
826            start: function(settings, nav, menu, topnavitems, toggleButton) {
827                var that = this;
828                this.settings = settings;
829                this.menu = menu;
830                this.topnavitems = topnavitems;
831                this.toggleButton = toggleButton;
832
833                nav.attr("role", "navigation");
834                _addUniqueId.call(that, menu);
835                menu.addClass(settings.menuClass);
836                menu.addClass(['js', settings.menuClass].join('-'));
837                topnavitems.each(function (i, topnavitem) {
838                    var topnavitemlink, topnavitempanel;
839                    topnavitem = $(topnavitem);
840                    topnavitem.addClass(settings.topNavItemClass);
841                    topnavitemlink = topnavitem.find(":tabbable:first");
842                    topnavitempanel = topnavitem.children(":not(:tabbable):last");
843                    _addUniqueId.call(that, topnavitemlink);
844                    if (topnavitempanel.length) {
845                        _addUniqueId.call(that, topnavitempanel);
846                        topnavitemlink.attr({
847                            "role": "button",
848                            "aria-controls": topnavitempanel.attr("id"),
849                            "aria-expanded": false,
850                            "tabindex": 0
851                        });
852
853                        topnavitempanel.attr({
854                            "role": "region",
855                            "aria-expanded": false,
856                            "aria-hidden": true
857                        })
858                            .addClass(settings.panelClass)
859                            .not("[aria-labelledby]")
860                            .attr("aria-labelledby", topnavitemlink.attr("id"));
861                    }
862                });
863
864                this.panels = menu.find("." + settings.panelClass);
865
866                menu.find("hr").attr("role", "separator");
867
868                toggleButton.addClass(settings.toggleButtonClass);
869                toggleButton.attr({'aria-expanded': false, 'aria-pressed': false, 'aria-controls': menu.attr('id')});
870
871                _addEventHandlers.call(this);
872            },
873
874            /**
875             * @desc Removes maga menu javascript behavior
876             * @example $(selector).accessibleMegaMenu("destroy");
877             * @return {object}
878             * @memberof jQuery.fn.accessibleMegaMenu
879             * @instance
880             */
881            destroy: function () {
882                this.menu.removeClass(['js', this.settings.menuClass].join('-'));
883                _removeEventHandlers.call(this, true);
884            },
885
886            /**
887             * @desc Get default values
888             * @example $(selector).accessibleMegaMenu("getDefaults");
889             * @return {object}
890             * @memberof jQuery.fn.accessibleMegaMenu
891             * @instance
892             */
893            getDefaults: function () {
894                return this._defaults;
895            },
896
897            /**
898             * @desc Get any option set to plugin using its name (as string)
899             * @example $(selector).accessibleMegaMenu("getOption", some_option);
900             * @param {string} opt
901             * @return {string}
902             * @memberof jQuery.fn.accessibleMegaMenu
903             * @instance
904             */
905            getOption: function (opt) {
906                return this.settings[opt];
907            },
908
909            /**
910             * @desc Get all options
911             * @example $(selector).accessibleMegaMenu("getAllOptions");
912             * @return {object}
913             * @memberof jQuery.fn.accessibleMegaMenu
914             * @instance
915             */
916            getAllOptions: function () {
917                return this.settings;
918            },
919
920            /**
921             * @desc Set option
922             * @example $(selector).accessibleMegaMenu("setOption", "option_name",  "option_value",  reinitialize);
923             * @param {string} opt - Option name
924             * @param {string} val - Option value
925             * @param {boolean} [reinitialize] - boolean to re-initialize the menu.
926             * @memberof jQuery.fn.accessibleMegaMenu
927             * @instance
928             */
929            setOption: function (opt, value, reinitialize) {
930                this.settings[opt] = value;
931                if (reinitialize) {
932                    this.init();
933                }
934            }
935        };
936    }());
937
938    /* lightweight plugin wrapper around the constructor,
939       to prevent against multiple instantiations */
940
941    /**
942     * @class accessibleMegaMenu
943     * @memberOf jQuery.fn
944     * @classdesc Implements an accessible mega menu as a jQuery plugin.
945     * <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>
946     * <h3>Keyboard Accessibility</h3>
947     * <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>
948     * <h3>Screen Reader Accessibility</h3>
949     * <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>
950     * @example <h4>HTML</h4><hr/>
951&lt;nav&gt;
952    &lt;ul class=&quot;nav-menu&quot;&gt;
953        &lt;li class=&quot;nav-item&quot;&gt;
954            &lt;a href=&quot;?movie&quot;&gt;Movies&lt;/a&gt;
955            &lt;div class=&quot;sub-nav&quot;&gt;
956                &lt;ul class=&quot;sub-nav-group&quot;&gt;
957                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=0&quot;&gt;Action &amp;amp; Adventure&lt;/a&gt;&lt;/li&gt;
958                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=2&quot;&gt;Children &amp;amp; Family&lt;/a&gt;&lt;/li&gt;
959                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
960                &lt;/ul&gt;
961                &lt;ul class=&quot;sub-nav-group&quot;&gt;
962                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=7&quot;&gt;Dramas&lt;/a&gt;&lt;/li&gt;
963                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=9&quot;&gt;Foreign&lt;/a&gt;&lt;/li&gt;
964                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
965                &lt;/ul&gt;
966                &lt;ul class=&quot;sub-nav-group&quot;&gt;
967                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=14&quot;&gt;Musicals&lt;/a&gt;&lt;/li&gt;
968                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=15&quot;&gt;Romance&lt;/a&gt;&lt;/li&gt;
969                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
970                &lt;/ul&gt;
971            &lt;/div&gt;
972        &lt;/li&gt;
973        &lt;li class=&quot;nav-item&quot;&gt;
974            &lt;a href=&quot;?tv&quot;&gt;TV Shows&lt;/a&gt;
975            &lt;div class=&quot;sub-nav&quot;&gt;
976                &lt;ul class=&quot;sub-nav-group&quot;&gt;
977                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=20&quot;&gt;Classic TV&lt;/a&gt;&lt;/li&gt;
978                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=21&quot;&gt;Crime TV&lt;/a&gt;&lt;/li&gt;
979                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
980                &lt;/ul&gt;
981                &lt;ul class=&quot;sub-nav-group&quot;&gt;
982                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=27&quot;&gt;Reality TV&lt;/a&gt;&lt;/li&gt;
983                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=30&quot;&gt;TV Action&lt;/a&gt;&lt;/li&gt;
984                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
985                &lt;/ul&gt;
986                &lt;ul class=&quot;sub-nav-group&quot;&gt;
987                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=33&quot;&gt;TV Dramas&lt;/a&gt;&lt;/li&gt;
988                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=34&quot;&gt;TV Horror&lt;/a&gt;&lt;/li&gt;
989                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
990                &lt;/ul&gt;
991            &lt;/div&gt;
992        &lt;/li&gt;
993    &lt;/ul&gt;
994&lt;/nav&gt;
995     * @example <h4>CSS</h4><hr/>
996&#47;* Rudimentary mega menu CSS for demonstration *&#47;
997
998&#47;* mega menu list *&#47;
999.nav-menu {
1000    display: block;
1001    position: relative;
1002    list-style: none;
1003    margin: 0;
1004    padding: 0;
1005    z-index: 15;
1006}
1007
1008&#47;* a top level navigation item in the mega menu *&#47;
1009.nav-item {
1010    list-style: none;
1011    display: inline-block;
1012    padding: 0;
1013    margin: 0;
1014}
1015
1016&#47;* first descendant link within a top level navigation item *&#47;
1017.nav-item &gt; a {
1018    position: relative;
1019    display: inline-block;
1020    padding: 0.5em 1em;
1021    margin: 0 0 -1px 0;
1022    border: 1px solid transparent;
1023}
1024
1025&#47;* focus/open states of first descendant link within a top level
1026   navigation item *&#47;
1027.nav-item &gt; a:focus,
1028.nav-item &gt; a.open {
1029    border: 1px solid #dedede;
1030}
1031
1032&#47;* open state of first descendant link within a top level
1033   navigation item *&#47;
1034.nav-item &gt; a.open {
1035    background-color: #fff;
1036    border-bottom: none;
1037    z-index: 1;
1038}
1039
1040&#47;* sub-navigation panel *&#47;
1041.sub-nav {
1042    position: absolute;
1043    display: none;
1044    top: 2.2em;
1045    margin-top: -1px;
1046    padding: 0.5em 1em;
1047    border: 1px solid #dedede;
1048    background-color: #fff;
1049}
1050
1051&#47;* sub-navigation panel open state *&#47;
1052.sub-nav.open {
1053    display: block;
1054}
1055
1056&#47;* list of items within sub-navigation panel *&#47;
1057.sub-nav ul {
1058    display: inline-block;
1059    vertical-align: top;
1060    margin: 0 1em 0 0;
1061    padding: 0;
1062}
1063
1064&#47;* list item within sub-navigation panel *&#47;
1065.sub-nav li {
1066    display: block;
1067    list-style-type: none;
1068    margin: 0;
1069    padding: 0;
1070}
1071     * @example <h4>JavaScript</h4><hr/>
1072&lt;!-- include jquery --&gt;
1073&lt;script src=&quot;http://code.jquery.com/jquery-1.10.1.min.js&quot;&gt;&lt;/script&gt;
1074
1075&lt;!-- include the jquery-accessibleMegaMenu plugin script --&gt;
1076&lt;script src=&quot;js/jquery-accessibleMegaMenu.js&quot;&gt;&lt;/script&gt;
1077
1078&lt;!-- initialize a selector as an accessibleMegaMenu --&gt;
1079&lt;script&gt;
1080    $(&quot;nav:first&quot;).accessibleMegaMenu({
1081        &#47;* prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby *&#47;
1082        uuidPrefix: &quot;accessible-megamenu&quot;,
1083
1084        &#47;* css class used to define the megamenu styling *&#47;
1085        menuClass: &quot;nav-menu&quot;,
1086
1087        &#47;* css class for a top-level navigation item in the megamenu *&#47;
1088        topNavItemClass: &quot;nav-item&quot;,
1089
1090        &#47;* css class for a megamenu panel *&#47;
1091        panelClass: &quot;sub-nav&quot;,
1092
1093        &#47;* css class for a group of items within a megamenu panel *&#47;
1094        panelGroupClass: &quot;sub-nav-group&quot;,
1095
1096        &#47;* css class for the hover state *&#47;
1097        hoverClass: &quot;hover&quot;,
1098
1099        &#47;* css class for the focus state *&#47;
1100        focusClass: &quot;focus&quot;,
1101
1102        &#47;* css class for the open state *&#47;
1103        openClass: &quot;open&quot;
1104    });
1105&lt;/script&gt;
1106     * @param {object} [options] Mega Menu options
1107     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
1108     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
1109     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
1110     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
1111     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
1112     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
1113     * @param {string} [options.focusClass=focus] - CSS class for the focus state
1114     * @param {string} [options.openClass=open] - CSS class for the open state
1115     * @param {string} [options.openDelay=0] - Open delay when opening menu via mouseover
1116     * @param {string} [options.closeDelay=250] - Open delay when opening menu via mouseover
1117     * @param {boolean} [options.openOnMouseover=false] - Should menu open on mouseover
1118     */
1119    $.fn[pluginName] = function (options) {
1120        return this.each(function () {
1121            var pluginInstance = $.data(this, "plugin_" + pluginName);
1122            if (!pluginInstance) {
1123                $.data(this, "plugin_" + pluginName, new $.fn[pluginName].Acce
1123ssibleMegaMenu(this, options));
1124            } else if (typeof pluginInstance[options] === 'function') {
1125                pluginInstance[options].apply(pluginInstance, Array.prototype.slice.call(arguments, 1));
1126            }
1127        });
1128    };
1129
1130    $.fn[pluginName].AccessibleMegaMenu = AccessibleMegaMenu;
1131
1132    /* :focusable and :tabbable selectors from
1133       https://raw.github.com/jquery/jquery-ui/master/ui/jquery.ui.core.js */
1134
1135    /**
1136     * @private
1137     */
1138    function visible(element) {
1139        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () {
1140            return $.css(this, "visibility") === "hidden";
1141        }).length;
1142    }
1143
1144    /**
1145     * @private
1146     */
1147    function focusable(element, isTabIndexNotNaN) {
1148        var map, mapName, img,
1149            nodeName = element.nodeName.toLowerCase();
1150        if ("area" === nodeName) {
1151            map = element.parentNode;
1152            mapName = map.name;
1153            if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
1154                return false;
1155            }
1156            img = $("img[usemap=#" + mapName + "]")[0];
1157            return !!img && visible(img);
1158        }
1159        return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled :
1160                "a" === nodeName ?
1161                        element.href || isTabIndexNotNaN :
1162                        isTabIndexNotNaN) &&
1163                            // the element and all of its ancestors must be visible
1164                            visible(element);
1165    }
1166
1167    $.extend($.expr[":"], {
1168        data: $.expr.createPseudo ? $.expr.createPseudo(function (dataName) {
1169            return function (elem) {
1170                return !!$.data(elem, dataName);
1171            };
1172        }) : // support: jQuery <1.8
1173                function (elem, i, match) {
1174                    return !!$.data(elem, match[3]);
1175                },
1176
1177        focusable: function (element) {
1178            return focusable(element, !isNaN($.attr(element, "tabindex")));
1179        },
1180
1181        tabbable: function (element) {
1182            var tabIndex = $.attr(element, "tabindex"),
1183                isTabIndexNaN = isNaN(tabIndex);
1184            return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN);
1185        }
1186    });
1187}(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.