PageSourceSearch

https://jamcf.jp/js/jquery-accessibleMegaMenu.js

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