PageSourceSearch

https://www.ephschool.com/apps/js/common/jquery-accessibleMegaMenu.2.js

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