/** * Expose `Menu`. */ exports.Menu = Menu; /** * Create a new `Menu`. * * @return {Menu} * @api public */ exports.menu = function(){ return new Menu; }; /** * Initialize a new `Menu`. * * Emits: * * - "show" when shown * - "hide" when hidden * - "remove" with the item name when an item is removed * - * menu item events are emitted when clicked * * @api public */ function Menu() { ui.Emitter.call(this); this.items = {}; this.el = $(html).hide().appendTo('body'); this.el.hover(this.deselect.bind(this)); $('html').click(this.hide.bind(this)); this.on('show', this.bindKeyboardEvents.bind(this)); this.on('hide', this.unbindKeyboardEvents.bind(this)); }; /** * Inherit from `Emitter.prototype`. */ Menu.prototype = new ui.Emitter; /** * Deselect selected menu items. * * @api private */ Menu.prototype.deselect = function(){ this.el.find('.selected').removeClass('selected'); }; /** * Bind keyboard events. * * @api private */ Menu.prototype.bindKeyboardEvents = function(){ $(document).bind('keydown.menu', this.onkeydown.bind(this)); return this; }; /** * Unbind keyboard events. * * @api private */ Menu.prototype.unbindKeyboardEvents = function(){ $(document).unbind('keydown.menu'); return this; }; /** * Handle keydown events. * * @api private */ Menu.prototype.onkeydown = function(e){ switch (e.keyCode) { // up case 38: e.preventDefault(); this.move('prev'); break; // down case 40: e.preventDefault(); this.move('next'); break; } }; /** * Focus on the next menu item in `direction`. * * @param {String} direction "prev" or "next" * @api public */ Menu.prototype.move = function(direction){ var prev = this.el.find('.selected').eq(0); var next = prev.length ? prev[direction]() : this.el.find('li:first-child'); if (next.length) { prev.removeClass('selected'); next.addClass('selected'); next.find('a').focus(); } }; /** * Add menu item with the given `text` and optional callback `fn`. * * When the item is clicked `fn()` will be invoked * and the `Menu` is immediately closed. When clicked * an event of the name `text` is emitted regardless of * the callback function being present. * * @param {String} text * @param {Function} fn * @return {Menu} * @api public */ Menu.prototype.add = function(text, fn){ var self = this , el = $('