PageSourceSearch

https://jewishitalianmusic.org/thesaurus/application/views/scripts…/vendor/jquery-accessibleMegaMenu.js?v=3.1

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