PageSourceSearch

https://www.banovichhillman.com.au/wp-content/plugins/rexsoftware_…/jquery.smartmenus.min.js?ver=1684192007475

js banovichhillman.com.au collected 2026-10-09 00:58:17 UTC 57,379 bytes, 1,146 lines download raw bytes

1/*!
2 * SmartMenus jQuery Plugin - v0.9.7 - August 25, 2014
3 * http://www.smartmenus.org/
4 *
5 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
6 * http://vadikom.com
7 *
8 * Licensed MIT
9 */
10
11(function($) {
12
13    var menuTrees = [],
14        IE = !!window.createPopup, // detect it for the iframe shim
15        mouse = false, // optimize for touch by default - we will detect for mouse input
16        mouseDetectionEnabled = false;
17
18    // Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
19    function initMouseDetection(disable) {
20        var eNS = '.smartmenus_mouse';
21        if (!mouseDetectionEnabled && !disable) {
22            // if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
23            // in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
24            var firstTime = true,
25                lastMove = null;
26            $(document).bind(getEventsNS([
27                ['mousemove', function(e) {
28                    var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
29                    if (lastMove) {
30                        var deltaX = Math.abs(lastMove.x - thisMove.x),
31                            deltaY = Math.abs(lastMove.y - thisMove.y);
32                        if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
33                            mouse = true;
34                            // if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
35                            if (firstTime) {
36                                var $a = $(e.target).closest('a');
37                                if ($a.is('a')) {
38                                    $.each(menuTrees, function() {
39                                        if ($.contains(this.$root[0], $a[0])) {
40                                            this.itemEnter({ currentTarget: $a[0] });
41                                            return false;
42                                        }
43                                    });
44                                }
45                                firstTime = false;
46                            }
47                        }
48                    }
49                    lastMove = thisMove;
50                }],
51                [touchEvents() ? 'touchstart' : 'pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut', function(e) {
52                    if (isTouchEvent(e.originalEvent)) {
53                        mouse = false;
54                    }
55                }]
56            ], eNS));
57            mouseDetectionEnabled = true;
58        } else if (mouseDetectionEnabled && disable) {
59            $(document).unbind(eNS);
60            mouseDetectionEnabled = false;
61        }
62    }
63
64    function isTouchEvent(e) {
65        return !/^(4|mouse)$/.test(e.pointerType);
66    }
67
68    // we use this just to choose between toucn and pointer events when we need to, not for touch screen detection
69    function touchEvents() {
70        return 'ontouchstart' in window;
71    }
72
73    // returns a jQuery bind() ready object
74    function getEventsNS(defArr, eNS) {
75        if (!eNS) {
76            eNS = '';
77        }
78        var obj = {};
79        $.each(defArr, function(index, value) {
80            obj[value[0].split(' ').join(eNS + ' ') + eNS] = value[1];
81        });
82        return obj;
83    }
84
85    $.SmartMenus = function(elm, options) {
86        this.$root = $(elm);
87        this.opts = options;
88        this.rootId = ''; // internal
89        this.$subArrow = null;
90        this.subMenus = []; // all sub menus in the tree (UL elms) in no particular order (only real - e.g. UL's in mega sub menus won't be counted)
91        this.activatedItems = []; // stores last activated A's for each level
92        this.visibleSubMenus = []; // stores visible sub menus UL's
93        this.showTimeout = 0;
94        this.hideTimeout = 0;
95        this.scrollTimeout = 0;
96        this.clickActivated = false;
97        this.zIndexInc = 0;
98        this.$firstLink = null; // we'll use these for some tests
99        this.$firstSub = null; // at runtime so we'll cache them
100        this.disabled = false;
101        this.$disableOverlay = null;
102        this.isTouchScrolling = false;
103        this.init();
104    };
105
106    $.extend($.SmartMenus, {
107        hideAll: function() {
108            $.each(menuTrees, function() {
109                this.menuHideAll();
110            });
111        },
112        destroy: function() {
113            while (menuTrees.length) {
114                menuTrees[0].destroy();
115            }
116            initMouseDetection(true);
117        },
118        prototype: {
119            init: function(refresh) {
120                var self = this;
121
122                if (!refresh) {
123                    menuTrees.push(this);
124
125                    this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
126
127                    if (this.$root.hasClass('sm-rtl')) {
128                        this.opts.rightToLeftSubMenus = true;
129                    }
130
131                    // init root (main menu)
132                    var eNS = '.smartmenus';
133                    this.$root
134                        .data('smartmenus', this)
135                        .attr('data-smartmenus-id', this.rootId)
136                        .dataSM('level', 1)
137                        .bind(getEventsNS([
138                            ['mouseover focusin', $.proxy(this.rootOver, this)],
139                            ['mouseout focusout', $.proxy(this.rootOut, this)]
140                        ], eNS))
141                        .delegate('a', getEventsNS([
142                            ['mouseenter', $.proxy(this.itemEnter, this)],
143                            ['mouseleave', $.proxy(this.itemLeave, this)],
144                            ['mousedown', $.proxy(this.itemDown, this)],
145                            ['focus', $.proxy(this.itemFocus, this)],
146                            ['blur', $.proxy(this.itemBlur, this)],
147                            ['click', $.proxy(this.itemClick, this)],
148                            ['touchend', $.proxy(this.itemTouchEnd, this)]
149                        ], eNS));
150
151                    // hide menus on tap or click outside the root UL
152                    eNS += this.rootId;
153                    if (this.opts.hideOnClick) {
154                        $(document).bind(getEventsNS([
155                            ['touchstart', $.proxy(this.docTouchStart, this)],
156                            ['touchmove', $.proxy(this.docTouchMove, this)],
157                            ['touchend', $.proxy(this.docTouchEnd, this)],
158                            // for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
159                            ['click', $.proxy(this.docClick, this)]
160                        ], eNS));
161                    }
162                    // hide sub menus on resize
163                    $(window).bind(getEventsNS([['resize orientationchange', $.proxy(this.winResize, this)]], eNS));
164
165                    if (this.opts.subIndicators) {
166                        this.$subArrow = $('<span/>').addClass('sub-arrow');
167                        if (this.opts.subIndicatorsText) {
168                            this.$subArrow.html(this.opts.subIndicatorsText);
169                        }
170                    }
171
172                    // make sure mouse detection is enabled
173                    initMouseDetection();
174                }
175
176                // init sub menus
177                this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
178
179                this.$firstLink = this.$root.find('a').eq(0);
180
181                // find current item
182                if (this.opts.markCurrentItem) {
183                    var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
184                        reHash = /#.*/,
185                        locHref = window.location.href.replace(reDefaultDoc, ''),
186                        locHrefNoHash = locHref.replace(reHash, '');
187                    this.$root.find('a').each(function() {
188                        var href = this.href.replace(reDefaultDoc, ''),
189                            $this = $(this);
190                        if (href == locHref || href == locHrefNoHash) {
191                            $this.addClass('current');
192                            if (self.opts.markCurrentTree) {
193                                $this.parent().parentsUntil('[data-smartmenus-id]', 'li').children('a').addClass('current');
194                            }
195                        }
196                    });
197                }
198            },
199            destroy: function() {
200                this.menuHideAll();
201                var eNS = '.smartmenus';
202                this.$root
203                    .removeData('smartmenus')
204                    .removeAttr('data-smartmenus-id')
205                    .removeDataSM('level')
206                    .unbind(eNS)
207                    .undelegate(eNS);
208                eNS += this.rootId;
209                $(document).unbind(eNS);
210                $(window).unbind(eNS);
211                if (this.opts.subIndicators) {
212                    this.$subArrow = null;
213                }
214                var self = this;
215                $.each(this.subMenus, function() {
216                    if (this.hasClass('mega-menu')) {
217                        this.find('ul').removeDataSM('in-mega');
218                    }
219                    if (this.dataSM('shown-before')) {
220                        if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
221                            this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
222                        }
223                        if (this.dataSM('scroll-arrows')) {
224                            this.dataSM('scroll-arrows').remove();
225                        }
226                        this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
227                    }
228                    if (self.opts.subIndicators) {
229                        this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
230                    }
231                    this.removeDataSM('shown-before')
232                        .removeDataSM('ie-shim')
233                        .removeDataSM('scroll-arrows')
234                        .removeDataSM('parent-a')
235                        .removeDataSM('level')
236                        .removeDataSM('beforefirstshowfired')
237                        .parent().removeDataSM('sub');
238                });
239                if (this.opts.markCurrentItem) {
240                    this.$root.find('a.current').removeClass('current');
241                }
242                this.$root = null;
243                this.$firstLink = null;
244                this.$firstSub = null;
245                if (this.$disableOverlay) {
246                    this.$disableOverlay.remove();
247                    this.$disableOverlay = null;
248                }
249                menuTrees.splice($.inArray(this, menuTrees), 1);
250            },
251            disable: function(noOverlay) {
252                if (!this.disabled) {
253                    this.menuHideAll();
254                    // display overlay over the menu to prevent interaction
255                    if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
256                        var pos = this.$root.offset();
257                        this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
258                            position: 'absolute',
259                            top: pos.top,
260                            left: pos.left,
261                            width: this.$root.outerWidth(),
262                            height: this.$root.outerHeight(),
263                            zIndex: this.getStartZIndex(true),
264                            opacity: 0
265                        }).appendTo(document.body);
266                    }
267                    this.disabled = true;
268                }
269            },
270            docClick: function(e) {
271                if (this.isTouchScrolling) {
272                    this.isTouchScrolling = false;
273                    return;
274                }
275                // hide on any click outside the menu or on a menu link
276                if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
277                    this.menuHideAll();
278                }
279            },
280            docTouchEnd: function(e) {
281                if (!this.lastTouch) {
282                    return;
283                }
284                if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
285                    if (this.hideTimeout) {
286                        clearTimeout(this.hideTimeout);
287                        this.hideTimeout = 0;
288                    }
289                    // hide with a delay to prevent triggering accidental unwanted click on some page element
290                    var self = this;
291                    this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, 350);
292                }
293                this.lastTouch = null;
294            },
295            docTouchMove: function(e) {
296                if (!this.lastTouch) {
297                    return;
298                }
299                var touchPoint = e.originalEvent.touches[0];
300                this.lastTouch.x2 = touchPoint.pageX;
301                this.lastTouch.y2 = touchPoint.pageY;
302            },
303            docTouchStart: function(e) {
304                var touchPoint = e.originalEvent.touches[0];
305                this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
306            },
307            enable: function() {
308                if (this.disabled) {
309                    if (this.$disableOverlay) {
310                        this.$disableOverlay.remove();
311                        this.$disableOverlay = null;
312                    }
313                    this.disabled = false;
314                }
315            },
316            getClosestMenu: function(elm) {
317                var $closestMenu = $(elm).closest('ul');
318                while ($closestMenu.dataSM('in-mega')) {
319                    $closestMenu = $closestMenu.parent().closest('ul');
320                }
321                return $closestMenu[0] || null;
322            },
323            getHeight: function($elm) {
324                return this.getOffset($elm, true);
325            },
326            // returns precise width/height float values
327            getOffset: function($elm, height) {
328                var old;
329                if ($elm.css('display') == 'none') {
330                    old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
331                    $elm.css({ position: 'absolute', visibility: 'hidden' }).show();
332                }
333                var box = $elm[0].getBoundingClientRect && $elm[0].getBoundingClientRect(),
334                    val = box && (height ? box.height || box.bottom - box.top : box.width || box.right - box.left);
335                if (!val && val !== 0) {
336                    val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
337                }
338                if (old) {
339                    $elm.hide().css(old);
340                }
341                return val;
342            },
343            getStartZIndex: function(root) {
344                var zIndex = parseInt(this[root ? '$root' : '$firstSub'].css('z-index'));
345                if (!root && isNaN(zIndex)) {
346                    zIndex = parseInt(this.$root.css('z-index'));
347                }
348                return !isNaN(zIndex) ? zIndex : 1;
349            },
350            getTouchPoint: function(e) {
351                return e.touches && e.touches[0] || e.changedTouches && e.changedTouches[0] || e;
352            },
353            getViewport: function(height) {
354                var name = height ? 'Height' : 'Width',
355                    val = document.documentElement['client' + name],
356                    val2 = window['inner' + name];
357                if (val2) {
358                    val = Math.min(val, val2);
359                }
360                return val;
361            },
362            getViewportHeight: function() {
363                return this.getViewport(true);
364            },
365            getViewportWidth: function() {
366                return this.getViewport();
367            },
368            getWidth: function($elm) {
369                return this.getOffset($elm);
370            },
371            handleEvents: function() {
372                return !this.disabled && this.isCSSOn();
373            },
374            handleItemEvents: function($a) {
375                return this.handleEvents() && !this.isLinkInMegaMenu($a);
376            },
377            isCollapsible: function() {
378                return this.$firstSub.css('position') == 'static';
379            },
380            isCSSOn: function() {
381                return this.$firstLink.css('display') == 'block';
382            },
383            isFixed: function() {
384                var isFixed = this.$root.css('position') == 'fixed';
385                if (!isFixed) {
386                    this.$root.parentsUntil('body').each(function() {
387                        if ($(this).css('position') == 'fixed') {
388                            isFixed = true;
389                            return false;
390                        }
391                    });
392                }
393                return isFixed;
394            },
395            isLinkInMegaMenu: function($a) {
396                return !$a.parent().parent().dataSM('level');
397            },
398            isTouchMode: function() {
399                return !mouse || this.isCollapsible();
400            },
401            itemActivate: function($a) {
402                var $li = $a.parent(),
403                    $ul = $li.parent(),
404                    level = $ul.dataSM('level');
405                // if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
406                if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
407                    var self = this;
408                    $($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
409                        self.itemActivate($(this).dataSM('parent-a'));
410                    });
411                }
412                // hide any visible deeper level sub menus
413                if (this.visibleSubMenus.length > level) {
414                    this.menuHideSubMenus(!this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level);
415                }
416                // save new active item and sub menu for this level
417                this.activatedItems[level - 1] = $a;
418                this.visibleSubMenus[level - 1] = $ul;
419                if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
420                    return;
421                }
422                // show the sub menu if this item has one
423                var $sub = $li.dataSM('sub');
424                if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
425                    this.menuShow($sub);
426                }
427            },
428            itemBlur: function(e) {
429                var $a = $(e.currentTarget);
430                if (!this.handleItemEvents($a)) {
431                    return;
432                }
433                this.$root.triggerHandler('blur.smapi', $a[0]);
434            },
435            itemClick: function(e) {
436                if (this.isTouchScrolling) {
437                    this.isTouchScrolling = false;
438                    e.stopPropagation();
439                    return false;
440                }
441                var $a = $(e.currentTarget);
442                if (!this.handleItemEvents($a)) {
443                    return;
444                }
445                $a.removeDataSM('mousedown');
446                if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
447                    return false;
448                }
449                var $sub = $a.parent().dataSM('sub');
450                if (this.isTouchMode()) {
451                    // undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
452                    if ($a.dataSM('href')) {
453                        $a.attr('href', $a.dataSM('href')).removeDataSM('href');
454                    }
455                    // if the sub is not visible
456                    if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
457                        // try to activate the item and show the sub
458                        this.itemActivate($a);
459                        // if "itemActivate" showed the sub, prevent the click so that the link is not loaded
460                        // if it couldn't show it, then the sub menus are disabled 
460with an !important declaration (e.g. via mobile styles) so let the link get loaded
461                        if ($sub.is(':visible')) {
462                            return false;
463                        }
464                    }
465                } else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
466                    this.clickActivated = true;
467                    this.menuShow($sub);
468                    return false;
469                }
470                if ($a.hasClass('disabled')) {
471                    return false;
472                }
473                if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
474                    return false;
475                }
476            },
477            itemDown: function(e) {
478                var $a = $(e.currentTarget);
479                if (!this.handleItemEvents($a)) {
480                    return;
481                }
482                $a.dataSM('mousedown', true);
483            },
484            itemEnter: function(e) {
485                var $a = $(e.currentTarget);
486                if (!this.handleItemEvents($a)) {
487                    return;
488                }
489                if (!this.isTouchMode()) {
490                    if (this.showTimeout) {
491                        clearTimeout(this.showTimeout);
492                        this.showTimeout = 0;
493                    }
494                    var self = this;
495                    this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
496                }
497                this.$root.triggerHandler('mouseenter.smapi', $a[0]);
498            },
499            itemFocus: function(e) {
500                var $a = $(e.currentTarget);
501                if (!this.handleItemEvents($a)) {
502                    return;
503                }
504                // fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
505                if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
506                    this.itemActivate($a);
507                }
508                this.$root.triggerHandler('focus.smapi', $a[0]);
509            },
510            itemLeave: function(e) {
511                var $a = $(e.currentTarget);
512                if (!this.handleItemEvents($a)) {
513                    return;
514                }
515                if (!this.isTouchMode()) {
516                    if ($a[0].blur) {
517                        $a[0].blur();
518                    }
519                    if (this.showTimeout) {
520                        clearTimeout(this.showTimeout);
521                        this.showTimeout = 0;
522                    }
523                }
524                $a.removeDataSM('mousedown');
525                this.$root.triggerHandler('mouseleave.smapi', $a[0]);
526            },
527            itemTouchEnd: function(e) {
528                var $a = $(e.currentTarget);
529                if (!this.handleItemEvents($a)) {
530                    return;
531                }
532                // prevent the address bar on iPhone from sliding down when expanding a sub menu
533                var $sub = $a.parent().dataSM('sub');
534                if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
535                    $a.dataSM('href', $a.attr('href'));
536                    $a.attr('href', '#');
537                }
538            },
539            menuFixLayout: function($ul) {
540                // fixes a menu that is being shown for the first time
541                if (!$ul.dataSM('shown-before')) {
542                    $ul.hide().dataSM('shown-before', true);
543                }
544            },
545            menuHide: function($sub) {
546                if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
547                    return;
548                }
549                $sub.stop(true, true);
550                if ($sub.is(':visible')) {
551                    var complete = function() {
552                        // unset z-index
553                        $sub.css('z-index', '');
554                    };
555                    // if sub is collapsible (mobile view)
556                    if (this.isCollapsible()) {
557                        if (this.opts.collapsibleHideFunction) {
558                            this.opts.collapsibleHideFunction.call(this, $sub, complete);
559                        } else {
560                            $sub.hide(this.opts.collapsibleHideDuration, complete);
561                        }
562                    } else {
563                        if (this.opts.hideFunction) {
564                            this.opts.hideFunction.call(this, $sub, complete);
565                        } else {
566                            $sub.hide(this.opts.hideDuration, complete);
567                        }
568                    }
569                    // remove IE iframe shim
570                    if ($sub.dataSM('ie-shim')) {
571                        $sub.dataSM('ie-shim').remove();
572                    }
573                    // deactivate scrolling if it is activated for this sub
574                    if ($sub.dataSM('scroll')) {
575                        this.menuScrollStop($sub);
576                        $sub.css({ 'touch-action': '', '-ms-touch-action': '' })
577                            .unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
578                    }
579                    // unhighlight parent item
580                    $sub.dataSM('parent-a').removeClass('highlighted');
581                    var level = $sub.dataSM('level');
582                    this.activatedItems.splice(level - 1, 1);
583                    this.visibleSubMenus.splice(level - 1, 1);
584                    this.$root.triggerHandler('hide.smapi', $sub[0]);
585                }
586            },
587            menuHideAll: function() {
588                if (this.showTimeout) {
589                    clearTimeout(this.showTimeout);
590                    this.showTimeout = 0;
591                }
592                // hide all subs
593                this.menuHideSubMenus();
594                // hide root if it's popup
595                if (this.opts.isPopup) {
596                    this.$root.stop(true, true);
597                    if (this.$root.is(':visible')) {
598                        if (this.opts.hideFunction) {
599                            this.opts.hideFunction.call(this, this.$root);
600                        } else {
601                            this.$root.hide(this.opts.hideDuration);
602                        }
603                        // remove IE iframe shim
604                        if (this.$root.dataSM('ie-shim')) {
605                            this.$root.dataSM('ie-shim').remove();
606                        }
607                    }
608                }
609                this.activatedItems = [];
610                this.visibleSubMenus = [];
611                this.clickActivated = false;
612                // reset z-index increment
613                this.zIndexInc = 0;
614            },
615            menuHideSubMenus: function(level) {
616                if (!level)
617                    level = 0;
618                for (var i = this.visibleSubMenus.length - 1; i > level; i--) {
619                    this.menuHide(this.visibleSubMenus[i]);
620                }
621            },
622            menuIframeShim: function($ul) {
623                // create iframe shim for the menu
624                if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
625                    $ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
626                            .css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
627                    );
628                }
629            },
630            menuInit: function($ul) {
631                if (!$ul.dataSM('in-mega')) {
632                    this.subMenus.push($ul);
633                    // mark UL's in mega drop downs (if any) so we can neglect them
634                    if ($ul.hasClass('mega-menu')) {
635                        $ul.find('ul').dataSM('in-mega', true);
636                    }
637                    // get level (much faster than, for example, using parentsUntil)
638                    var level = 2,
639                        par = $ul[0];
640                    while ((par = par.parentNode.parentNode) != this.$root[0]) {
641                        level++;
642                    }
643                    // cache stuff
644                    $ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
645                        .dataSM('level', level)
646                        .parent().dataSM('sub', $ul);
647                    // add sub indicator to parent item
648                    if (this.opts.subIndicators) {
649                        $ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
650                    }
651                }
652            },
653            menuPosition: function($sub) {
654                var $a = $sub.dataSM('parent-a'),
655                    $ul = $sub.parent().parent(),
656                    level = $sub.dataSM('level'),
657                    subW = this.getWidth($sub),
658                    subH = this.getHeight($sub),
659                    itemOffset = $a.offset(),
660                    itemX = itemOffset.left,
661                    itemY = itemOffset.top,
662                    itemW = this.getWidth($a),
663                    itemH = this.getHeight($a),
664                    $win = $(window),
665                    winX = $win.scrollLeft(),
666                    winY = $win.scrollTop(),
667                    winW = this.getViewportWidth(),
668                    winH = this.getViewportHeight(),
669                    horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
670                    subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
671                    subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
672                    x, y;
673                $sub.removeClass('change-direction');
674                if (horizontalParent) {
675
676                    x = this.opts.rightToLeftSubMenus || ( itemX + subW > winW ) ? itemW - subW - subOffsetX : subOffsetX;
677                    y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
678                } else {
679                    x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
680                    y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
681                }
682                if (this.opts.keepInViewport && !this.isCollapsible()) {
683
684                    var absX = itemX + x,
685                        absY = itemY + y;
686
687                    if(( itemX + subW > winW )) {
688                        $sub.addClass('change-direction');
689                    }
690                    if (this.opts.rightToLeftSubMenus && absX < winX) {
691
692
693                        x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
694                    } else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + winW) {
695                        x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
696                    }
697                    if (!horizontalParent) {
698                        if (subH < winH && absY + subH > winY + winH) {
699                            y += winY + winH - subH - absY;
700                        } else if (subH >= winH || absY < winY) {
701                            y += winY - absY;
702                        }
703                    }
704                    // do we need scrolling?
705                    // 0.49 used for better precision when dealing with float values
706                    if (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49) {
707                        var self = this;
708                        if (!$sub.dataSM('scroll-arrows')) {
709                            $sub.dataSM('scroll-arrows', $([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0], $('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]])
710                                    .bind({
711                                        mouseenter: function() {
712                                            $sub.dataSM('scroll').up = $(this).hasClass('scroll-up');
713                                            self.menuScroll($sub);
714                                        },
715                                        mouseleave: function(e) {
716                                            self.menuScrollStop($sub);
717                                            self.menuScrollOut($sub, e);
718                                        },
719                                        'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
720                                    })
721                                    .insertAfter($sub)
722                            );
723                        }
724                        // bind scroll events and save scroll data for this sub
725                        var eNS = '.smartmenus_scroll';
726                        $sub.dataSM('scroll', {
727                            step: 1,
728                            // cache stuff for faster recalcs later
729                            itemH: itemH,
730                            subH: subH,
731                            arrowDownH: this.getHeight($sub.dataSM('scroll-arrows').eq(1))
732                        })
733                            .bind(getEventsNS([
734                                ['mouseover', function(e) { self.menuScrollOver($sub, e); }],
735                                ['mouseout', function(e) { self.menuScrollOut($sub, e); }],
736                                ['mousewheel DOMMouseScroll', function(e) { self.menuScrollMousewheel($sub, e); }]
737                            ], eNS))
738                            .dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: subW - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: $sub.css('z-index') })
739                            .eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
740                        // when a menu tree is fixed positioned we allow scrolling via touch too
741                        // since there is no other way to access such long sub menus if no mouse is present
742                        if (this.isFixed()) {
743                            $sub.css({ 'touch-action': 'none', '-ms-touch-action': 'none' })
744                                .bind(getEventsNS([
745                                    [touchEvents() ? 'touchstart touchmove touchend' : 'pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp', function(e) {
746                                        self.menuScrollTouch($sub, e);
747                                    }]
748                                ], eNS));
749                        }
750                    }
751                }
752                $sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
753                // IE iframe shim
754                this.menuIframeShim($sub);
755                if ($sub.dataSM('ie-shim')) {
756                    $sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH });
757                }
758            },
759            menuScroll: function($sub, once, step) {
760                var data = $sub.dataSM('scroll'),
761                    $arrows = $sub.dataSM('scroll-arrows'),
762                    y = parseFloat($sub.css('margin-top')),
763                    end = data.up ? data.upEnd : data.downEnd,
764                    diff;
765                if (!once && data.velocity) {
766                    data.velocity *= 0.9;
767                    diff = data.velocity;
768                    if (diff < 0.5) {
769                        this.menuScrollStop($sub);
770                        return;
771                    }
772                } else {
773                    diff = step || (once || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor(data.step));
774                }
775                // hide any visible deeper level sub menus
776                var level = $sub.dataSM('level');
777                if (this.visibleSubMenus.length > level) {
778                    this.menuHideSubMenus(level - 1);
779                }
780                var newY = data.up && end <= y || !data.up && end >= y ? y : (Math.abs(end - y) > diff ? y + (data.up ? diff : -diff) : end);
781                $sub.add($sub.dataSM('ie-shim')).css('margin-top', newY);
782                // show opposite arrow if appropriate
783                if (mouse && (data.up && newY > data.downEnd || !data.up && newY < data.upEnd)) {
784                    $arrows.eq(data.up ? 1 : 0).show();
785                }
786                // if we've reached the end
787                if (newY == end) {
788                    if (mouse) {
789                        $arrows.eq(data.up ? 0 : 1).hide();
790                    }
791                    this.menuScrollStop($sub);
792                } else if (!once) {
793                    if (this.opts.scrollAccelerate && data.step < this.opts.scrollStep) {
794                        data.step += 0.5;
795                    }
796                    var self = this;
797                    this.scrollTimeout = setTimeout(function() { self.menuScroll($sub); }, this.opts.scrollInterval);
798                }
799            },
800            menuScrollMousewheel: function($sub, e) {
801                if (this.getClosestMenu(e.target) == $sub[0]) {
802                    e = e.originalEvent;
803                    var up = (e.wheelDelta || -e.detail) > 0;
804                    if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
805                        $sub.dataSM('scroll').up = up;
806                        this.menuScroll($sub, true);
807                    }
808                }
809                e.preventDefault();
810            },
811            menuScrollOut: function($sub, e) {
812                if (mouse) {
813                    if (!/^scroll-(up|down)/.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || this.getClosestMenu(e.relatedTarget) != $sub[0])) {
814                        $sub.dataSM('scroll-arrows').css('visibility', 'hidden');
815                    }
816                }
817            },
818            menuScrollOver: function($sub, e) {
819                if (mouse) {
820                    if (!/^scroll-(up|down)/.test(e.target.className) && this.getClosestMenu(e.target) == $sub[0]) {
821                        this.menuScrollRefreshData($sub);
822                        var data = $sub.dataSM('scroll');
823                        $sub.dataSM('scroll-arrows').eq(0).css('margin-top', data.upEnd).end()
824                            .eq(1).css('margin-top', data.downEnd + data.subH - data.arrowDownH).end()
825                            .css('visibility', 'visible');
826                    }
827                }
828            },
829            menuScrollRefreshData: function($sub) {
830                var data = $sub.dataSM('scroll'),
831                    $win = $(window),
832                    vportY = $win.scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
833                $.extend(data, {
834                    upEnd: vportY,
835                    downEnd: vportY + this.getViewportHeight() - data.subH
836                });
837            },
838            menuScrollStop: function($sub) {
839                if (this.scrollTimeout) {
840                    clearTimeout(this.scrollTimeout);
841                    this.scrollTimeout = 0;
842                    $.extend($sub.dataSM('scroll'), {
843                        step: 1,
844                        velocity: 0
845                    });
846                    return true;
847                }
848            },
849            menuScrollTouch: function($sub, e) {
850                e = e.originalEvent;
851                if (isTouchEvent(e)) {
852                    var touchPoint = this.getTouchPoint(e);
853                    // neglect event if we touched a visible deeper level sub menu
854                    if (this.getClosestMenu(touchPoint.target) == $sub[0]) {
855                        var data = $sub.dataSM('scroll');
856                        if (/(start|down)$/i.test(e.type)) {
857                            if (this.menuScrollStop($sub)) {
858                                // if we were scrolling, just stop and don't activate any link on the first touch
859                                e.preventDefault();
860                                this.isTouchScrolling = true;
861                            } else {
862                                this.isTouchScrolling = false;
863                            }
864                            // update scroll data since the user might have zoomed, etc.
865                            this.menuScrollRefreshData($sub);
866                            // extend it with the touch properties
867                            $.extend(data, {
868                                touchY: touchPoint.pageY,
869                                touchTimestamp: e.timeStamp,
870                                velocity: 0
871                            });
872                        } else if (/move$/i.test(e.type)) {
873                            var prevY = data.touchY;
874                            if (prevY !== undefined && prevY != touchPoint.pageY) {
875                                this.isTouchScrolling = true;
876                                $.extend(data, {
877                                    up: prevY < touchPoint.pageY,
878                                    touchY: touchPoint.pageY,
879                                    touchTimestamp: e.timeStamp,
880                                    velocity: data.velocity + Math.abs(touchPoint.pageY - prevY) * 0.5
881                                });
882                                this.menuScroll($sub, true, Math.abs(data.touchY - prevY));
883                            }
884                            e.preventDefault();
885                        } else { // touchend/pointerup
886                            if (data.touchY !== undefined) {
887                                // check if we need to scroll
888                                if (e.timeStamp - data.touchTimestamp < 120 && data.velocity > 0) {
889                                    data.velocity *= 0.5;
890                                    this.menuScrollStop($sub);
891                                    this.menuScroll($sub);
892                                    e.preventDefault();
893                                }
894                                delete data.touchY;
895                            }
896                        }
897                    }
898                }
899            },
900            menuShow: function($sub) {
901                if (!$sub.dataSM('beforefirstshowfired')) {
902                    $sub.dataSM('beforefirstshowfired', true);
903                    if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
904                        return;
905                    }
906                }
907                if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
908                    return;
909                }
910                this.menuFixLayout($sub);
911                $sub.stop(true, true);
912                if (!$sub.is(':visible')) {
913                    // set z-index
914                    $sub.css('z-index', this.zIndexInc = (this.zIndexInc || this.get
914StartZIndex()) + 1);
915                    // highlight parent item
916                    if (this.opts.keepHighlighted || this.isCollapsible()) {
917                        $sub.dataSM('parent-a').addClass('highlighted');
918                    }
919                    // min/max-width fix - no way to rely purely on CSS as all UL's are nested
920                    if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
921                        $sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
922                        if (this.opts.subMenusMinWidth) {
923                            $sub.css('min-width', this.opts.subMenusMinWidth);
924                        }
925                        if (this.opts.subMenusMaxWidth) {
926                            var noMaxWidth = this.getWidth($sub);
927                            $sub.css('max-width', this.opts.subMenusMaxWidth);
928                            if (noMaxWidth > this.getWidth($sub)) {
929                                $sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
930                            }
931                        }
932                    }
933                    this.menuPosition($sub);
934                    // insert IE iframe shim
935                    if ($sub.dataSM('ie-shim')) {
936                        $sub.dataSM('ie-shim').insertBefore($sub);
937                    }
938                    var complete = function() {
939                        // fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
940                        $sub.css('overflow', '');
941                    };
942                    // if sub is collapsible (mobile view)
943                    if (this.isCollapsible()) {
944                        if (this.opts.collapsibleShowFunction) {
945                            this.opts.collapsibleShowFunction.call(this, $sub, complete);
946                        } else {
947                            $sub.show(this.opts.collapsibleShowDuration, complete);
948                        }
949                    } else {
950                        if (this.opts.showFunction) {
951                            this.opts.showFunction.call(this, $sub, complete);
952                        } else {
953                            $sub.show(this.opts.showDuration, complete);
954                        }
955                    }
956                    // save new sub menu for this level
957                    this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
958                    this.$root.triggerHandler('show.smapi', $sub[0]);
959                }
960            },
961            popupHide: function(noHideTimeout) {
962                if (this.hideTimeout) {
963                    clearTimeout(this.hideTimeout);
964                    this.hideTimeout = 0;
965                }
966                var self = this;
967                this.hideTimeout = setTimeout(function() {
968                    self.menuHideAll();
969                }, noHideTimeout ? 1 : this.opts.hideTimeout);
970            },
971            popupShow: function(left, top) {
972                if (!this.opts.isPopup) {
973                    alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
974                    return;
975                }
976                if (this.hideTimeout) {
977                    clearTimeout(this.hideTimeout);
978                    this.hideTimeout = 0;
979                }
980                this.menuFixLayout(this.$root);
981                this.$root.stop(true, true);
982                if (!this.$root.is(':visible')) {
983                    this.$root.css({ left: left, top: top });
984                    // IE iframe shim
985                    this.menuIframeShim(this.$root);
986                    if (this.$root.dataSM('ie-shim')) {
987                        this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left: left, top: top }).insertBefore(this.$root);
988                    }
989                    // show menu
990                    var self = this,
991                        complete = function() {
992                            self.$root.css('overflow', '');
993                        };
994                    if (this.opts.showFunction) {
995                        this.opts.showFunction.call(this, this.$root, complete);
996                    } else {
997                        this.$root.show(this.opts.showDuration, complete);
998                    }
999                    this.visibleSubMenus[0] = this.$root;
1000                }
1001            },
1002            refresh: function() {
1003                this.menuHideAll();
1004                this.$root.find('ul').each(function() {
1005                    var $this = $(this);
1006                    if ($this.dataSM('scroll-arrows')) {
1007                        $this.dataSM('scroll-arrows').remove();
1008                    }
1009                })
1010                    .removeDataSM('in-mega')
1011                    .removeDataSM('shown-before')
1012                    .removeDataSM('ie-shim')
1013                    .removeDataSM('scroll-arrows')
1014                    .removeDataSM('parent-a')
1015                    .removeDataSM('level')
1016                    .removeDataSM('beforefirstshowfired');
1017                this.$root.find('a.has-submenu').removeClass('has-submenu')
1018                    .parent().removeDataSM('sub');
1019                if (this.opts.subIndicators) {
1020                    this.$root.find('span.sub-arrow').remove();
1021                }
1022                if (this.opts.markCurrentItem) {
1023                    this.$root.find('a.current').removeClass('current');
1024                }
1025                this.subMenus = [];
1026                this.init(true);
1027            },
1028            rootOut: function(e) {
1029                if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1030                    return;
1031                }
1032                if (this.hideTimeout) {
1033                    clearTimeout(this.hideTimeout);
1034                    this.hideTimeout = 0;
1035                }
1036                if (!this.opts.showOnClick || !this.opts.hideOnClick) {
1037                    var self = this;
1038                    this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
1039                }
1040            },
1041            rootOver: function(e) {
1042                if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
1043                    return;
1044                }
1045                if (this.hideTimeout) {
1046                    clearTimeout(this.hideTimeout);
1047                    this.hideTimeout = 0;
1048                }
1049            },
1050            winResize: function(e) {
1051                if (!this.handleEvents()) {
1052                    // we still need to resize the disable overlay if it's visible
1053                    if (this.$disableOverlay) {
1054                        var pos = this.$root.offset();
1055                        this.$disableOverlay.css({
1056                            top: pos.top,
1057                            left: pos.left,
1058                            width: this.$root.outerWidth(),
1059                            height: this.$root.outerHeight()
1060                        });
1061                    }
1062                    return;
1063                }
1064                // hide sub menus on resize - on mobile do it only on orientation change
1065                if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
1066                    if (this.activatedItems.length) {
1067                        this.activatedItems[this.activatedItems.length - 1][0].blur();
1068                    }
1069                    this.menuHideAll();
1070                }
1071            }
1072        }
1073    });
1074
1075    $.fn.dataSM = function(key, val) {
1076        if (val) {
1077            return this.data(key + '_smartmenus', val);
1078        }
1079        return this.data(key + '_smartmenus');
1080    }
1081
1082    $.fn.removeDataSM = function(key) {
1083        return this.removeData(key + '_smartmenus');
1084    }
1085
1086    $.fn.smartmenus = function(options) {
1087        if (typeof options == 'string') {
1088            var args = arguments,
1089                method = options;
1090            Array.prototype.shift.call(args);
1091            return this.each(function() {
1092                var smartmenus = $(this).data('smartmenus');
1093                if (smartmenus && smartmenus[method]) {
1094                    smartmenus[method].apply(smartmenus, args);
1095                }
1096            });
1097        }
1098        var opts = $.extend({}, $.fn.smartmenus.defaults, options);
1099        return this.each(function() {
1100            new $.SmartMenus(this, opts);
1101        });
1102    }
1103
1104    // default settings
1105    $.fn.smartmenus.defaults = {
1106        isPopup:		false,		// is this a popup menu (can be shown via the popupS
1106how/popupHide methods) or a permanent menu bar
1107        mainMenuSubOffsetX:	0,		// pixels offset from default position
1108        mainMenuSubOffsetY:	0,		// pixels offset from default position
1109        subMenusSubOffsetX:	0,		// pixels offset from default position
1110        subMenusSubOffsetY:	0,		// pixels offset from default position
1111        subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1112        subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1113        subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
1114        subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
1115        subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
1116        scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
1117        scrollInterval:		30,		// interval between each scrolling step
1118        scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
1119        showTimeout:		250,		// timeout before showing the sub menus
1120        hideTimeout:		500,		// timeout before hiding the sub menus
1121        showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
1122        showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
1123        // don't forget to call complete() at the end of whatever you do
1124        // e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
1125        hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
1126        hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
1127        // don't forget to call complete() at the end of whatever you do
1128        // e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
1129        collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
1130        collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
1131        // (i.e. when mobile styles are used to make the sub menus collapsible)
1132        collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
1133        collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
1134        // (i.e. when mobile styles are used to make the sub menus collapsible)
1135        showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
1136        hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
1137        keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
1138        keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
1139        markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
1140        markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
1141        rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
1142        bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
1143        overlapControlsInIE:	true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
1144    };
1145
1146})(jQuery);

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.