PageSourceSearch

https://www.lflegal.com/wp-content/themes/lflegal-standalone/js/adobe-accessible-menu.js?ver=3.1.6

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