/**
* Generic menu drilldown plugin for standard Drupal menu tree markup.
* The plugin should be inited against a DOM element that *contains*
* a Drupal ul.menu tree. Example:
*
* $('div.block-menu').drilldown('init', params);
*
* You must provide the following parameters as settings:
*
* var params = {
* activePath : A drupal menu path that is currently active including the basePath e.g. "/mysite/node"
* trail : A jquery selector to the DOM element that should act as the trail container, e.g. "div.my-menu-breadcrumb-trail"
* rootTitle : The title to use for the root menu item if the menu does not already possess one. Optional.
* }
*
*/
(function($) {
$.fn.drilldown = function(method, settings) {
var menu = this;
var activePath;
var rootTitle = settings.rootTitle || 'Home';
switch (method) {
case 'goTo':
// If the passed link refers to the current page, don't follow through
// the link.
if (this.activePath && this.activePath === $(settings.activeLink).attr('href')) {
return false;
}
return true;
case 'setActive':
var breadcrumb = [];
var activeMenu;
$(settings.activeLink).each(function() {
// Traverse backwards through menu parents and build breadcrumb array.
$(this).parents('ul.menu').each(function() {
if ($(this).parents('ul.menu').size() > 0) {
$(this).siblings('a').each(function() {
breadcrumb.unshift($(this));
});
}
// If this is a root menu with no root link to accompany it,
// generate one such that the breadcrumb may reference it.
else if ($(this).children('li').size() > 1) {
var root;
if ($(this).siblings('a.drilldown-root').size() > 0) {
root = $(this).siblings('a.drilldown-root');
}
else {
root = $(''+rootTitle+'');
$(this).before(root);
}
breadcrumb.unshift(root);
}
});
// If we have a child menu (actually a sibling in the DOM), use it
// as the active menu. Otherwise treat our direct parent as the
// active menu.
if ($(this).next().is('ul.menu')) {
activeMenu = $(this).next();
breadcrumb.push($(this));
}
else {
activeMenu = $(this).parents('ul.menu').eq(0);
}
if (activeMenu) {
$('.drilldown-active-trail', menu).removeClass('drilldown-active-trail');
$('ul.menu', menu).removeClass('drilldown-active-menu').removeClass('clearfix');
$(activeMenu)
.addClass('drilldown-active-menu').addClass('clearfix')
.parents('li').addClass('drilldown-active-trail').show();
}
});
// Render the breadcrumb to the target DOM object
if (breadcrumb.length > 0) {
var trail = $(settings.trail);
trail.empty();
for (var key in breadcrumb) {
if (breadcrumb[key]) {
// We don't use the $().clone() method here because of an
// IE & jQuery 1.2 bug.
var clone = $('')
.attr('href', $(breadcrumb[key]).attr('href'))
.attr('class', $(breadcrumb[key]).attr('class'))
.html($(breadcrumb[key]).html())
.addClass('depth-'+key)
.appendTo(trail);
// We add a reference to the original link and a click handler
// that traces back to that instance to set as the active link.
$('a.depth-'+key, trail)
.data('original', $(breadcrumb[key]))
.click(function() {
settings.activeLink = $(this).data('original');
// If the clicked link does not reference the current
// active menu, set it to be active.
if (settings.activeLink.siblings('ul.drilldown-active-menu').size() === 0) {
menu.drilldown('setActive', settings);
return false;
}
// Otherwise, pass-through and allow the link to be clicked.
return menu.drilldown('goTo', settings);
});
}
}
}
// Event in case others need to update themselves when a new active
// link is set.
$(menu).trigger('refresh.drilldown');
break;
case 'init':
if ($('ul.menu ul.menu', menu).size() > 0) {
$(menu).addClass('drilldown');
$(settings.trail).addClass('drilldown-trail');
// Set initial active menu state.
var activeLink;
$('ul.menu a', menu).removeClass('active');
if (settings.activePath && $('ul.menu a[href="'+settings.activePath+'"]', menu).size() > 0) {
this.activePath = settings.activePath;
activeLink = $('ul.menu a[href="'+settings.activePath+'"]', menu).addClass('active');
}
if (!activeLink) {
activeLink = $('ul.menu a.active', menu).size() ? $('ul.menu a.active', menu) : $('ul.menu > li > a', menu);
}
if (activeLink) {
menu.drilldown('setActive', {
activeLink: $(activeLink[0]),
trail: settings.trail,
rootTitle: rootTitle
});
}
// Attach click handlers to menu items
$('ul.menu li:has(ul.menu)', this).click(function() {
if ($(this).parent().is('.drilldown-active-menu')) {
if (menu.data('disableMenu')) {
return true;
}
else {
var url = $(this).children('a').attr('href');
var activeLink = $('ul.menu a[href="'+url+'"]', menu);
menu.drilldown('setActive', {
activeLink: activeLink,
trail: settings.trail,
rootTitle: rootTitle
});
return false;
}
}
});
$('ul.menu li:has(ul.menu) a', menu).click(function() {
menu.data('disableMenu', true);
});
}
break;
}
return this;
};
})(jQuery);