1// desktop tab menu Marianne E. 06/23/23 2; (function ($) { 3 'use strict'; 4 5 function DesktopTabMenu(options) { 6 7 this.options = $.extend({ 8 holder: null, 9 items: '.menu-item', 10 links: '>a', 11 delay: 10, 12 activeClass: 'hover', 13 focusAttr: 'data-focused', 14 hasChildrenClass: 'menu-item-has-children', 15 altClass: 'right-position', 16 drop: '> .drop-holder', 17 mainItemClass: 'main-item', 18 }, options); 19 this.init(); 20 } 21 22 DesktopTabMenu.prototype = { 23 init: function () { 24 if (this.options.holder) { 25 this.findElements(); 26 this.attachEvents(); 27 this.makeCallback('onInit', this); 28 } 29 }, 30 findElements: function () { 31 this.holder = $(this.options.holder); 32 this.holder.find('> ' + this.options.items).addClass(this.options.mainItemClass); 33 this.menuItems = this.holder.find(this.options.items); 34 this.menuLinks = this.holder.find(this.options.items + ' ' + this.options.links); 35 this.timer; 36 this.win = jQuery(window); 37 }, 38 openDrop: function (item) { 39 item.addClass(this.options.activeClass); 40 this.makeCallback('onDropOpen', this); 41 }, 42 closeItem: function (menuItem) { 43 menuItem.removeClass(this.options.activeClass); 44 this.makeCallback('onDropClose', this); 45 }, 46 closeDrop: function () { 47 var self = this; 48 49 setTimeout(() => { 50 var opened = self.menuItems 51 .filter('.' + self.options.activeClass) 52 .filter(':not(:has([' + self.options.focusAttr + ']))') 53 .removeClass(self.options.activeClass); 54 55 if (opened.length) { 56 this.makeCallback('onDropClose', this); 57 } 58 59 var menuFocus = self.holder.find('[' + self.options.focusAttr + ']'); 60 if (menuFocus.length === 0) self.menuItems.removeClass(self.options.activeClass); 61 }, 0); 62 }, 63 attachEvents: function () { 64 var self = this; 65 66 this.tabDirectionHandler = function (e) { 67 if (e.which === 9 && e.shiftKey) { 68 self.tabDirection = -1; 69 } else if (e.which === 9) { 70 self.tabDirection = 1; 71 } 72 }; 73 74 this.focusHandler = function (e) { 75 var link = jQuery(e.target).attr(self.options.focusAttr, true); 76 var parent = link.closest(self.options.items); 77 78 if (parent.hasClass(self.options.hasChildrenClass) && parent.hasClass(self.options.mainItemClass)) { 79 self.openDrop(parent); 80 } 81 }; 82 83 this.enterHandler = function (e) { 84 var link = jQuery(this); 85 var parent = link.closest(self.options.items); 86 87 if (e.keyCode === 13 && parent.hasClass(self.options.hasChildrenClass) && !parent.hasClass(self.options.mainItemClass)) { 88 89 e.preventDefault(); 90 e.stopPropagation(); 91 e.returnValue = false; 92 e.cancelBubble = true; 93 self.openDrop(parent); 94 return false; 95 } 96 }; 97 98 this.blurHandler = function (e) { 99 jQuery(this).removeAttr(self.options.focusAttr); 100 101 self.closeDrop(); 102 }; 103 104 this.resizeHandler = function () { 105 clearTimeout(self.timer); 106 self.timer = setTimeout(function () { 107 self.menuItems.filter('.' + self.options.hasChildrenClass).each(function () { 108 var item = jQuery(this); 109 var drop = item.find(self.options.drop); 110 if (!drop.length) return; 111 112 item.removeClass(self.options.altClass); 113 drop.offset().left + drop.outerWidth() > self.win.width() && item.addClass(self.options.altClass); 114 }, 100); 115 }); 116 }; 117 118 this.resizeHandler(); 119 this.menuLinks 120 .on('keyup keydown', this.enterHandler); 121 122 this.win.on('resize orientationchange', this.resizeHandler); 123 }, 124 destroy: function () { 125 this.menuItems 126 .removeClass(this.options.activeClass) 127 .removeClass(this.options.altClass); 128 this.holder.find('[' + this.options.focusAttr + ']').removeAttr(this.options.focusAttr); 129 130 this.menuLinks 131 .off('focus', this.focusHandler) 132 .off('blur', this.blurHandler); 133 this.win.off('resize orientationchange', this.resizeHandler); 134 this.makeCallback('onDestroy', this); 135 }, 136 makeCallback: function (name) { 137 if (typeof this.options[name] === 'function') { 138 var args = Array.prototype.slice.call(arguments); 139 args.shift(); 140 this.options[name].apply(this, args); 141 } 142 } 143 }; 144 145 $.fn.desktopTabMenu = function (options) { 146 return this.each(function () { 147 $(this).data('DesktopTabMenu', new DesktopTabMenu($.extend(options, { holder: this }))); 148 }); 149 }; 150}(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.