/** Menu button arrow which you can apply to DOM Element to have a menu drop down below it. @namespace ludo.menu @class ludo.menu.Button @param {object} config @param {string|HTMLElement} renderTo Render menu button to this DOM node @param {boolean} alwaysVisible Button always visible. When false, it will be visible when mouse enters @param {string} region Position button in this region. Valid values : 'nw','ne','sw' and 'se' @param {object} menu View configuration for menu, example { layout:{}, children:[ { "item 1", "item 2" ]} @ */ ludo.menu.Button = new Class({ Extends: ludo.Core, width: 15, // TODO refactor this class renderTo: undefined, alwaysVisible: false, region: 'ne', el: undefined, menu: undefined, menuCreated: false, autoPosition: true, toggleOnClick: false, __construct: function (config) { this.parent(config); this.setConfigParams(config, ['alwaysVisible', 'region', 'renderTo', 'menu', 'autoPosition', 'toggleOnClick']); }, ludoEvents: function () { this.parent(); this.ludoDOM(); this.createButtonEvents(); }, ludoDOM: function () { var el = this.el = $('<div>'); el.id = 'ludo-menu-button-' + String.uniqueID(); el.addClass('ludo-menu-button'); $(this.renderTo).append(el); el.css({ position: 'absolute', height: '100%' }); this.createButtonEl(); this.positionButton(); }, createButtonEvents: function () { this.buttonEl.on('click', this.toggle.bind(this)); ludo.EffectObject.addEvent('start', this.hideMenu.bind(this)); this.buttonEl.mouseenter(this.enterButton.bind(this)); this.buttonEl.mouseleave(this.leaveButton.bind(this)); if (!this.alwaysVisible) { var el = $(this.renderTo); el.on('mouseenter', this.show.bind(this)); el.on('mouseleave', this.hide.bind(this)); this.hide(); } else { this.show(); } }, enterButton: function () { this.el.addClass('ludo-menu-button-over'); }, leaveButton: function () { this.el.removeClass('ludo-menu-button-over'); }, toggle: function (e) { e.stopPropagation(); if (this.toggleOnClick && this.menuCreated) { this.menu[this.menu.isHidden() ? 'show' : 'hide'](); } else { this.showMenu(); } }, createButtonEl: function () { var el = this.buttonEl = $('<div>'); el.addClass('ludo-menu-button-arrow'); this.getEl().append(el); }, positionButton: function () { var e = this.getEl(); var r = this.region; if (r == 'ne' || r == 'se')e.css('right', 0); if (r == 'nw' || r == 'sw')e.css('left', 0); if (r == 'se' || r == 'sw')e.css('bottom', 0); if (r == 'ne' || r == 'nw')e.css('top', 0); }, getEl: function () { return this.el; }, showMenu: function () { if (!this.menuCreated) { this.createMenuView(); } if (this.menu._button && this.menu._button !== this.id) { var el = ludo.get(this.menu._button); if (el)el.hideButton(); } this.menu._button = this.id; this.menu.show(); this.positionMenu(); this.fireEvent('show', this); }, /* This method should be called from function added as event handler to "beforeShow" @function cancelShow @example button.addEvent('beforeShow', function(button){ if(!this.isOkToShowButton()){ button.cancel(); } }); */ cancelShow: function () { this.okToShowButton = false; }, hideMenu: function () { if (this.menu.hidden)return; if (this.menu.hide !== undefined) { if (this.menu.getLayout().hideAllMenus)this.menu.getLayout().hideAllMenus(); this.menu.hide(); } this.hide(); }, createMenuView: function () { if (this.menu.id) { var menu = ludo.get(this.menu.id); if (menu)this.menu = menu; } this.menuCreated = true; if (this.menu.getEl === undefined) { this.menu.renderTo = document.body; this.menu.type = this.menu.type || 'View'; this.menu.hidden = true; this.menu = this.createDependency('menuForButton', this.menu); this.menu._button = this.getEl().id; $(document.body).on('mouseup', this.autoHideMenu.bind(this)); } else { $(document.body).append(this.menu.getEl()); } this.menu.addEvent('show', this.showIf.bind(this)); this.menu.addEvent('hide', this.hideButton.bind(this)); this.menu.getEl().css('position', 'absolute'); this.menu.getEl().addClass('ludo-menu-button-menu'); }, positionMenu: function () { if (this.autoPosition) { var pos = this.el.offset(); this.menu.resize({ left: pos.left, top: pos.top + this.el.height() }); } }, showIf: function () { if (this.menu._button === this.id) { this.show(); } }, okToShowButton: false, show: function () { this.okToShowButton = true; /* * Event fired before button is shown. You can use this event and call * the cancel method if there are situations where you don't always want to show the button * @event beforeShow * @param {menu.Button} this */ this.fireEvent('beforeShow', this); if (this.okToShowButton) { this.buttonEl.css('display', ''); this.el.addClass('ludo-menu-button-active'); } }, hide: function () { if (this.menu === undefined || this.menu.isHidden === undefined || this.menu.isHidden()) { this.hideButton(); } else if (this.menu._button !== this.id) { this.hideButton(); } }, hideButton: function () { if (this.alwaysVisible)return; this.buttonEl.css('display', 'none'); this.el.removeClass('ludo-menu-button-active'); }, getMenuView: function () { return this.menu; }, autoHideMenu: function (e) { if (!this.menu || this.menu.hidden)return; if (!ludo.dom.isInFamilies(e.target, [this.el.id, this.menu.getEl().id])) { this.hideMenu(); this.hideButton(); } } });