PageSourceSearch

https://storageconsortium.de/core/misc/smartmenus/addons/keyboard/jquery.smartmenus.keyboard.js?v=1.0.0

js storageconsortium.de collected 2026-09-24 18:07:12 UTC 8,240 bytes, 215 lines download raw bytes

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.