PageSourceSearch

https://abilitynet.org.uk/sites/abilitynet.org.uk/themes/abilityne…/jquery-accessibleMegaMenu.js?tlp6ul

js abilitynet.org.uk collected 2026-09-24 07:31:19 UTC 53,048 bytes, 1,210 lines download raw bytes

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