devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,055 lines (872 loc) • 37.4 kB
JavaScript
"use strict";
var $ = require("../../core/renderer"),
domAdapter = require("../../core/dom_adapter"),
eventsEngine = require("../../events/core/events_engine"),
Guid = require("../../core/guid"),
registerComponent = require("../../core/component_registrator"),
noop = require("../../core/utils/common").noop,
typeUtils = require("../../core/utils/type"),
domUtils = require("../../core/utils/dom"),
contains = domUtils.contains,
getPublicElement = domUtils.getPublicElement,
each = require("../../core/utils/iterator").each,
inArray = require("../../core/utils/array").inArray,
extend = require("../../core/utils/extend").extend,
windowUtils = require("../../core/utils/window"),
getPublicElement = require("../../core/utils/dom").getPublicElement,
fx = require("../../animation/fx"),
positionUtils = require("../../animation/position"),
devices = require("../../core/devices"),
eventUtils = require("../../events/utils"),
Overlay = require("../overlay"),
MenuBase = require("./ui.menu_base"),
Deferred = require("../../core/utils/deferred").Deferred;
var DX_MENU_CLASS = "dx-menu",
DX_MENU_ITEM_CLASS = DX_MENU_CLASS + "-item",
DX_MENU_ITEM_EXPANDED_CLASS = DX_MENU_ITEM_CLASS + "-expanded",
DX_MENU_PHONE_CLASS = "dx-menu-phone-overlay",
DX_MENU_ITEMS_CONTAINER_CLASS = DX_MENU_CLASS + "-items-container",
DX_MENU_ITEM_WRAPPER_CLASS = DX_MENU_ITEM_CLASS + "-wrapper",
DX_SUBMENU_CLASS = "dx-submenu",
DX_CONTEXT_MENU_CLASS = "dx-context-menu",
DX_HAS_CONTEXT_MENU_CLASS = "dx-has-context-menu",
DX_STATE_DISABLED_CLASS = "dx-state-disabled",
FOCUS_UP = "up",
FOCUS_DOWN = "down",
FOCUS_LEFT = "left",
FOCUS_RIGHT = "right",
FOCUS_FIRST = "first",
FOCUS_LAST = "last",
ACTIONS = ["onShowing", "onShown", "onSubmenuCreated", "onHiding", "onHidden", "onPositioning", "onLeftFirstItem", "onLeftLastItem", "onCloseRootSubmenu", "onExpandLastSubmenu"],
LOCAL_SUBMENU_DIRECTIONS = [FOCUS_UP, FOCUS_DOWN, FOCUS_FIRST, FOCUS_LAST],
DEFAULT_SHOW_EVENT = "dxcontextmenu";
var ContextMenu = MenuBase.inherit(function () {
var getShowEvent = function getShowEvent(that) {
var result = null,
optionValue = that.option("showEvent");
if (typeUtils.isObject(optionValue)) {
if (optionValue.name !== null) {
result = optionValue.name || DEFAULT_SHOW_EVENT;
}
} else {
result = optionValue;
}
return result;
},
getShowDelay = function getShowDelay(that) {
var optionValue = that.option("showEvent");
return typeUtils.isObject(optionValue) && optionValue.delay;
};
return {
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxContextMenuOptions.items
* @publicName items
* @type Array<dxContextMenuItemTemplate>
* @inheritdoc
*/
/**
* @name dxContextMenuOptions.showEvent
* @publicName showEvent
* @type Object|string
* @default "dxcontextmenu"
*/
/**
* @name dxContextMenuOptions.showEvent.name
* @publicName name
* @type string
* @default undefined
*/
/**
* @name dxContextMenuOptions.showEvent.delay
* @publicName delay
* @type number
* @default undefined
*/
showEvent: DEFAULT_SHOW_EVENT,
/**
* @name dxContextMenuOptions.closeOnOutsideClick
* @publicName closeOnOutsideClick
* @type boolean|function
* @default true
* @type_function_param1 event:event
* @type_function_return Boolean
*/
closeOnOutsideClick: true,
/**
* @name dxContextMenuOptions.position
* @publicName position
* @type positionConfig
* @default { my: 'top left', at: 'top left' }
* @ref
*/
position: {
at: "top left",
my: "top left"
},
/**
* @name dxContextMenuOptions.onShowing
* @publicName onShowing
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 cancel:boolean
* @action
*/
onShowing: null,
/**
* @name dxContextMenuOptions.onShown
* @publicName onShown
* @extends Action
* @action
*/
onShown: null,
onSubmenuCreated: null,
/**
* @name dxContextMenuOptions.onHiding
* @publicName onHiding
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 cancel:boolean
* @action
*/
onHiding: null,
/**
* @name dxContextMenuOptions.onHidden
* @publicName onHidden
* @extends Action
* @action
*/
onHidden: null,
/**
* @name dxContextMenuOptions.onPositioning
* @publicName onPositioning
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field5 event:event
* @type_function_param1_field6 position:positionConfig
* @action
*/
onPositioning: null,
/**
* @name dxContextMenuOptions.submenuDirection
* @publicName submenuDirection
* @type Enums.ContextMenuSubmenuDirection
* @default "auto"
*/
submenuDirection: "auto",
/**
* @name dxContextMenuOptions.visible
* @publicName visible
* @type boolean
* @default false
* @fires dxContextMenuOptions.onShowing
* @fires dxContextMenuOptions.onHiding
*/
visible: false,
/**
* @name dxContextMenuOptions.target
* @publicName target
* @type string|Node|jQuery
* @default undefined
*/
target: undefined,
/**
* @name dxContextMenuOptions.itemHoldAction
* @publicName itemHoldAction
* @hidden
* @inheritdoc
*/
/**
* @name dxContextMenuOptions.onItemReordered
* @publicName onItemReordered
* @hidden
* @inheritdoc
*/
/**
* @name dxContextMenuOptions.selectedItems
* @publicName selectedItems
* @hidden
* @inheritdoc
*/
/**
* @name dxContextMenuItemTemplate
* @publicName dxContextMenuItemTemplate
* @inherits dxMenuBaseItemTemplate
* @type object
* @inheritdoc
*/
/**
* @name dxContextMenuItemTemplate.items
* @publicName items
* @type Array<dxContextMenuItemTemplate>
* @inheritdoc
*/
onLeftFirstItem: null,
onLeftLastItem: null,
onCloseRootSubmenu: null,
onExpandLastSubmenu: null
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: function device() {
return !windowUtils.hasWindow();
},
options: {
animation: null
}
}]);
},
_initActions: function _initActions() {
this._actions = {};
each(ACTIONS, function (index, action) {
this._actions[action] = this._createActionByOption(action) || noop;
}.bind(this));
},
_setOptionsByReference: function _setOptionsByReference() {
this.callBase();
extend(this._optionsByReference, {
animation: true,
selectedItem: true
});
},
_focusInHandler: noop,
_itemContainer: function _itemContainer() {
return this._overlay ? this._overlay.$content() : $();
},
_eventBindingTarget: function _eventBindingTarget() {
return this._itemContainer();
},
itemsContainer: function itemsContainer() {
return this._overlay ? this._overlay.$content() : undefined;
},
_supportedKeys: function _supportedKeys() {
var selectItem = function selectItem() {
var $item = $(this.option("focusedElement"));
this.hide();
if (!$item.length || !this._isSelectionEnabled()) {
return;
}
this.selectItem($item[0]);
};
return extend(this.callBase(), {
space: selectItem,
esc: this.hide
});
},
_getActiveItem: function _getActiveItem() {
var $items = this._getAvailableItems(),
$focusedItem = $items.filter(".dx-state-focused"),
$hoveredItem = $items.filter(".dx-state-hover"),
$hoveredItemContainer = $hoveredItem.closest("." + DX_MENU_ITEMS_CONTAINER_CLASS);
if ($hoveredItemContainer.find("." + DX_MENU_ITEM_CLASS).index($focusedItem) >= 0) {
return $focusedItem;
}
if ($hoveredItem.length) return $hoveredItem;
return this.callBase();
},
_moveFocus: function _moveFocus(location) {
var $items = this._getItemsByLocation(location),
$oldTarget = this._getActiveItem(true),
$newTarget,
$hoveredItem = this.itemsContainer().find(".dx-state-hover"),
$focusedItem = $(this.option("focusedElement")),
$activeItemHighlighted = !!($focusedItem.length || $hoveredItem.length);
switch (location) {
case FOCUS_UP:
$newTarget = $activeItemHighlighted ? this._prevItem($items) : $oldTarget;
if ($oldTarget.is($items.first())) {
this._actions.onLeftFirstItem($oldTarget);
}
break;
case FOCUS_DOWN:
$newTarget = $activeItemHighlighted ? this._nextItem($items) : $oldTarget;
if ($oldTarget.is($items.last())) {
this._actions.onLeftLastItem($oldTarget);
}
break;
case FOCUS_RIGHT:
$newTarget = this.option("rtlEnabled") ? this._hideSubmenuHandler() : this._expandSubmenuHandler($items, location);
break;
case FOCUS_LEFT:
$newTarget = this.option("rtlEnabled") ? this._expandSubmenuHandler($items, location) : this._hideSubmenuHandler();
break;
case FOCUS_FIRST:
$newTarget = $items.first();
break;
case FOCUS_LAST:
$newTarget = $items.last();
break;
default:
return this.callBase(location);
}
if ($newTarget.length !== 0) {
this.option("focusedElement", getPublicElement($newTarget));
}
},
_getItemsByLocation: function _getItemsByLocation(location) {
var $items,
$activeItem = this._getActiveItem(true);
if (inArray(location, LOCAL_SUBMENU_DIRECTIONS) >= 0) {
$items = $activeItem.closest("." + DX_MENU_ITEMS_CONTAINER_CLASS).children().children();
}
$items = this._getAvailableItems($items);
return $items;
},
_getAriaTarget: function _getAriaTarget() {
return this.$element();
},
_refreshActiveDescendant: function _refreshActiveDescendant() {
if (!this._overlay) {
return;
}
var id = this.getFocusedItemId();
this.setAria("activedescendant", "", this._overlay.$content());
this.setAria("activedescendant", id, this._overlay.$content());
},
_hideSubmenuHandler: function _hideSubmenuHandler() {
var $curItem = this._getActiveItem(true),
$parentItem = $curItem.parents("." + DX_MENU_ITEM_EXPANDED_CLASS).first();
if ($parentItem.length) {
this._hideSubmenusOnSameLevel($parentItem);
this._hideSubmenu($curItem.closest("." + DX_SUBMENU_CLASS));
return $parentItem;
}
this._actions.onCloseRootSubmenu($curItem);
return $curItem;
},
_expandSubmenuHandler: function _expandSubmenuHandler($items, location) {
var $curItem = this._getActiveItem(true),
itemData = this._getItemData($curItem),
node = this._dataAdapter.getNodeByItem(itemData),
isItemHasSubmenu = this._hasSubmenu(node),
$submenu = $curItem.children("." + DX_SUBMENU_CLASS);
if (isItemHasSubmenu && !$curItem.hasClass(DX_STATE_DISABLED_CLASS)) {
if (!$submenu.length || $submenu.css("visibility") === "hidden") {
this._showSubmenu($curItem);
}
return this._nextItem(this._getItemsByLocation(location));
}
this._actions.onExpandLastSubmenu($curItem);
return $curItem;
},
_clean: function _clean() {
if (this._overlay) {
this._overlay.$element().remove();
this._overlay = null;
}
this._detachShowContextMenuEvents(this._getTarget());
this.callBase();
},
_initMarkup: function _initMarkup() {
this.$element().addClass(DX_HAS_CONTEXT_MENU_CLASS);
this.callBase();
},
_render: function _render() {
this.callBase();
this._renderVisibility(this.option("visible"));
this._addWidgetClass();
},
_renderContentImpl: function _renderContentImpl() {
this._detachShowContextMenuEvents(this._getTarget());
this._attachShowContextMenuEvents();
},
_attachKeyboardEvents: function _attachKeyboardEvents() {
!this._keyboardProcessor && this._focusTarget().length && this.callBase();
},
_renderContextMenuOverlay: function _renderContextMenuOverlay() {
if (this._overlay) {
return;
}
var overlayOptions = this._getOverlayOptions(),
$overlayElement = $("<div>"),
$overlayContent;
this._overlay = this._createComponent($overlayElement.appendTo(this._$element), Overlay, overlayOptions);
$overlayContent = this._overlay.$content();
$overlayContent.addClass(DX_CONTEXT_MENU_CLASS);
this._addCustomCssClass($overlayContent);
this._addPlatformDependentClass($overlayContent);
this._attachContextMenuEvent();
},
_itemContextMenuHandler: function _itemContextMenuHandler(e) {
this.callBase(e);
e.stopPropagation();
},
_addPlatformDependentClass: function _addPlatformDependentClass($element) {
if (devices.current().phone) {
$element.addClass(DX_MENU_PHONE_CLASS);
}
},
_detachShowContextMenuEvents: function _detachShowContextMenuEvents(target) {
var eventName,
showEvent = getShowEvent(this);
if (!showEvent) {
return;
}
eventName = eventUtils.addNamespace(showEvent, this.NAME);
if (this._showContextMenuEventHandler) {
eventsEngine.off(domAdapter.getDocument(), eventName, target, this._showContextMenuEventHandler);
} else {
eventsEngine.off($(target), eventName);
}
},
_attachShowContextMenuEvents: function _attachShowContextMenuEvents() {
var that = this,
delay,
handler,
eventName,
contextMenuAction,
target = that._getTarget(),
showEvent = getShowEvent(that);
if (!showEvent) {
return;
}
eventName = eventUtils.addNamespace(showEvent, that.NAME);
contextMenuAction = that._createAction(function (e) {
delay = getShowDelay(that);
if (delay) {
setTimeout(function () {
that._show(e.event);
}, delay);
} else {
that._show(e.event);
}
}.bind(that), { validatingTargetName: "target" });
handler = function handler(e) {
contextMenuAction({ event: e, target: $(e.currentTarget) });
};
contextMenuAction = that._createAction(contextMenuAction);
if (typeUtils.isRenderer(target) || target.nodeType || typeUtils.isWindow(target)) {
that._showContextMenuEventHandler = undefined;
eventsEngine.on(target, eventName, handler);
} else {
that._showContextMenuEventHandler = handler;
eventsEngine.on(domAdapter.getDocument(), eventName, target, that._showContextMenuEventHandler);
}
},
_hoverEndHandler: function _hoverEndHandler(e) {
this.callBase(e);
e.stopPropagation();
},
_renderDimensions: noop,
_renderContainer: function _renderContainer($wrapper, submenuContainer) {
var $itemsContainer,
$holder = submenuContainer || this._itemContainer();
$wrapper = $("<div>");
$wrapper.appendTo($holder).addClass(DX_SUBMENU_CLASS).css("visibility", submenuContainer ? "hidden" : "visible");
$itemsContainer = this.callBase($wrapper);
if (submenuContainer) {
return $itemsContainer;
}
if (this.option("width")) {
return $itemsContainer.css("minWidth", this.option("width"));
}
if (this.option("height")) {
return $itemsContainer.css("minHeight", this.option("height"));
}
return $itemsContainer;
},
_renderSubmenuItems: function _renderSubmenuItems(node, $itemFrame) {
this._renderItems(this._getChildNodes(node), $itemFrame);
this._actions.onSubmenuCreated({
itemElement: getPublicElement($itemFrame),
itemData: node.internalFields.item,
submenuElement: getPublicElement($itemFrame.children("." + DX_SUBMENU_CLASS))
});
},
_getOverlayOptions: function _getOverlayOptions() {
var position = this.option("position"),
overlayAnimation = this.option("animation"),
overlayOptions = {
focusStateEnabled: this.option("focusStateEnabled"),
animation: overlayAnimation,
closeOnOutsideClick: this._closeOnOutsideClickHandler.bind(this),
closeOnTargetScroll: true,
deferRendering: false,
position: {
at: position.at,
my: position.my,
of: this._getTarget(),
collision: "flipfit"
},
shading: false,
showTitle: false,
height: "auto",
width: "auto",
onShown: this._overlayShownActionHandler.bind(this),
onHiding: this._overlayHidingActionHandler.bind(this),
onHidden: this._overlayHiddenActionHandler.bind(this)
};
return overlayOptions;
},
_overlayShownActionHandler: function _overlayShownActionHandler(arg) {
this._actions.onShown(arg);
},
_overlayHidingActionHandler: function _overlayHidingActionHandler(arg) {
this._actions.onHiding(arg);
if (!arg.cancel) {
this._hideAllShownSubmenus();
this._setOptionSilent("visible", false);
}
},
_overlayHiddenActionHandler: function _overlayHiddenActionHandler(arg) {
this._actions.onHidden(arg);
},
_closeOnOutsideClickHandler: function _closeOnOutsideClickHandler(e) {
var $clickedItem,
$activeItemContainer,
$itemContainers,
$rootItem,
isRootItemClicked,
isInnerOverlayClicked,
closeOnOutsideClick = this.option("closeOnOutsideClick");
if (typeUtils.isFunction(closeOnOutsideClick)) {
return closeOnOutsideClick(e);
}
if (!closeOnOutsideClick) {
return false;
}
if (domAdapter.isDocument(e.target)) {
return true;
}
$activeItemContainer = this._getActiveItemsContainer(e.target);
$itemContainers = this._getItemsContainers();
$clickedItem = this._searchActiveItem(e.target);
$rootItem = this.$element().parents("." + DX_MENU_ITEM_CLASS);
isRootItemClicked = $clickedItem[0] === $rootItem[0] && $clickedItem.length && $rootItem.length;
isInnerOverlayClicked = this._isIncludeOverlay($activeItemContainer, $itemContainers) && $clickedItem.length;
if (isInnerOverlayClicked || isRootItemClicked) {
if (this._getShowSubmenuMode() === "onClick") {
this._hideAllShownChildSubmenus($clickedItem);
}
return false;
}
return true;
},
_getActiveItemsContainer: function _getActiveItemsContainer(target) {
return $(target).closest("." + DX_MENU_ITEMS_CONTAINER_CLASS);
},
_getItemsContainers: function _getItemsContainers() {
return this._overlay._$content.find("." + DX_MENU_ITEMS_CONTAINER_CLASS);
},
_searchActiveItem: function _searchActiveItem(target) {
return $(target).closest("." + DX_MENU_ITEM_CLASS).eq(0);
},
_isIncludeOverlay: function _isIncludeOverlay($activeOverlay, $allOverlays) {
var isSame = false;
each($allOverlays, function (index, $overlay) {
if ($activeOverlay.is($overlay) && !isSame) {
isSame = true;
}
});
return isSame;
},
_hideAllShownChildSubmenus: function _hideAllShownChildSubmenus($clickedItem) {
var that = this,
$submenuElements = $clickedItem.find("." + DX_SUBMENU_CLASS),
shownSubmenus = extend([], this._shownSubmenus),
$context;
if ($submenuElements.length > 0) {
each(shownSubmenus, function (index, $submenu) {
$context = that._searchActiveItem($submenu.context).parent();
if ($context.parent().is($clickedItem.parent().parent()) && !$context.is($clickedItem.parent())) {
that._hideSubmenu($submenu);
}
});
}
},
_showSubmenu: function _showSubmenu($item) {
var node = this._dataAdapter.getNodeByItem(this._getItemData($item));
this._hideSubmenusOnSameLevel($item);
if (!this._hasSubmenu(node)) return;
var $submenu = $item.children("." + DX_SUBMENU_CLASS),
isSubmenuRendered = $submenu.length;
this.callBase($item);
if (!isSubmenuRendered) {
this._renderSubmenuItems(node, $item);
}
if (!this._isSubmenuVisible($submenu)) {
this._drawSubmenu($item);
}
},
_hideSubmenusOnSameLevel: function _hideSubmenusOnSameLevel($item) {
var $expandedItems = $item.parent("." + DX_MENU_ITEM_WRAPPER_CLASS).siblings().find("." + DX_MENU_ITEM_EXPANDED_CLASS);
if ($expandedItems.length) {
$expandedItems.removeClass(DX_MENU_ITEM_EXPANDED_CLASS);
this._hideSubmenu($expandedItems.find("." + DX_SUBMENU_CLASS));
}
},
_hideSubmenuGroup: function _hideSubmenuGroup($submenu) {
if (this._isSubmenuVisible($submenu)) {
this._hideSubmenuCore($submenu);
}
},
_isSubmenuVisible: function _isSubmenuVisible($submenu) {
return $submenu.css("visibility") === "visible";
},
_drawSubmenu: function _drawSubmenu($itemElement) {
var animation = this.option("animation") ? this.option("animation").show : {},
$submenu = $itemElement.children("." + DX_SUBMENU_CLASS),
submenuPosition = this._getSubmenuPosition($itemElement);
if (this._overlay && this._overlay.option("visible")) {
if (!typeUtils.isDefined(this._shownSubmenus)) {
this._shownSubmenus = [];
}
if (inArray($submenu, this._shownSubmenus)) {
this._shownSubmenus.push($submenu);
}
if (animation) {
fx.stop($submenu);
}
positionUtils.setup($submenu, submenuPosition);
if (animation) {
if (typeUtils.isPlainObject(animation.to)) {
animation.to.position = submenuPosition;
}
this._animate($submenu, animation);
}
$submenu.css("visibility", "visible");
}
},
_animate: function _animate($container, options) {
fx.animate($container, options);
},
_getSubmenuPosition: function _getSubmenuPosition($rootItem) {
var submenuDirection = this.option("submenuDirection").toLowerCase(),
$rootItemWrapper = $rootItem.parent("." + DX_MENU_ITEM_WRAPPER_CLASS),
position = {
collision: "flip",
of: $rootItemWrapper,
offset: { h: 0, v: -1 }
};
switch (submenuDirection) {
case "left":
position.at = "left top";
position.my = "right top";
break;
case "right":
position.at = "right top";
position.my = "left top";
break;
default:
if (this.option("rtlEnabled")) {
position.at = "left top";
position.my = "right top";
} else {
position.at = "right top";
position.my = "left top";
}
break;
}
return position;
},
// TODO: try to simplify it
_updateSubmenuVisibilityOnClick: function _updateSubmenuVisibilityOnClick(actionArgs) {
if (!actionArgs.args.length) return;
var $itemElement = $(actionArgs.args[0].itemElement),
itemData = actionArgs.args[0].itemData,
node = this._dataAdapter.getNodeByItem(itemData);
if (!node) return;
var $submenu = $itemElement.find("." + DX_SUBMENU_CLASS),
shouldRenderSubmenu = this._hasSubmenu(node) && !$submenu.length;
if (shouldRenderSubmenu) {
this._renderSubmenuItems(node, $itemElement);
$submenu = $itemElement.find("." + DX_SUBMENU_CLASS);
}
if ($itemElement.context === $submenu.context && $submenu.css("visibility") === "visible") {
return;
}
// T238943. Give the workaround with e.cancel and remove this hack
var notCloseMenuOnItemClick = itemData && itemData.closeMenuOnClick === false;
if (!itemData || itemData.disabled || notCloseMenuOnItemClick) {
return;
}
this._updateSelectedItemOnClick(actionArgs);
if ($submenu.length === 0) {
var $prevSubmenu = $($itemElement.parents("." + DX_SUBMENU_CLASS)[0]);
this._hideSubmenu($prevSubmenu);
if (!actionArgs.canceled && this._overlay && this._overlay.option("visible")) {
this.option("visible", false);
}
} else {
if (this._shownSubmenus && this._shownSubmenus.length > 0) {
if (this._shownSubmenus[0].is($submenu)) {
this._hideSubmenu($submenu); // close to parent?
} else {
this._hideAllShownSubmenus();
}
}
this._showSubmenu($itemElement);
}
},
_hideSubmenu: function _hideSubmenu($curSubmenu) {
var that = this,
shownSubmenus = extend([], that._shownSubmenus);
each(shownSubmenus, function (index, $submenu) {
if ($curSubmenu.is($submenu) || contains($curSubmenu[0], $submenu[0])) {
$submenu.parent().removeClass(DX_MENU_ITEM_EXPANDED_CLASS);
that._hideSubmenuCore($submenu);
}
});
},
_hideSubmenuCore: function _hideSubmenuCore($submenu) {
var index = inArray($submenu, this._shownSubmenus),
animation = this.option("animation") ? this.option("animation").hide : null;
if (index >= 0) {
this._shownSubmenus.splice(index, 1);
}
this._stopAnimate($submenu);
animation && this._animate($submenu, animation);
$submenu.css("visibility", "hidden");
},
_stopAnimate: function _stopAnimate($container) {
fx.stop($container, true);
},
_hideAllShownSubmenus: function _hideAllShownSubmenus() {
var that = this,
shownSubmenus = extend([], that._shownSubmenus),
$expandedItems = this._overlay.$content().find("." + DX_MENU_ITEM_EXPANDED_CLASS);
$expandedItems.removeClass(DX_MENU_ITEM_EXPANDED_CLASS);
each(shownSubmenus, function (_, $submenu) {
that._hideSubmenu($submenu);
});
},
_visibilityChanged: function _visibilityChanged(visible) {
if (visible) {
this._renderContentImpl();
}
},
_optionChanged: function _optionChanged(args) {
if (this._cancelOptionChange === args.name) {
return;
}
if (inArray(args.name, ACTIONS) > -1) {
this._initActions();
return;
}
switch (args.name) {
case "visible":
this._renderVisibility(args.value);
break;
case "showEvent":
case "position":
case "submenuDirection":
this._invalidate();
break;
case "target":
args.previousValue && this._detachShowContextMenuEvents(args.previousValue);
this.option("position").of = null;
this._invalidate();
break;
case "closeOnOutsideClick":
break;
default:
this.callBase(args);
}
},
_renderVisibility: function _renderVisibility(showing) {
return showing ? this._show() : this._hide();
},
_toggleVisibility: noop,
_show: function _show(event) {
var args = { jQEvent: event },
promise = new Deferred().reject().promise();
this._actions.onShowing(args);
if (args.cancel) {
return promise;
}
var position = this._positionContextMenu(event);
if (position) {
if (!this._overlay) {
this._renderContextMenuOverlay();
this._overlay.$content().addClass(this._widgetClass());
this._renderFocusState();
this._attachHoverEvents();
this._attachClickEvent();
this._renderItems(this._dataAdapter.getRootNodes());
}
this._setOptionSilent("visible", true);
this._overlay.option("position", position);
promise = this._overlay.show();
event && event.stopPropagation();
var id = "dx-" + new Guid();
this._overlay.$content().attr({ "id": id, role: "menu" });
this.setAria("owns", id);
}
return promise;
},
_getTarget: function _getTarget() {
return this.option("target") || this.option("position").of || $(domAdapter.getDocument());
},
_getContextMenuPosition: function _getContextMenuPosition() {
return extend({}, this.option("position"), { of: this._getTarget() });
},
_positionContextMenu: function _positionContextMenu(jQEvent) {
var position = this._getContextMenuPosition(),
isInitialPosition = this._isInitialOptionValue("position"),
positioningAction = this._createActionByOption("onPositioning", actionArgs),
actionArgs;
if (jQEvent && jQEvent.preventDefault && isInitialPosition) {
position.of = jQEvent;
}
actionArgs = {
position: position,
event: jQEvent
};
positioningAction(actionArgs);
if (actionArgs.cancel) {
position = null;
} else {
if (actionArgs.event) {
actionArgs.event.cancel = true;
jQEvent.preventDefault();
}
}
return position;
},
_hide: function _hide() {
var promise;
if (this._overlay) {
this._overlay.$content().removeAttr("id");
promise = this._overlay.hide();
this._setOptionSilent("visible", false);
}
this.setAria("owns", undefined);
return promise || new Deferred().reject().promise();
},
/**
* @name dxContextMenuMethods.toggle
* @publicName toggle(showing)
* @param1 showing:boolean
* @return Promise<void>
*/
toggle: function toggle(showing) {
var visible = this.option("visible");
showing = showing === undefined ? !visible : showing;
return this._renderVisibility(showing);
},
/**
* @name dxContextMenuMethods.show
* @publicName show()
* @return Promise<void>
*/
show: function show() {
return this.toggle(true);
},
/**
* @name dxContextMenuMethods.hide
* @publicName hide()
* @return Promise<void>
*/
hide: function hide() {
return this.toggle(false);
}
};
}());
registerComponent("dxContextMenu", ContextMenu);
module.exports = ContextMenu;