tag inside the LI
	 */
	getEntryWrapper: function(cmd) {
		return this._getMenuEntry(cmd).find(">[role=menuitem]").addBack("[role=menuitem]");
	},
	/** Return Menu element (UL). */
	getMenu: function() {
		return this.$menu;
	},
	/** Return true if menu is open. */
	isOpen: function() {
//            return this.$menu && this.$menu.is(":visible");
		return !!this.$menu && !!this.currentTarget;
	},
	/** Open context menu on a specific target (must match options.delegate)
	 *  Optional `extraData` is passed to event handlers as `ui.extraData`.
	 */
	open: function(targetOrEvent, extraData) {
		// Fake a 'contextmenu' event
		extraData = extraData || {};
		var isEvent = (targetOrEvent && targetOrEvent.type && targetOrEvent.target),
			event =  isEvent ? targetOrEvent : {},
			target = isEvent ? targetOrEvent.target : targetOrEvent,
			e = jQuery.Event("contextmenu", {
				target: $(target).get(0),
				pageX: event.pageX,
				pageY: event.pageY,
				originalEvent: isEvent ? targetOrEvent : undefined,
				_extraData: extraData
			});
		return this.element.trigger(e);
	},
	/** Replace the menu altogether. */
	replaceMenu: function(data) {
		this._createUiMenu(data);
	},
	/** Redefine a whole menu entry. */
	setEntry: function(cmd, entry) {
		var $ul,
			$entryLi = this._getMenuEntry(cmd);
		if (typeof entry === "string") {
			window.console && window.console.warn(
				"setEntry(cmd, t) with a plain string title is deprecated since v1.18." +
				"Use setTitle(cmd, '" + entry + "') instead.");
			return this.setTitle(cmd, entry);
		}
		$entryLi.empty();
		entry.cmd = entry.cmd || cmd;
		$.moogle.contextmenu.createEntryMarkup(entry, $entryLi);
		if ($.isArray(entry.children)) {
			$ul = $("
").appendTo($entryLi);
			$.moogle.contextmenu.createMenuMarkup(entry.children, $ul);
		}
		// #110: jQuery UI 1.12: refresh only works when this class is not set:
		$entryLi.removeClass("ui-menu-item");
		this.getMenu().menu("refresh");
	},
	/** Set icon (pass null to remove). */
	setIcon: function(cmd, icon) {
		return this.updateEntry(cmd, { uiIcon: icon });
	},
	/** Set title. */
	setTitle: function(cmd, title) {
		return this.updateEntry(cmd, { title: title });
	},
	// /** Set tooltip (pass null to remove). */
	// setTooltip: function(cmd, tooltip) {
	// 	this._getMenuEntry(cmd).attr("title", tooltip);
	// },
	/** Show or hide the menu command. */
	showEntry: function(cmd, flag) {
		this._getMenuEntry(cmd).toggle(flag !== false);
	},
	/** Redefine selective attributes of a menu entry. */
	updateEntry: function(cmd, entry) {
		var $icon, $wrapper,
			$entryLi = this._getMenuEntry(cmd);
		if ( entry.title !== undefined ) {
			$.moogle.contextmenu.updateTitle($entryLi, "" + entry.title);
		}
		if ( entry.tooltip !== undefined ) {
			if ( entry.tooltip === null ) {
				$entryLi.removeAttr("title");
			} else {
				$entryLi.attr("title", entry.tooltip);
			}
		}
		if ( entry.uiIcon !== undefined ) {
			$wrapper = this.getEntryWrapper(cmd),
			$icon = $wrapper.find("span.ui-icon").not(".ui-menu-icon");
			$icon.remove();
			if ( entry.uiIcon ) {
				$wrapper.append($("
").addClass(entry.uiIcon));
			}
		}
		if ( entry.hide !== undefined ) {
			$entryLi.toggle(!entry.hide);
		} else if ( entry.show !== undefined ) {
			// Note: `show` is an undocumented variant. `hide: false` is preferred
			$entryLi.toggle(!!entry.show);
		}
		// if ( entry.isHeader !== undefined ) {
		// 	$entryLi.toggleClass("ui-widget-header", !!entry.isHeader);
		// }
		if ( entry.data !== undefined ) {
			$entryLi.data(entry.data);
		}
		// Set/clear class names, but handle ui-state-disabled separately
		if ( entry.disabled === undefined ) {
			entry.disabled = $entryLi.hasClass("ui-state-disabled");
		}
		if ( entry.setClass ) {
			if ( $entryLi.hasClass("ui-menu-item") ) {
				entry.setClass += " ui-menu-item";
			}
			$entryLi.removeClass();
			$entryLi.addClass(entry.setClass);
		} else if ( entry.addClass ) {
			$entryLi.addClass(entry.addClass);
		}
		$entryLi.toggleClass("ui-state-disabled", !!entry.disabled);
		// // #110: jQuery UI 1.12: refresh only works when this class is not set:
		// $entryLi.removeClass("ui-menu-item");
		// this.getMenu().menu("refresh");
	}
});
/*
 * Global functions
 */
$.extend($.moogle.contextmenu, {
	/** Convert a menu description into a into a 
 content. */
	createEntryMarkup: function(entry, $parentLi) {
		var $wrapper = null;
		$parentLi.attr("data-command", entry.cmd);
		if ( !/[^\-\u2014\u2013\s]/.test( entry.title ) ) {
			// hyphen, em dash, en dash: separator as defined by UI Menu 1.10
			$parentLi.text(entry.title);
		} else {
			if ( isLTE110 ) {
				// jQuery UI Menu 1.10 or before required an `` tag
				$wrapper = $("", {
						html: "" + entry.title,
						href: "#"
					}).appendTo($parentLi);
			} else if ( isLTE111 ) {
				// jQuery UI Menu 1.11 preferes to avoid `` tags or  wrapper
				$parentLi.html("" + entry.title);
				$wrapper = $parentLi;
			} else {
				// jQuery UI Menu 1.12 introduced `
` wrappers
				$wrapper = $("
", {
						html: "" + entry.title
					}).appendTo($parentLi);
			}
			if ( entry.uiIcon ) {
				$wrapper.append($("
").addClass(entry.uiIcon));
			}
			// Store option callbacks in entry's data
			$.each( [ "action", "disabled", "title", "tooltip" ], function(i, attr) {
				if ( $.isFunction(entry[attr]) ) {
					$parentLi.data(attr + "Handler", entry[attr]);
				}
			});
			if ( entry.disabled === true ) {
				$parentLi.addClass("ui-state-disabled");
			}
			if ( entry.isHeader ) {
				$parentLi.addClass("ui-widget-header");
			}
			if ( entry.addClass ) {
				$parentLi.addClass(entry.addClass);
			}
			if ( $.isPlainObject(entry.data) ) {
				$parentLi.data(entry.data);
			}
			if ( typeof entry.tooltip === "string" ) {
				$parentLi.attr("title", entry.tooltip);
			}
		}
	},
	/** Convert a nested array of command objects into a