'use strict'
/**
* @class
* The menu management class.
*
* @constructor
* @param {external:jQuery~Object|external:jQuery~Selector|external:domNode} container - The container element.
* @param {wcMenu~Options} [options] - Custom options.
*/
function wcMenu(container, options) {
this.$container = $(container);
this._menuOptions = [];
// Setup our options.
this._options = {
outer: 'body',
data: false,
manualUpdate: false,
version: ''
};
for (var prop in options) {
this._options[prop] = options[prop];
}
this.$fileMenu = $('<ul class="wcMenu wcMenuNoHighlights"></ul>');
this.$toolbar = $('<div class="wcMenuToolbar wcMenuNoHighlights"></div>');
this.$version = $('<div class="wcMenuVersionTag">' + this._options.version + '</div>');
this.$outer = $(this._options.outer);
this.$container.append(this.$fileMenu);
this.$container.append(this.$toolbar);
this.$fileMenu.append(this.$version);
if (!this.$outer.attr('tabindex')) {
this.$outer.attr('tabindex', '1');
}
// Setup events.
var self = this;
this.$container.on('mouseenter', 'ul.wcMenu > li', function(event) {self.__onMouseEnter.call(this, event, self);});
this.$container.on('click', 'ul.wcMenu > li > ul', function(event) {self.__onClicked.call(this, event, self);});
this.$container.on('mouseleave', 'ul.wcMenu > li', function(event) {self.__onMouseLeave.call(this, event, self);});
this.$container.on('mouseleave', 'ul.wcMenu > li > ul', function(event) {self.__onSubMouseLeave.call(this, event, self);});
this.$outer.on('keydown', function(event) {self.__onKey.call(this, event, self);});
if (!this._options.manualUpdate) {
window.requestAnimationFrame(function() {self.__autoUpdate();});
}
}
wcMenu.prototype = {
/**
* Updates dynamic menus, if you've assigned options.manualUpdate = false, then you will need to call this yourself.
* @function wcMenu#update
*/
update: function() {
// Update menu items.
for (var i = 0; i < this._menuOptions.length; ++i) {
var items = this._menuOptions[i].items;
for (var a = 0; a < items.length; ++a) {
var item = items[a];
if (item.display) {
var display = item.display(this._options.data);
if (display !== item.lastDisplay) {
item.$itemSpan.html(' ' + display)
item.$itemSpan.prepend(item.$icon).append(item.$hotkey);
item.lastDisplay = display;
}
}
if (item.toggle) {
var toggle = item.toggle(this._options.data);
if (toggle !== item.lastToggle) {
item.$icon.toggleClass('wcButtonActive', toggle);
if (item.$toolbarIcon) {
item.$toolbarIcon.parent().toggleClass('wcButtonActive', toggle);
}
item.lastToggle = toggle;
}
}
if (item.icon) {
var icon = item.icon(this._options.data);
if (icon !== item.lastIcon) {
item.$icon.children('i').removeClass().addClass('' + icon);
if (item.$toolbarIcon) {
item.$toolbarIcon.removeClass().addClass('' + icon);
}
item.lastIcon = icon;
}
}
if (item.description) {
var desc = item.description(this._options.data);
if (desc !== item.lastDesc) {
item.$itemSpan.attr('title', desc);
if (item.$toolbar) {
item.$toolbarSpan.attr('title', desc + (item.hotkeyString? ' (' + item.hotkeyString + ')': ''));
}
item.lastDesc = desc;
}
}
if (item.condition) {
var disabled = !item.condition(this._options.data);
if (disabled !== item.lastDisabled) {
item.$itemSpan.toggleClass('disabled', disabled);
if (item.$toolbar) {
item.$toolbar.toggleClass('disabled', disabled);
}
item.lastDisabled = disabled;
}
}
}
}
},
/**
* Gets, or Sets the version tag to display in the right margin of the menu bar.
* @function wcMenu#version
* @param {String} [tag] - The version tag.
* @returns {String}
*/
version: function(tag) {
if (tag !== undefined) {
this.$version.text(tag);
}
return this.$version.text();
},
/**
* Adds a new file menu and/or toolbar option.
* @function wcMenu#addOption
* @param {String} categoryName - The top level category to place this option in the menu.
* @param {String} name - The name for the option.
* @param {wcMenu~MenuOptions} options - Custom options for the menu item.
* @returns {Boolean} - Success or failure.
*/
addOption: function(categoryName, name, options) {
if (!name || !categoryName) {
return false;
}
var optionData = {
name: name,
};
if (options && typeof options.onActivated === 'function') {
optionData.click = options.onActivated;
}
if (options && typeof options.display === 'function') {
optionData.display = options.display;
}
if (options && typeof options.toggle === 'function') {
optionData.toggle = options.toggle;
}
// Find the category if it exists.
var category = null;
var $category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (options && typeof options.toolbarIndex === 'number') {
optionData.$toolbar = $('<div class="wcMenuIcon wcButton"><span></span></div>');
optionData.$toolbarSpan = optionData.$toolbar.children('span');
var $tools = this.$toolbar.children();
if ($tools.length === 0 || options.toolbarIndex < 0 || options.toolbarIndex >= $tools.length) {
this.$toolbar.append(optionData.$toolbar);
} else {
optionData.$toolbar.insertBefore($tools[options.toolbarIndex]);
}
}
if (options && typeof options.condition === 'function') {
optionData.condition = options.condition;
}
// Category doesn't exist, make it.
if (!category) {
$category = $('<li class="wcMenuCategory"><span>' + categoryName + '</span><ul class="wcMenuCategoryDropdown"></ul></li>');
category = {
name: categoryName,
$category: $category,
items: []
};
if (this._menuOptions.length === 0) {
this.$fileMenu.append($category);
this._menuOptions.push(category);
} else {
var insertIndex = this._menuOptions.length;
if (options && typeof options.categoryIndex === 'number' && options.categoryIndex >= 0 && options.categoryIndex < insertIndex) {
insertIndex = options.categoryIndex;
}
if (insertIndex < this._menuOptions.length) {
$category.insertBefore(this._menuOptions[insertIndex].$category);
this._menuOptions.splice(insertIndex, 0, category);
} else {
$category.insertAfter(this._menuOptions[this._menuOptions.length-1].$category);
this._menuOptions.push(category);
}
}
} else {
$category = category.$category;
}
// Create the menu item.
optionData.$item = $('<li><span class="wcMenuItem"> ' + name + '</span></li>');
optionData.$itemSpan = optionData.$item.children('span');
var $icon = $('<div class="wcMenuIcon wcButton"><i></i></div>');
if (options && options.icon) {
if (typeof options.icon === 'string') {
$icon.children('i').addClass(options.icon);
if (optionData.$toolbar) {
optionData.$toolbarSpan.addClass(options.icon);
}
} else if (typeof options.icon === 'function') {
optionData.icon = options.icon;
}
}
optionData.$icon = $icon;
if (optionData.$toolbar) {
optionData.$toolbarIcon = optionData.$toolbarSpan;
}
if (options && typeof options.description === 'string') {
optionData.$itemSpan.attr('title', options.description);
if (optionData.$toolbar) {
optionData.$toolbarSpan.attr('title', options.description);
}
} else if (options && typeof options.description === 'function') {
optionData.description = options.description;
} else {
optionData.$itemSpan.attr('title', '');
if (optionData.$toolbar) {
optionData.$toolbarSpan.attr('title', '');
}
}
var $hotkey = $('<span class="wcMenuHotkey">');
if (options && typeof options.hotkeys === 'string') {
optionData.hotkeys = [];
var hotkeys = (options.hotkeys.indexOf(',') > -1? options.hotkeys.split(','): [options.hotkeys]);
for (var i = 0; i < hotkeys.length; ++i) {
var hotkey = hotkeys[i].toLowerCase();
var hotkeyData = {
ctrlKey: false,
altKey: false,
shiftKey: false
};
// Modifier keys.
if (hotkey.indexOf('ctrl') > -1) {
hotkeyData.ctrlKey = true;
}
if (hotkey.indexOf('alt') > -1) {
hotkeyData.altKey = true;
}
if (hotkey.indexOf('shift') > -1) {
hotkeyData.shiftKey = true;
}
// Special keys.
if (hotkey.indexOf('del') > -1) {
hotkeyData.keyCode = 46;
} else if (hotkey.indexOf('ent') > -1 || hotkey.indexOf('ret') > -1) {
hotkeyData.keyCode = 13;
} else if (hotkey.indexOf('space') > -1 || hotkey.indexOf('spc') > -1) {
hotkeyData.keyCode = 32;
} else if (hotkey.indexOf('left') > -1) {
hotkeyData.keyCode = 37;
} else if (hotkey.indexOf('up') > -1) {
hotkeyData.keyCode = 38;
} else if (hotkey.indexOf('right') > -1) {
hotkeyData.keyCode = 39;
} else if (hotkey.indexOf('down') > -1) {
hotkeyData.keyCode = 40;
} else {
// Ascii character code.
hotkeyData.keyCode = hotkey.toUpperCase().charCodeAt(hotkey.length-1);
}
optionData.hotkeys.push(hotkeyData);
}
optionData.$hotkey = $hotkey;
optionData.hotkeyString = options.hotkeys;
$hotkey.text(options.hotkeys);
if (optionData.$toolbar) {
optionData.$toolbarSpan.attr('title', optionData.$toolbarSpan.attr('title') + (options.hotkeys? ' (' + options.hotkeys + ')': ''));
}
}
optionData.$itemSpan.prepend($icon).append($hotkey);
if (category.items.length === 0) {
category.items.push(optionData);
$category.children('ul').append(optionData.$item);
} else {
insertIndex = category.items.length;
if (options && typeof options.itemIndex === 'number' && options.itemIndex >= 0 && options.itemIndex < insertIndex) {
insertIndex = options.itemIndex;
}
if (insertIndex < category.items.length) {
optionData.$item.insertBefore(category.items[insertIndex].$item);
category.items.splice(insertIndex, 0, optionData);
} else {
optionData.$item.insertAfter(category.items[category.items.length-1].$spacer || category.items[category.items.length-1].$item);
category.items.push(optionData);
}
}
var menuOptions = this._options;
function __onActivated(event) {
if ($(this).hasClass('disabled')) {
return;
}
optionData.click && optionData.click(menuOptions.data);
};
optionData.$itemSpan.click(__onActivated);
if (optionData.$toolbar) {
optionData.$toolbar.click(__onActivated);
}
return true;
},
/**
* Removes an existing file menu and/or toolbar option.
* @function wcMenu#removeOption
* @param {String} categoryName - The top level category to place this option in the menu.
* @param {String} name - The name to display for the option.
* @param {wcMenu~MenuOptions} options - Custom options for the menu item.
* @returns {Boolean} - Success or failure.
*/
removeOption: function(categoryName, name) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
var $category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
category.items.splice(i, 1);
break;
}
}
if (!optionData) {
return false;
}
// Remove a category when no items exist.
if (category.items.length === 0) {
category.$category.remove();
this._menuOptions.splice(this._menuOptions.indexOf(category), 1);
}
optionData.$item.remove();
optionData.$toolbar.remove();
if (optionData.$spacer) {
optionData.$spacer.remove();
}
if (optionData.$toolbarSpacer) {
optionData.$toolbarSpacer.remove();
}
},
/**
* Adds a spacer to a menu list.
* @function wcMenu#addSpacer
* @param {String} categoryName - The top level category to place this spacer in the menu.
* @param {Number} name - The name of the menu option, the spacer is inserted after.
* @returns {Boolean} - Success or failure.
*/
addSpacer: function(categoryName, name) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
optionData.$spacer = $('<li><hr class="wcMenuSeparator"></li>');
optionData.$spacer.insertAfter(optionData.$item);
return true;
},
/**
* Removes an existing spacer from the menu list.
* @function wcMenu#removeSpacer
* @param {String} categoryName - The top level category where this spacer can be found.
* @param {Number} name - The name of the menu option to remove the spacer from.
* @returns {Boolean} - Success or failure.
*/
removeSpacer: function(categoryName, name) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (optionData.$spacer) {
optionData.$spacer.remove();
}
return true;
},
/**
* Adds a spacer to the toolbar.
* @function wcMenu#addToolbarSpacer
* @param {String} categoryName - The top level category to place this spacer in the menu.
* @param {Number} name - The name of the toolbar option, the spacer is inserted after.
* @returns {Boolean} - Success or failure.
*/
addToolbarSpacer: function(categoryName, name) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (optionData.$toolbar) {
optionData.$toolbarSpacer = $('<div class="wcMenuSeparator"></div>');
optionData.$toolbarSpacer.insertAfter(optionData.$toolbar);
}
return true;
},
/**
* Removes an existing spacer from the toolbar.
* @function wcMenu#removeToolbarSpacer
* @param {String} categoryName - The top level category where this spacer can be found.
* @param {Number} name - The name of the toolbar option to remove the spacer from.
* @returns {Boolean} - Success or failure.
*/
removeToolbarSpacer: function(categoryName, name) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (optionData.$toolbarSpacer) {
optionData.$toolbarSpacer.remove();
}
return true;
},
/**
* Replaces the activation handler for an existing option.
* @function wcMenu#optionOnActivated
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {wcMenu~OnActivation} onActivated - A handler to call when the option is activated.
* @returns {Boolean} - Success or failure.
*/
optionOnActivated: function(categoryName, name, onActivated) {
if (!name || !categoryName) {
return false;
}
if (typeof onActivated !== 'function') {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
optionData.click = onActivated;
return true;
},
/**
* Replaces the display for an existing option.
* @function wcMenu#optionDisplay
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {wcMenu~MenuDisplayFunc} [display] - A function that returns the display text that should be shown on the menu option item. If not supplied, the default menu name will be applied.
* @returns {Boolean} - Success or failure.
*/
optionDisplay: function(categoryName, name, display) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof display !== 'function') {
optionData.display = undefined;
delete optionData.display;
} else {
optionData.display = display;
}
return true;
},
/**
* Replaces the toggle function for an existing option.
* @function wcMenu#optionToggle
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {wcMenu~MenuToggleFunc} [toggle] - A function that returns the toggle state of the menu option item. If not supplied, option will no longer toggle.
* @returns {Boolean} - Success or failure.
*/
optionToggle: function(categoryName, name, toggle) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof toggle !== 'function') {
optionData.toggle = undefined;
delete optionData.toggle;
optionData.$icon.removeClass('wcButtonActive');
if (item.$toolbarIcon) {
optionData.$toolbarIcon.parent().removeClass('wcButtonActive');
}
} else {
optionData.toggle = toggle;
}
return true;
},
/**
* Replaces the icon for an existing option.
* @function wcMenu#optionIcon
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {String|wcMenu~MenuIconFunc} [icon] - A classname to apply as an icon for the option. If this is a function, it will be called during the update call and should return the icon class.
* @returns {Boolean} - Success or failure.
*/
optionIcon: function(categoryName, name, icon) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof icon !== 'function') {
optionData.icon = undefined;
delete optionData.icon;
if (typeof icon === 'string') {
optionData.$icon.children('i').removeClass().addClass('' + icon);
if (optionData.$toolbar) {
optionData.$toolbarIcon.removeClass().addClass('' + icon);
}
}
} else {
optionData.icon = icon;
}
return true;
},
/**
* Replaces the description for an existing option.
* @function wcMenu#optionCondition
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {String|wcMenu~MenuDescriptionFunc} [description] - The description to show as a tooltip for this option.
* @returns {Boolean} - Success or failure.
*/
optionDescription: function(categoryName, name, description) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof description !== 'function') {
optionData.description = undefined;
delete optionData.description;
if (typeof description === 'string') {
optionData.$itemSpan.attr('title', description);
if (optionData.$toolbar) {
optionData.$toolbarSpan.attr('title', description + (item.hotkeyString? ' (' + item.hotkeyString + ')': ''));
}
}
} else {
optionData.description = description;
}
return true;
},
/**
* Replaces the hotkeys for an existing option.
* @function wcMenu#optionHotkeys
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @property {String} [hotkeys] - A string that describes hotkeys for your control, the format is 'Ctrl+Z' or 'Alt+Shift+L,Shift+L' for multiple.
* @returns {Boolean} - Success or failure.
*/
optionHotkeys: function(categoryName, name, hotkeys) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof hotkeys === 'string') {
optionData.hotkeys = [];
var optionHotkeys = (hotkeys.indexOf(',') > -1? hotkeys.split(','): [hotkeys]);
for (var i = 0; i < optionHotkeys.length; ++i) {
var hotkey = optionHotkeys[i].toLowerCase();
var hotkeyData = {
ctrlKey: false,
altKey: false,
shiftKey: false
};
// Modifier keys.
if (hotkey.indexOf('ctrl') > -1) {
hotkeyData.ctrlKey = true;
}
if (hotkey.indexOf('alt') > -1) {
hotkeyData.altKey = true;
}
if (hotkey.indexOf('shift') > -1) {
hotkeyData.shiftKey = true;
}
// Special keys.
if (hotkey.indexOf('del') > -1) {
hotkeyData.keyCode = 46;
} else if (hotkey.indexOf('ent') > -1 || hotkey.indexOf('ret') > -1) {
hotkeyData.keyCode = 13;
} else if (hotkey.indexOf('space') > -1 || hotkey.indexOf('spc') > -1) {
hotkeyData.keyCode = 32;
} else {
// Ascii character code.
hotkeyData.keyCode = hotkey.toUpperCase().charCodeAt(hotkey.length-1);
}
optionData.hotkeys.push(hotkeyData);
}
optionData.$hotkey.text(hotkeys);
if (optionData.$toolbar) {
var title = optionData.$toolbarSpan.attr('title');
if (optionData.hotkeyString) {
title = title.replace(' (' + optionData.hotkeyString + ')', '');
}
optionData.$toolbarSpan.attr('title', title + (hotkeys? ' (' + hotkeys + ')': ''));
}
optionData.hotkeyString = hotkeys;
}
return true;
},
/**
* Replaces the condition for an existing option.
* @function wcMenu#optionCondition
* @param {String} categoryName - The top level category of the option.
* @param {String} name - The name of the option.
* @param {Function} [condition] - A function to call during the update to determine if the menu option should be enabled or disabled. Return false to disable the option.
* @returns {Boolean} - Success or failure.
*/
optionCondition: function(categoryName, name, condition) {
if (!name || !categoryName) {
return false;
}
// Find the category if it exists.
var category = null;
for (var i = 0; i < this._menuOptions.length; ++i) {
if (this._menuOptions[i].name === categoryName) {
category = this._menuOptions[i];
break;
}
}
if (!category) {
return false;
}
var optionData = null;
for (var i = 0; i < category.items.length; ++i) {
if (category.items[i].name === name) {
optionData = category.items[i];
break;
}
}
if (!optionData) {
return false;
}
if (typeof condition !== 'function') {
optionData.condition = undefined;
delete optionData.condition;
} else {
optionData.condition = condition;
}
return true;
},
/**
* Used when options.manualUpdate is not enabled.
* @function wcMenu#__autoUpdate
* @private
*/
__autoUpdate: function() {
this.update();
var self = this;
window.requestAnimationFrame(function() {self.__autoUpdate();});
},
/**
* Mouse over an menu option on the top bar to open it.
* @function wcMenu#__onMouseEnter
* @private
* @param {Object} event - The mouse event.
* @param {wcMenu} menu - The menu instance.
*/
__onMouseEnter: function(event, menu) {
var $self = $(this);
setTimeout(function() {
if ($self.is(':hover')) {
$self.addClass('wcMenuOpen').addClass('wcMenuItemHover');
}
}, 100);
},
/**
* Clicking a menu item will also hide that menu.
* @function wcMenu#__onClicked
* @private
* @param {Object} event - The mouse event.
* @param {wcMenu} menu - The menu instance.
*/
__onClicked: function(event, menu) {
// Clicking a menu item will also hide that menu.
menu.$container.find('ul.wcMenu li ul').css('display', 'none');
setTimeout(function() {
menu.$container.find('ul.wcMenu li ul').css('display', '');
}, 200);
},
/**
* Leaving the popup menu will hide it.
* @function wcMenu#__onMouseLeave
* @private
* @param {Object} event - The mouse event.
* @param {wcMenu} menu - The menu instance.
*/
__onMouseLeave: function(event, menu) {
if ($(this).find(event.toElement).length === 0) {
$(this).removeClass('wcMenuOpen').removeClass('wcMenuItemHover');
}
},
/**
* Moving your mouse cursor away from the drop down menu will also hide it.
* @function wcMenu#__onSubMouseLeave
* @private
* @param {Object} event - The mouse event.
* @param {wcMenu} menu - The menu instance.
*/
__onSubMouseLeave: function(event, menu) {
// Make sure that we are actually leaving the menu
// and not just jumping to another item in the menu
var $parent = $(this).parent();
if ($parent.find(event.toElement).length === 0) {
$parent.removeClass('wcMenuOpen').removeClass('wcMenuItemHover');
}
},
/**
* Handle key press events.
* @function wcMenu#__onKey
* @private
* @param {Object} event - The mouse event.
* @param {wcMenu} menu - The menu instance.
*/
__onKey: function(event, menu) {
// Update menu items.
for (var i = 0; i < menu._menuOptions.length; ++i) {
var items = menu._menuOptions[i].items;
for (var a = 0; a < items.length; ++a) {
var item = items[a];
if (item.hotkeys) {
for (var b = 0; b < item.hotkeys.length; ++b) {
var hotkey = item.hotkeys[b];
if (hotkey.ctrlKey == event.ctrlKey &&
hotkey.shiftKey == event.shiftKey &&
hotkey.altKey == event.altKey &&
hotkey.keyCode == event.keyCode) {
item.$itemSpan.click();
event.stopPropagation();
event.preventDefault();
return false;
}
}
}
}
}
},
};