(function(global, undefined) { var kafe = global.kafe, $ = kafe.dependencies.jQuery; kafe.bonify({name:'plugin.menu', version:'1.1.0', obj:(function(){
/**
* ### Version 1.1.0
* Attaches javascript behaviors to an HTML menu structure to create a *dropdown* style navigation.
*
* To preserve flexibility, the plugin only controls events, speeds, delays and callbacks. It will only manage a single custom class (`kafemenu-open`) on the handle elements upon opening or closing, leaving the positioning, visibility and other asthetic responsabilities to its css.
*
* @module kafe.plugin
* @class kafe.plugin.menu
*/
var menu = {};
/**
* Attach behaviors to the menu structure.
*
* @method init
* @param {Object} options Initial configurations.
* @param {String|jQueryObject|DOMElement} options.selector Root element of the menu structure.
* @param {String} [options.handle='li'] Children element of the container that will serve as a handle to open and close the submenu.
* @param {String} [options.submenus='ul'] Children element of the handle that will serve as a submenu, opening and closing when the handle is used.
* @param {String} [options.animation='slide'] Animation used when opening and closing the submenus.
* @param {Number} [options.openSpeed=200] Duration (in milliseconds) of the opening animation.
* @param {Number} [options.openDelay=500] Delay (in milliseconds) used when entering the handle before starting the opening animation.
* @param {Number} [options.closeSpeed=150] Duration (in milliseconds) of the closing animation.
* @param {Number} [options.closeDelay=400] Delay (in milliseconds) used when exiting the handle before starting the closing animation.
* @param {Function} [options.enterCallback] Upon entering a handle, will be triggered after the delay but before the animation. The current submenu is passed as a first argument.
* @param {Function} [options.leaveCallback] Upon exiting a handle, will be triggered after the delay but before the animation. The current submenu is passed as a first argument.
*
* @example
* // Sample menu structure
* <nav id="main-menu">
* <ul>
* <li><a href="#">Menu 1</a>
* <ul>
* <li><a href="#">Submenu 1.1</a></li>
* <li><a href="#">Submenu 1.2</a></li>
* <li><a href="#">Submenu 1.3</a></li>
* </ul>
* </li>
* <li><a href="#">Menu 2</a>
* <ul>
* <li><a href="#">Submenu 2.1</a></li>
* <li><a href="#">Submenu 2.2</a></li>
* <li><a href="#">Submenu 2.3</a></li>
* </ul>
* </li>
* </ul>
* </nav>
*
* @example
* // Attach behaviors using...
* kafe.plugin.menu.init({ selector: '#main-menu > ul' });
*
* @example
* // Or use the jQuery alternative...
* $('#main-menu > ul').kafeMenu('init', {});
*/
menu.init = function() {
var
options = (arguments) ? arguments[0] : {},
c = {
$menu: $(options.selector),
handle: (options.handle) ? options.handle : 'li',
handleBtn: (options.handleBtn) ? options.handleBtn : 'a',
submenus: (options.submenus) ? options.submenus : 'ul',
animation: (options.animation) ? options.animation : '',
openSpeed: !isNaN(Number(options.openSpeed)) ? Number(options.openSpeed) : 200,
openDelay: !isNaN(Number(options.openDelay)) ? Number(options.openDelay) : 500,
closeSpeed: !isNaN(Number(options.closeSpeed)) ? Number(options.closeSpeed) : 150,
closeDelay: !isNaN(Number(options.closeDelay)) ? Number(options.closeDelay) : 400,
enterCallback: (typeof(options.enterCallback) == 'function') ? options.enterCallback : undefined,
leaveCallback: (typeof(options.leaveCallback) == 'function') ? options.leaveCallback : undefined,
clickOnly: !!options.clickOnly
}
;
if (!c.$menu.length) {
return false;
}
var $handles = c.$menu.children(c.handle);
$handles
.bind('kafemenu:open', function(){ _openMenu(this, 0); })
.bind('kafemenu:close', function(){ _closeMenu(this, 0); })
;
if (!c.clickOnly) {
$handles
.bind('mouseenter', function(e){ _openMenu(this, c.openDelay); })
.bind('mouseleave', function(e){ _closeMenu(this, c.closeDelay); })
;
}
else
{
$handles.each(function(){
var $handle = $(this);
if($handle.children(c.submenus).length > 0) {
$handle.children(c.handleBtn).on('click', function(e){
e.preventDefault();
e.stopPropagation();
var $handle = $(this).parent();
if($handle.hasClass('kafemenu-open')){
$handle.trigger('kafemenu:close');
document.location = $(this).attr('href');
}else{
$handles.filter('.kafemenu-open').trigger('kafemenu:close');
$handle.trigger('kafemenu:open');
}
});
}
});
$('html').on('click', function() {
c.$menu.children(c.handle).filter('.kafemenu-open').trigger('kafemenu:close');
});
}
_closeMenu = function(_handle, _delay){
var
$parent = $(_handle),
$sub = $parent.children(c.submenus),
_clearclass = function() {
$parent.removeClass('kafemenu-open');
}
;
if ($sub.data('kafemenu-timer') !== undefined) {
clearTimeout($sub.data('kafemenu-timer'));
}
if ($sub.length > 0) {
$sub.data('kafemenu-timer', setTimeout(function() {
var returnCallback = true;
if (!!c.leaveCallback) {
returnCallback = c.leaveCallback($sub);
}
if (returnCallback) {
switch (c.animation) {
case 'fade' :
$sub.fadeOut(c.closeSpeed, _clearclass);
break;
case 'slide' :
$sub.slideUp(c.closeSpeed, _clearclass);
break;
default :
$sub.hide(c.closeSpeed, _clearclass);
break;
}
}
}, _delay));
}
};
_openMenu = function(_handle, _delay) {
var
$parent = $(_handle),
$sub = $parent.children(c.submenus)
;
if ($sub.data('kafemenu-timer') !== undefined) {
clearTimeout($sub.data('kafemenu-timer'));
}
if ($sub.length > 0) {
$sub.data('kafemenu-timer', setTimeout(function() {
$parent.addClass('kafemenu-open');
var returnCallback = true;
if (!!c.enterCallback) {
returnCallback = c.enterCallback($sub);
}
if (returnCallback) {
switch (c.animation) {
case 'fade' :
$sub.fadeIn(c.openSpeed);
break;
case 'slide' :
$sub.slideDown(c.openSpeed);
break;
default :
$sub.show(c.openSpeed);
break;
}
}
}, _delay));
}
};
};
// Add as jQuery plugin
kafe.fn.plugIntojQuery('Menu', {
init: function(obj, parameters) {
menu.init($.extend({}, parameters[0], {selector:obj}));
}
});
return menu;
})()}); })(typeof window !== 'undefined' ? window : this);