Path: blob/trunk/third_party/closure/goog/ui/menuitem.js
4116 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview A class for representing items in menus.8* @see goog.ui.Menu9* @see ../demos/menuitem.html10*/1112goog.provide('goog.ui.MenuItem');1314goog.require('goog.a11y.aria.Role');15goog.require('goog.array');16goog.require('goog.dom');17goog.require('goog.dom.classlist');18goog.require('goog.math.Coordinate');19goog.require('goog.string');20goog.require('goog.ui.Component');21goog.require('goog.ui.Control');22goog.require('goog.ui.MenuItemRenderer');23goog.require('goog.ui.registry');24goog.requireType('goog.events.KeyCodes');25goog.requireType('goog.ui.ControlContent'); // circular26goog.requireType('goog.ui.Menu');27282930/**31* Class representing an item in a menu.32*33* @param {goog.ui.ControlContent} content Text caption or DOM structure to34* display as the content of the item (use to add icons or styling to35* menus).36* @param {*=} opt_model Data/model associated with the menu item.37* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for38* document interactions.39* @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.40* @constructor41* @extends {goog.ui.Control}42*/43goog.ui.MenuItem = function(content, opt_model, opt_domHelper, opt_renderer) {44'use strict';45goog.ui.Control.call(46this, content, opt_renderer || goog.ui.MenuItemRenderer.getInstance(),47opt_domHelper);48this.setValue(opt_model);49};50goog.inherits(goog.ui.MenuItem, goog.ui.Control);515253/**54* The access key for this menu item. This key allows the user to quickly55* trigger this item's action with they keyboard. For example, setting the56* mnenomic key to 70 (F), when the user opens the menu and hits "F," the57* menu item is triggered.58*59* @type {goog.events.KeyCodes}60* @private61*/62goog.ui.MenuItem.prototype.mnemonicKey_;636465/**66* The class set on an element that contains a parenthetical mnemonic key hint.67* Parenthetical hints are added to items in which the mnemonic key is not found68* within the menu item's caption itself. For example, if you have a menu item69* with the caption "Record," but its mnemonic key is "I", the caption displayed70* in the menu will appear as "Record (I)".71*72* @type {string}73* @private74*/75goog.ui.MenuItem.MNEMONIC_WRAPPER_CLASS_ =76goog.getCssName('goog-menuitem-mnemonic-separator');777879/**80* The class set on an element that contains a keyboard accelerator hint.81* @type {string}82*/83goog.ui.MenuItem.ACCELERATOR_CLASS = goog.getCssName('goog-menuitem-accel');848586// goog.ui.Component and goog.ui.Control implementation.878889/**90* Returns the value associated with the menu item. The default implementation91* returns the model object associated with the item (if any), or its caption.92* @return {*} Value associated with the menu item, if any, or its caption.93*/94goog.ui.MenuItem.prototype.getValue = function() {95'use strict';96var model = this.getModel();97return model != null ? model : this.getCaption();98};99100101/**102* Sets the value associated with the menu item. The default implementation103* stores the value as the model of the menu item.104* @param {*} value Value to be associated with the menu item.105*/106goog.ui.MenuItem.prototype.setValue = function(value) {107'use strict';108this.setModel(value);109};110111112/** @override */113goog.ui.MenuItem.prototype.setSupportedState = function(state, support) {114'use strict';115goog.ui.MenuItem.base(this, 'setSupportedState', state, support);116switch (state) {117case goog.ui.Component.State.SELECTED:118this.setSelectableInternal_(support);119break;120case goog.ui.Component.State.CHECKED:121this.setCheckableInternal_(support);122break;123}124};125126127/**128* Sets the menu item to be selectable or not. Set to true for menu items129* that represent selectable options.130* @param {boolean} selectable Whether the menu item is selectable.131*/132goog.ui.MenuItem.prototype.setSelectable = function(selectable) {133'use strict';134this.setSupportedState(goog.ui.Component.State.SELECTED, selectable);135};136137138/**139* Sets the menu item to be selectable or not.140* @param {boolean} selectable Whether the menu item is selectable.141* @private142* @suppress {strictMissingProperties} Added to tighten compiler checks143*/144goog.ui.MenuItem.prototype.setSelectableInternal_ = function(selectable) {145'use strict';146if (this.isChecked() && !selectable) {147this.setChecked(false);148}149150var element = this.getElement();151if (element) {152this.getRenderer().setSelectable(this, element, selectable);153}154};155156157/**158* Sets the menu item to be checkable or not. Set to true for menu items159* that represent checkable options.160* @param {boolean} checkable Whether the menu item is checkable.161*/162goog.ui.MenuItem.prototype.setCheckable = function(checkable) {163'use strict';164this.setSupportedState(goog.ui.Component.State.CHECKED, checkable);165};166167168/**169* Sets the menu item to be checkable or not.170* @param {boolean} checkable Whether the menu item is checkable.171* @private172* @suppress {strictMissingProperties} Added to tighten compiler checks173*/174goog.ui.MenuItem.prototype.setCheckableInternal_ = function(checkable) {175'use strict';176var element = this.getElement();177if (element) {178this.getRenderer().setCheckable(this, element, checkable);179}180};181182183/**184* Returns the text caption of the component while ignoring accelerators.185* @override186*/187goog.ui.MenuItem.prototype.getCaption = function() {188'use strict';189var content = this.getContent();190if (Array.isArray(content)) {191var acceleratorClass = goog.ui.MenuItem.ACCELERATOR_CLASS;192var mnemonicWrapClass = goog.ui.MenuItem.MNEMONIC_WRAPPER_CLASS_;193var caption =194goog.array195.map(196content,197function(node) {198'use strict';199if (goog.dom.isElement(node) &&200(goog.dom.classlist.contains(201/** @type {!Element} */ (node), acceleratorClass) ||202goog.dom.classlist.contains(203/** @type {!Element} */ (node),204mnemonicWrapClass))) {205return '';206} else {207return goog.dom.getRawTextContent(node);208}209})210.join('');211return goog.string.collapseBreakingSpaces(caption);212}213return goog.ui.MenuItem.superClass_.getCaption.call(this);214};215216217/**218* @return {?string} The keyboard accelerator text, or null if the menu item219* doesn't have one.220*/221goog.ui.MenuItem.prototype.getAccelerator = function() {222'use strict';223var dom = this.getDomHelper();224var content = this.getContent();225if (Array.isArray(content)) {226var acceleratorEl = goog.array.find(content, function(e) {227'use strict';228return goog.dom.classlist.contains(229/** @type {!Element} */ (e), goog.ui.MenuItem.ACCELERATOR_CLASS);230});231if (acceleratorEl) {232return dom.getTextContent(acceleratorEl);233}234}235return null;236};237238239/** @override */240goog.ui.MenuItem.prototype.handleMouseUp = function(e) {241'use strict';242var parentMenu = /** @type {goog.ui.Menu} */ (this.getParent());243244if (parentMenu) {245var oldCoords = parentMenu.openingCoords;246// Clear out the saved opening coords immediately so they're not used twice.247parentMenu.openingCoords = null;248249if (oldCoords && typeof e.clientX === 'number') {250/**251* @suppress {strictMissingProperties} Added to tighten compiler checks252*/253var newCoords = new goog.math.Coordinate(e.clientX, e.clientY);254if (goog.math.Coordinate.equals(oldCoords, newCoords)) {255// This menu was opened by a mousedown and we're handling the consequent256// mouseup. The coords haven't changed, meaning this was a simple click,257// not a click and drag. Don't do the usual behavior because the menu258// just popped up under the mouse and the user didn't mean to activate259// this item.260return;261}262}263}264265goog.ui.MenuItem.base(this, 'handleMouseUp', e);266};267268269/** @override */270goog.ui.MenuItem.prototype.handleKeyEventInternal = function(e) {271'use strict';272if (e.keyCode == this.getMnemonic() && this.performActionInternal(e)) {273return true;274} else {275return goog.ui.MenuItem.base(this, 'handleKeyEventInternal', e);276}277};278279280/**281* Sets the mnemonic key code. The mnemonic is the key associated with this282* action.283* @param {goog.events.KeyCodes} key The key code.284*/285goog.ui.MenuItem.prototype.setMnemonic = function(key) {286'use strict';287this.mnemonicKey_ = key;288};289290291/**292* Gets the mnemonic key code. The mnemonic is the key associated with this293* action.294* @return {goog.events.KeyCodes} The key code of the mnemonic key.295*/296goog.ui.MenuItem.prototype.getMnemonic = function() {297'use strict';298return this.mnemonicKey_;299};300301302// Register a decorator factory function for goog.ui.MenuItems.303goog.ui.registry.setDecoratorByClassName(304goog.ui.MenuItemRenderer.CSS_CLASS, function() {305'use strict';306// MenuItem defaults to using MenuItemRenderer.307return new goog.ui.MenuItem(null);308});309310311/**312* @override313*/314goog.ui.MenuItem.prototype.getPreferredAriaRole = function() {315'use strict';316if (this.isSupportedState(goog.ui.Component.State.CHECKED)) {317return goog.a11y.aria.Role.MENU_ITEM_CHECKBOX;318}319if (this.isSupportedState(goog.ui.Component.State.SELECTED)) {320return goog.a11y.aria.Role.MENU_ITEM_RADIO;321}322return goog.ui.MenuItem.base(this, 'getPreferredAriaRole');323};324325326/**327* @override328* @return {goog.ui.Menu}329*/330goog.ui.MenuItem.prototype.getParent = function() {331'use strict';332return /** @type {goog.ui.Menu} */ (333goog.ui.Control.prototype.getParent.call(this));334};335336337/**338* @override339* @return {goog.ui.Menu}340*/341goog.ui.MenuItem.prototype.getParentEventTarget = function() {342'use strict';343return /** @type {goog.ui.Menu} */ (344goog.ui.Control.prototype.getParentEventTarget.call(this));345};346347348