1/*! 2 * SmartMenus jQuery Plugin Keyboard Addon - v0.4.0 - September 17, 2017 3 * http://www.smartmenus.org/ 4 * 5 * Copyright Vasil Dinkov, Vadikom Web Ltd. 6 * http://vadikom.com 7 * 8 * Licensed MIT 9 */ 10 11(function(factory) { 12 if (typeof define === 'function' && define.amd) { 13 // AMD 14 define(['jquery', 'smartmenus'], factory); 15 } else if (typeof module === 'object' && typeof module.exports === 'object') { 16 // CommonJS 17 module.exports = factory(require('jquery')); 18 } else { 19 // Global jQuery 20 factory(jQuery); 21 } 22} (function($) { 23 24 function getFirstItemLink($ul) { 25 // make sure we also allow the link to be nested deeper inside the LI's (e.g. in a heading) 26 return $ul.find('> li > a:not(.disabled), > li > :not(ul) a:not(.disabled)').eq(0); 27 } 28 function getLastItemLink($ul) { 29 return $ul.find('> li > a:not(.disabled), > li > :not(ul) a:not(.disabled)').eq(-1); 30 } 31 function getNextItemLink($li, noLoop) { 32 var $a = $li.nextAll('li').find('> a:not(.disabled), > :not(ul) a:not(.disabled)').eq(0); 33 return noLoop || $a.length ? $a : getFirstItemLink($li.parent()); 34 } 35 function getPreviousItemLink($li, noLoop) { 36 // bug workaround: elements are returned in reverse order just in jQuery 1.8.x 37 var $a = $li.prevAll('li').find('> a:not(.disabled), > :not(ul) a:not(.disabled)').eq(/^1\.8\./.test($.fn.jquery) ? 0 : -1); 38 return noLoop || $a.length ? $a : getLastItemLink($li.parent()); 39 } 40 41 // jQuery's .focus() is unreliable in some versions, so we're going to call the links' native JS focus method 42 $.fn.focusSM = function() { 43 if (this.length && this[0].focus) { 44 this[0].focus(); 45 } 46 return this; 47 }; 48 49 $.extend($.SmartMenus.Keyboard = {}, { 50 docKeydown: function(e) { 51 var keyCode = e.keyCode; 52 if (!/^(37|38|39|40)$/.test(keyCode)) { 53 return; 54 } 55 var $root = $(this), 56 obj = $root.data('smartmenus'), 57 $target = $(e.target); 58 // exit if this is an A inside a mega drop-down 59 if (!obj || !$target.is('a') || !obj.handleItemEvents($target)) { 60 return; 61 } 62 var $li = $target.closest('li'), 63 $ul = $li.parent(), 64 level = $ul.dataSM('level');
65 // swap left & right keys 66 if ($root.hasClass('sm-rtl')) { 67 if (keyCode == 37) { 68 keyCode = 39; 69 } else if (keyCode == 39) { 70 keyCode = 37; 71 } 72 } 73 switch (keyCode) { 74 case 37: // Left 75 if (obj.isCollapsible()) { 76 break; 77 } 78 if (level > 2 || level == 2 && $root.hasClass('sm-vertical')) { 79 obj.activatedItems[level - 2].focusSM(); 80 // move to previous non-disabled parent item (make sure we cycle so it might be the last item) 81 } else if (!$root.hasClass('sm-vertical')) { 82 getPreviousItemLink((obj.activatedItems[0] || $target).closest('li')).focusSM(); 83 } 84 break; 85 case 38: // Up 86 if (obj.isCollapsible()) { 87 var $firstItem; 88 // if this is the first item of a sub menu, move to the parent item 89 if (level > 1 && ($firstItem = getFirstItemLink($ul)).length && $target[0] == $firstItem[0]) { 90 obj.activatedItems[level - 2].focusSM(); 91 } else { 92 getPreviousItemLink($li).focusSM(); 93 } 94 } else { 95 if (level == 1 && !$root.hasClass('sm-vertical') && obj.opts.bottomToTopSubMenus) { 96 if (!obj.activatedItems[0] && $target.dataSM('sub')) { 97 if (obj.opts.showOnClick) { 98 obj.clickActivated = true; 99 } 100 obj.itemActivate($target); 101 if ($target.dataSM('sub').is(':visible')) { 102 obj.focusActivated = true; 103 } 104 } 105 if (obj.activatedItems[0] && obj.activatedItems[0].dataSM('sub') && obj.activatedItems[0].dataSM('sub').is(':visible') && !obj.activatedItems[0].dataSM('sub').hasClass('mega-menu')) { 106 getLastItemLink(obj.activatedItems[0].dataSM('sub')).focusSM(); 107 } 108 } else if (level > 1 || $root.hasClass('sm-vertical')) { 109 getPreviousItemLink($li).focusSM(); 110 } 111 } 112 break; 113 case 39: // Right 114 if (obj.isCollapsible()) { 115 break; 116 } 117 if (level == 1 && $root.hasClass('sm-vertical')) { 118 if (!obj.activatedItems[0] && $target.dataSM('sub')) { 119 if (obj.opts.showOnClick) { 120 obj.clickActivated = true; 121 } 122 obj.itemActivate($target); 123 if ($target.dataSM('sub').is(':visible')) { 124 obj.focusActivated = true; 125 } 126 } 127 if (obj.activatedItems[0] && obj.activatedItems[0].dataSM('sub') && obj.activatedItems[0].dataSM('sub').is(':visible') && !obj.activatedItems[0].dataSM('sub').hasClass('mega-menu')) { 128 getFirstItemLink(obj.activatedItems[0].dataSM('sub')).focusSM(); 129 } 130 // move to next non-disabled parent item (make sure we cycle so it might be the last item) 131 } else if ((level == 1 || obj.activatedItems[level - 1] && (!obj.activatedItems[level - 1].dataSM('sub') || !obj.activatedItems[level - 1].dataSM('sub').is(':visible') || obj.activatedItems[level - 1].dataSM('sub').hasClass('mega-menu'))) && !$root.hasClass('sm-vertical')) { 132 getNextItemLink((obj.activatedItems[0] || $target).closest('li')).focusSM(); 133 } else if (obj.activatedItems[level - 1] && obj.activatedItems[level - 1].dataSM('sub') && obj.activatedItems[level - 1].dataSM('sub').is(':visible') && !obj.activatedItems[level - 1].dataSM('sub').hasClass('mega-menu')) { 134 getFirstItemLink(obj.activatedItems[level - 1].dataSM('sub')).focusSM(); 135 } 136 break; 137 case 40: // Down 138 if (obj.isCollapsible()) { 139 var $firstSubItem, 140 $lastItem; 141 // move to sub menu if appropriate 142 if (obj.activatedItems[level - 1] && obj.activatedItems[level - 1].dataSM('sub') && obj.activatedItems[level - 1].dataSM('sub').is(':visible') && !obj.activatedItems[level - 1].dataSM('sub').hasClass('mega-menu') && ($firstSubItem = getFirstItemLink(obj.activatedItems[level - 1].dataSM('sub'))).length) { 143 $firstSubItem.focusSM(); 144 // if this is the last item of a sub menu, move to the next parent item 145 } else if (level > 1 && ($lastItem = getLastItemLink($ul)).length && $target[0] == $lastItem[0]) { 146 var $parentItem = obj.activatedItems[level - 2].closest('li'), 147 $nextParentItem = null; 148 while ($parentItem.is('li') && !($nextParentItem = getNextItemLink($parentItem, true)).length) { 149 $parentItem = $parentItem.parent().parent(); 150 } 151 if ($nextParentItem.length) { 152 $nextParentItem.focusSM(); 153 } else { 154 getFirstItemLink($root).focusSM(); 155 } 156 } else { 157 getNextItemLink($li).focusSM(); 158 } 159 } else { 160 if (level == 1 && !$root.hasClass('sm-vertical') && !obj.opts.bottomToTopSubMenus) { 161 if (!obj.activatedItems[0] && $target.dataSM('sub')) { 162 if (obj.opts.showOnClick) { 163 obj.clickActivated = true; 164 } 165 obj.itemActivate($target); 166 if ($target.dataSM('sub').is(':visible')) { 167 obj.focusActivated = true; 168 } 169 } 170 if (obj.activatedItems[0] && obj.activatedItems[0].dataSM('sub') && obj.activatedItems[0].dataSM('sub').is(':visible') && !obj.activatedItems[0].dataSM('sub').hasClass('mega-menu')) { 171 getFirstItemLink(obj.activatedItems[0].dataSM('sub')).focusSM(); 172 } 173 } else if (level > 1 || $root.hasClass('sm-vertical')) { 174 getNextItemLink($li).focusSM(); 175 } 176 } 177 break; 178 } 179 e.stopPropagation(); 180 e.preventDefault(); 181 } 182 }); 183 184 // hook it 185 $(document).on('keydown.smartmenus', 'ul.sm, ul.navbar-nav:not([data-sm-skip])', $.SmartMenus.Keyboard.docKeydown); 186 187 $.extend($.SmartMenus.prototype, { 188 keyboardSetHotkey: function(keyCode, modifiers) { 189 var self = this; 190 $(document).on('keydown.smartmenus' + this.rootId, function(e) { 191 if (keyCode == e.keyCode) { 192 var procede = true; 193 if (modifiers) { 194 if (typeof modifiers == 'string') { 195 modifiers = [modifiers]; 196 } 197 $.each(['ctrlKey', 'shiftKey', 'altKey', 'metaKey'], function(index, value) { 198 if ($.inArray(value, modifiers) >= 0 && !e[value] || $.inArray(value, modifiers) < 0 && e[value]) { 199 procede = false;
200 return false; 201 } 202 }); 203 } 204 if (procede) { 205 getFirstItemLink(self.$root).focusSM(); 206 e.stopPropagation(); 207 e.preventDefault(); 208 } 209 } 210 }); 211 } 212 }); 213 214 return $; 215}));
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.