PageSourceSearch

https://www.chiropracticissafe.org/nyk/gen3/webroot/js/jquery-accessibleMegaMenu.js

js chiropracticissafe.org collected 2026-09-28 14:23:45 UTC 46,318 bytes, 1,066 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 = typeof window.hasOwnProperty === "function" && !!window.hasOwnProperty("ontouchstart"),
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.currentTarget),
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(this.settings.panelClass) || $(event.target).closest(":focusable").length) {
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 settings = this.settings,
707                    nav = $(this.element),
708                    menu = nav.children().first(),
709                    topnavitems = menu.children();
710                this.start(settings, nav, menu, topnavitems);
711            },
712
713            start: function(settings, nav, menu, topnavitems) {
714                var that = this;
715                this.settings = settings;
716                this.menu = menu;
717                this.topnavitems = topnavitems;
718
719                nav.attr("role", "navigation");
720                menu.addClass(settings.menuClass);
721                topnavitems.each(function (i, topnavitem) {
722                    var topnavitemlink, topnavitempanel;
723                    topnavitem = $(topnavitem);
724                    topnavitem.addClass(settings.topNavItemClass);
725                    topnavitemlink = topnavitem.find(":tabbable:first");
726                    topnavitempanel = topnavitem.children(":not(:tabbable):last");
727                    _addUniqueId.call(that, topnavitemlink);
728                    if (topnavitempanel.length) {
729                        _addUniqueId.call(that, topnavitempanel);
730                        topnavitemlink.attr({
731                            "aria-haspopup": true,
732                            "aria-controls": topnavitempanel.attr("id"),
733                            "aria-expanded": false
734                        });
735
736                        topnavitempanel.attr({
737                            "role": "group",
738                            "aria-expanded": false,
739                            "aria-hidden": true
740                        })
741                            .addClass(settings.panelClass)
742                            .not("[aria-labelledby]")
743                            .attr("aria-labelledby", topnavitemlink.attr("id"));
744                    }
745                });
746
747                this.panels = menu.find("." + settings.panelClass);
748
749                menu.on("focusin.accessible-megamenu", ":focusable, ." + settings.panelClass, $.proxy(_focusInHandler, this))
750                    .on("focusout.accessible-megamenu", ":focusable, ." + settings.panelClass, $.proxy(_focusOutHandler, this))
751                    .on("keydown.accessible-megamenu", $.proxy(_keyDownHandler, this))
752                    .on("mouseover.accessible-megamenu", $.proxy(_mouseOverHandler, this))
753                    .on("mouseout.accessible-megamenu", $.proxy(_mouseOutHandler, this))
754                    .on("mousedown.accessible-megamenu", $.proxy(_mouseDownHandler, this));
755
756                if (isTouch) {
757                    menu.on("touchstart.accessible-megamenu",  $.proxy(_clickHandler, this));
758                }
759
760                menu.find("hr").attr("role", "separator");
761
762                if ($(document.activeElement).closest(menu).length) {
763                  $(document.activeElement).trigger("focusin.accessible-megamenu");
764                }
765            },
766
767            /**
768             * @desc Get default values
769             * @example $(selector).accessibleMegaMenu("getDefaults");
770             * @return {object}
771             * @memberof jQuery.fn.accessibleMegaMenu
772             * @instance
773             */
774            getDefaults: function () {
775                return this._defaults;
776            },
777
778            /**
779             * @desc Get any option set to plugin using its name (as string)
780             * @example $(selector).accessibleMegaMenu("getOption", some_option);
781             * @param {string} opt
782             * @return {string}
783             * @memberof jQuery.fn.accessibleMegaMenu
784             * @instance
785             */
786            getOption: function (opt) {
787                return this.settings[opt];
788            },
789
790            /**
791             * @desc Get all options
792             * @example $(selector).accessibleMegaMenu("getAllOptions");
793             * @return {object}
794             * @memberof jQuery.fn.accessibleMegaMenu
795             * @instance
796             */
797            getAllOptions: function () {
798                return this.settings;
799            },
800
801            /**
802             * @desc Set option
803             * @example $(selector).accessibleMegaMenu("setOption", "option_name",  "option_value",  reinitialize);
804             * @param {string} opt - Option name
805             * @param {string} val - Option value
806             * @param {boolean} [reinitialize] - boolean to re-initialize the menu.
807             * @memberof jQuery.fn.accessibleMegaMenu
808             * @instance
809             */
810            setOption: function (opt, value, reinitialize) {
811                this.settings[opt] = value;
812                if (reinitialize) {
813                    this.init();
814                }
815            }
816        };
817    }());
818
819    /* lightweight plugin wrapper around the constructor,
820       to prevent against multiple instantiations */
821
822    /**
823     * @class accessibleMegaMenu
824     * @memberOf jQuery.fn
825     * @classdesc Implements an accessible mega menu as a jQuery plugin.
826     * <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>
827     * <h3>Keyboard Accessibility</h3>
828     * <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>
829     * <h3>Screen Reader Accessibility</h3>
830     * <p>The accessible mega menu models its use of WAI-ARIA Roles, States, and Properties after those described 
830in 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>
831     * @example <h4>HTML</h4><hr/>
832&lt;nav&gt;
833    &lt;ul class=&quot;nav-menu&quot;&gt;
834        &lt;li class=&quot;nav-item&quot;&gt;
835            &lt;a href=&quot;?movie&quot;&gt;Movies&lt;/a&gt;
836            &lt;div class=&quot;sub-nav&quot;&gt;
837                &lt;ul class=&quot;sub-nav-group&quot;&gt;
838                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=0&quot;&gt;Action &amp;amp; Adventure&lt;/a&gt;&lt;/li&gt;
839                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=2&quot;&gt;Children &amp;amp; Family&lt;/a&gt;&lt;/li&gt;
840                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
841                &lt;/ul&gt;
842                &lt;ul class=&quot;sub-nav-group&quot;&gt;
843                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=7&quot;&gt;Dramas&lt;/a&gt;&lt;/li&gt;
844                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=9&quot;&gt;Foreign&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=14&quot;&gt;Musicals&lt;/a&gt;&lt;/li&gt;
849                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=15&quot;&gt;Romance&lt;/a&gt;&lt;/li&gt;
850                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
851                &lt;/ul&gt;
852            &lt;/div&gt;
853        &lt;/li&gt;
854        &lt;li class=&quot;nav-item&quot;&gt;
855            &lt;a href=&quot;?tv&quot;&gt;TV Shows&lt;/a&gt;
856            &lt;div class=&quot;sub-nav&quot;&gt;
857                &lt;ul class=&quot;sub-nav-group&quot;&gt;
858                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=20&quot;&gt;Classic TV&lt;/a&gt;&lt;/li&gt;
859                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=21&quot;&gt;Crime TV&lt;/a&gt;&lt;/li&gt;
860                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
861                &lt;/ul&gt;
862                &lt;ul class=&quot;sub-nav-group&quot;&gt;
863                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=27&quot;&gt;Reality TV&lt;/a&gt;&lt;/li&gt;
864                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=30&quot;&gt;TV Action&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=33&quot;&gt;TV Dramas&lt;/a&gt;&lt;/li&gt;
869                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=34&quot;&gt;TV Horror&lt;/a&gt;&lt;/li&gt;
870                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
871                &lt;/ul&gt;
872            &lt;/div&gt;
873        &lt;/li&gt;
874    &lt;/ul&gt;
875&lt;/nav&gt;
876     * @example <h4>CSS</h4><hr/>
877&#47;* Rudimentary mega menu CSS for demonstration *&#47;
878
879&#47;* mega menu list *&#47;
880.nav-menu {
881    display: block;
882    position: relative;
883    list-style: none;
884    margin: 0;
885    padding: 0;
886    z-index: 15;
887}
888
889&#47;* a top level navigation item in the mega menu *&#47;
890.nav-item {
891    list-style: none;
892    display: inline-block;
893    padding: 0;
894    margin: 0;
895}
896
897&#47;* first descendant link within a top level navigation item *&#47;
898.nav-item &gt; a {
899    position: relative;
900    display: inline-block;
901    padding: 0.5em 1em;
902    margin: 0 0 -1px 0;
903    border: 1px solid transparent;
904}
905
906&#47;* focus/open states of first descendant link within a top level
907   navigation item *&#47;
908.nav-item &gt; a:focus,
909.nav-item &gt; a.open {
910    border: 1px solid #dedede;
911}
912
913&#47;* open state of first descendant link within a top level
914   navigation item *&#47;
915.nav-item &gt; a.open {
916    background-color: #fff;
917    border-bottom: none;
918    z-index: 1;
919}
920
921&#47;* sub-navigation panel *&#47;
922.sub-nav {
923    position: absolute;
924    display: none;
925    top: 2.2em;
926    margin-top: -1px;
927    padding: 0.5em 1em;
928    border: 1px solid #dedede;
929    background-color: #fff;
930}
931
932&#47;* sub-navigation panel open state *&#47;
933.sub-nav.open {
934    display: block;
935}
936
937&#47;* list of items within sub-navigation panel *&#47;
938.sub-nav ul {
939    display: inline-block;
940    vertical-align: top;
941    margin: 0 1em 0 0;
942    padding: 0;
943}
944
945&#47;* list item within sub-navigation panel *&#47;
946.sub-nav li {
947    display: block;
948    list-style-type: none;
949    margin: 0;
950    padding: 0;
951}
952     * @example <h4>JavaScript</h4><hr/>
953&lt;!-- include jquery --&gt;
954&lt;script src=&quot;http://code.jquery.com/jquery-1.10.1.min.js&quot;&gt;&lt;/script&gt;
955
956&lt;!-- include the jquery-accessibleMegaMenu plugin script --&gt;
957&lt;script src=&quot;js/jquery-accessibleMegaMenu.js&quot;&gt;&lt;/script&gt;
958
959&lt;!-- initialize a selector as an accessibleMegaMenu --&gt;
960&lt;script&gt;
961    $(&quot;nav:first&quot;).accessibleMegaMenu({
962        &#47;* prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby *&#47;
963        uuidPrefix: &quot;accessible-megamenu&quot;,
964
965        &#47;* css class used to define the megamenu styling *&#47;
966        menuClass: &quot;nav-menu&quot;,
967
968        &#47;* css class for a top-level navigation item in the megamenu *&#47;
969        topNavItemClass: &quot;nav-item&quot;,
970
971        &#47;* css class for a megamenu panel *&#47;
972        panelClass: &quot;sub-nav&quot;,
973
974        &#47;* css class for a group of items within a megamenu panel *&#47;
975        panelGroupClass: &quot;sub-nav-group&quot;,
976
977        &#47;* css class for the hover state *&#47;
978        hoverClass: &quot;hover&quot;,
979
980        &#47;* css class for the focus state *&#47;
981        focusClass: &quot;focus&quot;,
982
983        &#47;* css class for the open state *&#47;
984        openClass: &quot;open&quot;
985    });
986&lt;/script&gt;
987     * @param {object} [options] Mega Menu options
988     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
989     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
990     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
991     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
992     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
993     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
994     * @param {string} [options.focusClass=focus] - CSS class for the focus state
995     * @param {string} [options.openClass=open] - CSS class for the open state
996     */
997    $.fn[pluginName] = function (options) {
998        return this.each(function () {
999            if (!$.data(this, "plugin_" + pluginName)) {
1000                $.data(this, "plugin_" + pluginName, new $.fn[pluginName].Acce
vendor: 2,234 bytes, lines 1000-1066
1000ssibleMegaMenu(this, options));
1001            }
1002        });
1003    };
1004
1005    $.fn[pluginName].AccessibleMegaMenu = AccessibleMegaMenu;
1006
1007    /* :focusable and :tabbable selectors from
1008       https://raw.github.com/jquery/jquery-ui/master/ui/jquery.ui.core.js */
1009
1010    /**
1011     * @private
1012     */
1013    function visible(element) {
1014        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () {
1015            return $.css(this, "visibility") === "hidden";
1016        }).length;
1017    }
1018
1019    /**
1020     * @private
1021     */
1022    function focusable(element, isTabIndexNotNaN) {
1023        var map, mapName, img,
1024            nodeName = element.nodeName.toLowerCase();
1025        if ("area" === nodeName) {
1026            map = element.parentNode;
1027            mapName = map.name;
1028            if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
1029                return false;
1030            }
1031            img = $("img[usemap=#" + mapName + "]")[0];
1032            return !!img && visible(img);
1033        }
1034        return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled :
1035                "a" === nodeName ?
1036                        element.href || isTabIndexNotNaN :
1037                        isTabIndexNotNaN) &&
1038                            // the element and all of its ancestors must be visible
1039                            visible(element);
1040    }
1041
1042    $.extend($.expr[":"], {
1043        data: $.expr.createPseudo ? $.expr.createPseudo(function (dataName) {
1044            return function (elem) {
1045                return !!$.data(elem, dataName);
1046            };
1047        }) : // support: jQuery <1.8
1048                function (elem, i, match) {
1049                    return !!$.data(elem, match[3]);
1050                },
1051
1052        focusable: function (element) {
1053            return focusable(element, !isNaN($.attr(element, "tabindex")));
1054        },
1055
1056        tabbable: function (element) {
1057            var tabIndex = $.attr(element, "tabindex"),
1058                isTabIndexNaN = isNaN(tabIndex);
1059            return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN);
1060        }
1061    });
1062}(jQuery, window, document));
1063
1064$(document).ready(function() {
1065    $("nav:first").accessibleMegaMenu();
1066});

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.