// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. cr.define('cr.ui', function() { const MenuItem = cr.ui.MenuItem; /** * Creates a new menu element. * @param {Object=} opt_propertyBag Optional properties. * @constructor * @extends {HTMLMenuElement} */ var Menu = cr.ui.define('menu'); Menu.prototype = { __proto__: HTMLMenuElement.prototype, selectedIndex_: -1, /** * Initializes the menu element. */ decorate: function() { this.addEventListener('mouseover', this.handleMouseOver_); this.addEventListener('mouseout', this.handleMouseOut_); // Decorate the children as menu items. var children = this.children; for (var i = 0, child; child = children[i]; i++) { cr.ui.decorate(child, MenuItem); } }, /** * Walks up the ancestors until a menu item belonging to this menu is found. * @param {Element} el * @return {cr.ui.MenuItem} The found menu item or null. * @private */ findMenuItem_: function(el) { while (el && el.parentNode != this) { el = el.parentNode; } return el; }, /** * Handles mouseover events and selects the hovered item. * @param {Event} e The mouseover event. * @private */ handleMouseOver_: function(e) { var overItem = this.findMenuItem_(e.target); this.selectedItem = overItem; }, /** * Handles mouseout events and deselects any selected item. * @param {Event} e The mouseout event. * @private */ handleMouseOut_: function(e) { this.selectedItem = null; }, /** * The selected menu item or null if none. * @type {cr.ui.MenuItem} */ get selectedItem() { return this.children[this.selectedIndex]; }, set selectedItem(item) { var index = Array.prototype.indexOf.call(this.children, item); this.selectedIndex = index; }, /** * This is the function that handles keyboard navigation. This is usually * called by the element responsible for managing the menu. * @param {Event} e The keydown event object. * @return {boolean} Whether the event was handled be the menu. */ handleKeyDown: function(e) { var item = this.selectedItem; var self = this; function selectNextVisible(m) { var children = self.children; var len = children.length; var i = self.selectedIndex; if (i == -1 && m == -1) { // Edge case when we need to go the last item fisrt. i = 0; } while (true) { i = (i + m + len) % len; item = children[i]; if (item && !item.isSeparator() && !item.hidden) break; } if (item) self.selectedIndex = i; } switch (e.keyIdentifier) { case 'Down': selectNextVisible(1); return true; case 'Up': selectNextVisible(-1); return true; case 'Enter': case 'U+0020': // Space if (item) { if (cr.dispatchSimpleEvent(item, 'activate', true, true)) { if (item.command) item.command.execute(); } } return true; } return false; } }; function selectedIndexChanged(selectedIndex, oldSelectedIndex) { var oldSelectedItem = this.children[oldSelectedIndex]; if (oldSelectedItem) oldSelectedItem.selected = false; var item = this.selectedItem; if (item) item.selected = true; } /** * The selected menu item. * @type {number} */ cr.defineProperty(Menu, 'selectedIndex', cr.PropertyKind.JS, selectedIndexChanged); // Export return { Menu: Menu }; });