--- /dev/null
+/**
+ * @class Ext.menu.KeyNav
+ * @private
+ */
+Ext.define('Ext.menu.KeyNav', {
+ extend: 'Ext.util.KeyNav',
+
+ requires: ['Ext.FocusManager'],
+
+ constructor: function(menu) {
+ var me = this;
+
+ me.menu = menu;
+ me.callParent([menu.el, {
+ down: me.down,
+ enter: me.enter,
+ esc: me.escape,
+ left: me.left,
+ right: me.right,
+ space: me.enter,
+ tab: me.tab,
+ up: me.up
+ }]);
+ },
+
+ down: function(e) {
+ var me = this,
+ fi = me.menu.focusedItem;
+
+ if (fi && e.getKey() == Ext.EventObject.DOWN && me.isWhitelisted(fi)) {
+ return true;
+ }
+ me.focusNextItem(1);
+ },
+
+ enter: function(e) {
+ var menu = this.menu;
+
+ if (menu.activeItem) {
+ menu.onClick(e);
+ }
+ },
+
+ escape: function(e) {
+ Ext.menu.Manager.hideAll();
+ },
+
+ focusNextItem: function(step) {
+ var menu = this.menu,
+ items = menu.items,
+ focusedItem = menu.focusedItem,
+ startIdx = focusedItem ? items.indexOf(focusedItem) : -1,
+ idx = startIdx + step;
+
+ while (idx != startIdx) {
+ if (idx < 0) {
+ idx = items.length - 1;
+ } else if (idx >= items.length) {
+ idx = 0;
+ }
+
+ var item = items.getAt(idx);
+ if (menu.canActivateItem(item)) {
+ menu.setActiveItem(item);
+ break;
+ }
+ idx += step;
+ }
+ },
+
+ isWhitelisted: function(item) {
+ return Ext.FocusManager.isWhitelisted(item);
+ },
+
+ left: function(e) {
+ var menu = this.menu,
+ fi = menu.focusedItem,
+ ai = menu.activeItem;
+
+ if (fi && this.isWhitelisted(fi)) {
+ return true;
+ }
+
+ menu.hide();
+ if (menu.parentMenu) {
+ menu.parentMenu.focus();
+ }
+ },
+
+ right: function(e) {
+ var menu = this.menu,
+ fi = menu.focusedItem,
+ ai = menu.activeItem,
+ am;
+
+ if (fi && this.isWhitelisted(fi)) {
+ return true;
+ }
+
+ if (ai) {
+ am = menu.activeItem.menu;
+ if (am) {
+ ai.expandMenu(0);
+ Ext.defer(function() {
+ am.setActiveItem(am.items.getAt(0));
+ }, 25);
+ }
+ }
+ },
+
+ tab: function(e) {
+ var me = this;
+
+ if (e.shiftKey) {
+ me.up(e);
+ } else {
+ me.down(e);
+ }
+ },
+
+ up: function(e) {
+ var me = this,
+ fi = me.menu.focusedItem;
+
+ if (fi && e.getKey() == Ext.EventObject.UP && me.isWhitelisted(fi)) {
+ return true;
+ }
+ me.focusNextItem(-1);
+ }
+});
\ No newline at end of file